@@ -1,11 +1,46 @@ | |||
namespace UHFHelper | |||
using System.Runtime.InteropServices; | |||
using System.Text; | |||
namespace UHFHelper | |||
{ | |||
public static class UHFHelper | |||
{ | |||
/// <summary> | |||
/// 解析数据 | |||
/// </summary> | |||
/// <param name="data"></param> | |||
/// <returns></returns> | |||
public static string ByteArrayToHexString(this byte[] data) | |||
{ | |||
StringBuilder sb = new(data.Length * 3); | |||
foreach (byte b in data) | |||
sb.Append(Convert.ToString(b, 16).PadLeft(2, '0')); | |||
return sb.ToString().ToUpper(); | |||
} | |||
} | |||
public class Resultoutput | |||
{ | |||
public bool Res { get; set; } | |||
public string? ResMes { get; set; } | |||
} | |||
public class DKoutput | |||
{ | |||
/// <summary> | |||
/// 刷卡机地址 | |||
/// </summary> | |||
public string? Address { get; set; } | |||
/// <summary> | |||
/// 刷卡器数据 | |||
/// </summary> | |||
public string? ResData { get; set; } | |||
} | |||
/// <summary> | |||
/// 蜂鸣开关 | |||
/// </summary> | |||
@@ -15,4 +50,38 @@ | |||
open=0, | |||
close=1, | |||
} | |||
/// <summary> | |||
/// CRC算法 | |||
/// </summary> | |||
public class CRC16 | |||
{ | |||
/// <summary> | |||
/// CRC算法 | |||
/// </summary> | |||
/// <param name="pucY"></param> | |||
/// <returns></returns> | |||
public static byte[] ToCRC16(byte[] pucY) | |||
{ | |||
ushort uiCrcValue = 0xFFFF; | |||
for ( int ucI = 0; ucI < pucY.Length; ucI++) | |||
{ | |||
uiCrcValue = Convert.ToUInt16(uiCrcValue ^ pucY[ucI]); | |||
for ( int ucJ = 0; ucJ < 8; ucJ++) | |||
{ | |||
if ((uiCrcValue & 0x0001)==1) | |||
{ | |||
uiCrcValue =Convert.ToUInt16((uiCrcValue >> 1) ^ 0x8408); | |||
} | |||
else | |||
{ | |||
uiCrcValue = Convert.ToUInt16((uiCrcValue >> 1)); | |||
} | |||
} | |||
} | |||
byte[] tem = new byte[2]; | |||
tem[0] = (byte)uiCrcValue; | |||
tem[1] = (byte)(uiCrcValue >> 8); | |||
return tem; | |||
} | |||
} | |||
} |
@@ -7,6 +7,10 @@ | |||
<Platforms>AnyCPU;x86</Platforms> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="System.IO.Ports" Version="6.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Reference Include="UHFReader18CSharp"> | |||
<HintPath>..\lib\UHFReader18CSharp.dll</HintPath> | |||
@@ -0,0 +1,197 @@ | |||
using System.Diagnostics; | |||
using System.IO.Ports; | |||
namespace UHFHelper | |||
{ | |||
public class UHF_RS485_Helper | |||
{ | |||
private SerialPort? _serialPort; | |||
private SerialParam? _serialParam; | |||
/// <summary> | |||
/// 单例模式 | |||
/// </summary> | |||
private static UHF_RS485_Helper? Instance { get; set; } | |||
public static UHF_RS485_Helper GetInstance() | |||
{ | |||
if (Instance == null) | |||
{ | |||
Instance = new UHF_RS485_Helper(); | |||
} | |||
return Instance; | |||
} | |||
/// <summary> | |||
/// 打开串口 | |||
/// </summary> | |||
/// <param name="param"></param> | |||
public void Open(SerialParam param) | |||
{ | |||
_serialParam = param; | |||
_serialPort = new SerialPort(_serialParam.PortName, _serialParam.BaudRate, _serialParam.PortParity, _serialParam.DataBits); | |||
_serialPort.Open(); | |||
} | |||
/// <summary> | |||
/// 获取串口状态 | |||
/// </summary> | |||
/// <returns></returns> | |||
public bool GetSerialPortState() | |||
{ | |||
if (_serialPort!=null) | |||
{ | |||
return _serialPort.IsOpen; | |||
} | |||
else | |||
{ | |||
return false; | |||
} | |||
} | |||
/// <summary> | |||
/// 关闭串口 | |||
/// </summary> | |||
public void Close() | |||
{ | |||
if (_serialPort == null) return; | |||
_serialPort.Close(); | |||
_serialPort.Dispose(); | |||
_serialPort = null; | |||
} | |||
/// <summary> | |||
/// 读卡方法,返回读卡器数据 | |||
/// </summary> | |||
/// <param name="adr">设备读取地址</param> | |||
/// <returns></returns> | |||
public async Task<DKoutput?> ReadCard(int adr) | |||
{ | |||
DKoutput dKoutput = new(); | |||
var readByte = ReadByte(adr); | |||
var result = await SendMessageAsync(readByte, TimeSpan.FromSeconds(1), 18); | |||
if (result == null) | |||
{ | |||
return null; | |||
} | |||
else | |||
{ | |||
//获取校验码 | |||
var crc = result.Skip(16).Take(2).ToArray(); | |||
//获取卡号 | |||
var cardNo = result.Skip(6).Take(16).ToArray(); | |||
//获取读卡器数据 | |||
var readData = result.Skip(0).Take(16).ToArray(); | |||
//获取读卡器地址 | |||
var address = result.Skip(1).Take(1).ToArray(); | |||
//判断数据是否合法 | |||
var temcrc = CRC16.ToCRC16(readData); | |||
if (crc.ByteArrayToHexString() == temcrc.ByteArrayToHexString()) | |||
{ | |||
dKoutput.Address = address.ByteArrayToHexString(); | |||
dKoutput.ResData = cardNo.ByteArrayToHexString(); | |||
return dKoutput; | |||
} | |||
else | |||
{ | |||
return null; | |||
} | |||
} | |||
} | |||
/// <summary> | |||
/// 生成读取命令 | |||
/// </summary> | |||
/// <returns></returns> | |||
private byte[] ReadByte(int adr) | |||
{ | |||
//根据读取地址生成读取命令 | |||
byte[] data1 = new byte[3] { 04, (byte)adr, 01 }; | |||
//生成crc校验码 | |||
byte[] data2 = CRC16.ToCRC16(data1); | |||
byte[] data3 = new byte[data1.Length + data2.Length]; | |||
data1.CopyTo(data3, 0); | |||
data2.CopyTo(data3, data1.Length); | |||
return data3; | |||
} | |||
/// <summary> | |||
/// 获取本机可用串口 | |||
/// </summary> | |||
/// <returns></returns> | |||
public string[] GetLocalSerialPortNames() | |||
{ | |||
return SerialPort.GetPortNames(); | |||
} | |||
/// <summary> | |||
/// 发送数据 | |||
/// </summary> | |||
/// <param name="msg">数据</param> | |||
/// <param name="timeout">超时时间</param> | |||
/// <param name="count">串口回复字节数</param> | |||
/// <returns></returns> | |||
public byte[]? SendMessage(byte[] msg, TimeSpan timeout, int count) | |||
{ | |||
if (_serialPort == null) return null; | |||
var stopwatch = Stopwatch.StartNew(); | |||
var originalWriteTimeout = _serialPort.WriteTimeout; | |||
var originalReadTimeout = _serialPort.ReadTimeout; | |||
try | |||
{ | |||
_serialPort.WriteTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0); | |||
_serialPort.Write(msg, 0, msg.Length); | |||
var tmpCount = count; | |||
byte[] buffer = new byte[tmpCount]; | |||
int offset = 0; | |||
while (tmpCount > 0) | |||
{ | |||
_serialPort.ReadTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0); | |||
var readCount = _serialPort.Read(buffer, offset, tmpCount); | |||
if (readCount==6) | |||
{ | |||
tmpCount = 0; | |||
continue; | |||
} | |||
offset += readCount; | |||
tmpCount -= readCount; | |||
} | |||
_serialPort.DiscardInBuffer(); | |||
return buffer; | |||
}catch(Exception ex) | |||
{ | |||
return null; | |||
} | |||
finally | |||
{ | |||
_serialPort.ReadTimeout = originalReadTimeout; | |||
_serialPort.WriteTimeout = originalWriteTimeout; | |||
} | |||
} | |||
/// <summary> | |||
/// 发送数据 | |||
/// </summary> | |||
/// <param name="msg">数据</param> | |||
/// <param name="timeout">超时时间</param> | |||
/// <param name="count">串口回复字节数</param> | |||
/// <returns></returns> | |||
public async Task<byte[]> SendMessageAsync(byte[] msg, TimeSpan timeout, int count) | |||
{ | |||
var sendTask = Task.Run(() => SendMessage(msg, timeout, count)); | |||
try | |||
{ | |||
await Task.WhenAny(sendTask, Task.Delay(timeout)); | |||
} | |||
catch (TaskCanceledException) | |||
{ | |||
throw new TimeoutException(); | |||
} | |||
return await sendTask; | |||
} | |||
} | |||
public class SerialParam | |||
{ | |||
public string PortName { get; set; } = "COM3"; | |||
public int BaudRate { get; set; } = 57600; | |||
public int DataBits { get; set; } = 8; | |||
public Parity PortParity { get; set; } = Parity.None; | |||
public StopBits PortStopBits { get; set; } = StopBits.One; | |||
} | |||
} |
@@ -34,6 +34,11 @@ | |||
this.button3 = new System.Windows.Forms.Button(); | |||
this.button4 = new System.Windows.Forms.Button(); | |||
this.textBox1 = new System.Windows.Forms.TextBox(); | |||
this.button5 = new System.Windows.Forms.Button(); | |||
this.comboBox1 = new System.Windows.Forms.ComboBox(); | |||
this.button6 = new System.Windows.Forms.Button(); | |||
this.label2 = new System.Windows.Forms.Label(); | |||
this.textBox4 = new System.Windows.Forms.TextBox(); | |||
this.SuspendLayout(); | |||
// | |||
// button1 | |||
@@ -92,11 +97,60 @@ | |||
this.textBox1.Size = new System.Drawing.Size(100, 23); | |||
this.textBox1.TabIndex = 4; | |||
// | |||
// button5 | |||
// | |||
this.button5.Location = new System.Drawing.Point(55, 323); | |||
this.button5.Name = "button5"; | |||
this.button5.Size = new System.Drawing.Size(108, 23); | |||
this.button5.TabIndex = 5; | |||
this.button5.Text = "rs485打开串口"; | |||
this.button5.UseVisualStyleBackColor = true; | |||
this.button5.Click += new System.EventHandler(this.button5_Click); | |||
// | |||
// comboBox1 | |||
// | |||
this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | |||
this.comboBox1.FormattingEnabled = true; | |||
this.comboBox1.Location = new System.Drawing.Point(178, 323); | |||
this.comboBox1.Name = "comboBox1"; | |||
this.comboBox1.Size = new System.Drawing.Size(121, 25); | |||
this.comboBox1.TabIndex = 6; | |||
// | |||
// button6 | |||
// | |||
this.button6.Location = new System.Drawing.Point(322, 323); | |||
this.button6.Name = "button6"; | |||
this.button6.Size = new System.Drawing.Size(75, 23); | |||
this.button6.TabIndex = 7; | |||
this.button6.Text = "读取"; | |||
this.button6.UseVisualStyleBackColor = true; | |||
this.button6.Click += new System.EventHandler(this.button6_Click); | |||
// | |||
// label2 | |||
// | |||
this.label2.Location = new System.Drawing.Point(451, 324); | |||
this.label2.Name = "label2"; | |||
this.label2.Size = new System.Drawing.Size(266, 23); | |||
this.label2.TabIndex = 8; | |||
this.label2.Text = "label2"; | |||
// | |||
// textBox4 | |||
// | |||
this.textBox4.Location = new System.Drawing.Point(451, 369); | |||
this.textBox4.Name = "textBox4"; | |||
this.textBox4.Size = new System.Drawing.Size(100, 23); | |||
this.textBox4.TabIndex = 9; | |||
// | |||
// Form1 | |||
// | |||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); | |||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |||
this.ClientSize = new System.Drawing.Size(800, 450); | |||
this.Controls.Add(this.textBox4); | |||
this.Controls.Add(this.label2); | |||
this.Controls.Add(this.button6); | |||
this.Controls.Add(this.comboBox1); | |||
this.Controls.Add(this.button5); | |||
this.Controls.Add(this.textBox1); | |||
this.Controls.Add(this.button4); | |||
this.Controls.Add(this.label1); | |||
@@ -105,6 +159,7 @@ | |||
this.Controls.Add(this.button1); | |||
this.Name = "Form1"; | |||
this.Text = "Form1"; | |||
this.Load += new System.EventHandler(this.Form1_Load); | |||
this.ResumeLayout(false); | |||
this.PerformLayout(); | |||
@@ -118,5 +173,10 @@ | |||
private Button button3; | |||
private Button button4; | |||
private TextBox textBox1; | |||
private Button button5; | |||
private ComboBox comboBox1; | |||
private Button button6; | |||
private Label label2; | |||
private TextBox textBox4; | |||
} | |||
} |
@@ -1,3 +1,4 @@ | |||
using System.Text; | |||
using UHFHelper; | |||
namespace test | |||
@@ -44,8 +45,44 @@ namespace test | |||
private void button4_Click(object sender, EventArgs e) | |||
{ | |||
var res = UHFCardHelper.GetInstance().WriteCard(textBox1.Text); | |||
var res = UHFCardHelper.GetInstance().WriteCard(textBox1.Text); | |||
MessageBox.Show(res.ResMes); | |||
} | |||
private void Form1_Load(object sender, EventArgs e) | |||
{ | |||
//// 查看本机可用串口 | |||
//foreach (var val in UHF_RS485_Helper.GetLocalSerialPortNames()) | |||
//{ | |||
// this.comboBox1.Items.Add(val); | |||
//} | |||
} | |||
UHF_RS485_Helper uHF_RS485_Helper; | |||
private void button5_Click(object sender, EventArgs e) | |||
{ | |||
// 初始化 | |||
UHF_RS485_Helper.GetInstance().Open(new SerialParam | |||
{ | |||
PortName = "COM5", | |||
BaudRate = 57600, | |||
DataBits = 8 | |||
}); | |||
if (!UHF_RS485_Helper.GetInstance().GetSerialPortState()) | |||
{ | |||
MessageBox.Show("打开失败"); | |||
} | |||
} | |||
private async void button6_Click(object sender, EventArgs e) | |||
{ | |||
if (UHF_RS485_Helper.GetInstance().GetSerialPortState()) | |||
{ | |||
var oldss = await UHF_RS485_Helper.GetInstance().ReadCard(01); | |||
var sss = oldss?.ResData; | |||
textBox4.Text = sss; | |||
label2.Text = sss; | |||
} | |||
} | |||
} | |||
} |