@@ -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 class Resultoutput | ||||
{ | { | ||||
public bool Res { get; set; } | public bool Res { get; set; } | ||||
public string? ResMes { 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> | ||||
/// 蜂鸣开关 | /// 蜂鸣开关 | ||||
/// </summary> | /// </summary> | ||||
@@ -15,4 +50,38 @@ | |||||
open=0, | open=0, | ||||
close=1, | 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> | <Platforms>AnyCPU;x86</Platforms> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | |||||
<PackageReference Include="System.IO.Ports" Version="6.0.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | <ItemGroup> | ||||
<Reference Include="UHFReader18CSharp"> | <Reference Include="UHFReader18CSharp"> | ||||
<HintPath>..\lib\UHFReader18CSharp.dll</HintPath> | <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.button3 = new System.Windows.Forms.Button(); | ||||
this.button4 = new System.Windows.Forms.Button(); | this.button4 = new System.Windows.Forms.Button(); | ||||
this.textBox1 = new System.Windows.Forms.TextBox(); | 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(); | this.SuspendLayout(); | ||||
// | // | ||||
// button1 | // button1 | ||||
@@ -92,11 +97,60 @@ | |||||
this.textBox1.Size = new System.Drawing.Size(100, 23); | this.textBox1.Size = new System.Drawing.Size(100, 23); | ||||
this.textBox1.TabIndex = 4; | 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 | // Form1 | ||||
// | // | ||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); | ||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||
this.ClientSize = new System.Drawing.Size(800, 450); | 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.textBox1); | ||||
this.Controls.Add(this.button4); | this.Controls.Add(this.button4); | ||||
this.Controls.Add(this.label1); | this.Controls.Add(this.label1); | ||||
@@ -105,6 +159,7 @@ | |||||
this.Controls.Add(this.button1); | this.Controls.Add(this.button1); | ||||
this.Name = "Form1"; | this.Name = "Form1"; | ||||
this.Text = "Form1"; | this.Text = "Form1"; | ||||
this.Load += new System.EventHandler(this.Form1_Load); | |||||
this.ResumeLayout(false); | this.ResumeLayout(false); | ||||
this.PerformLayout(); | this.PerformLayout(); | ||||
@@ -118,5 +173,10 @@ | |||||
private Button button3; | private Button button3; | ||||
private Button button4; | private Button button4; | ||||
private TextBox textBox1; | 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; | using UHFHelper; | ||||
namespace test | namespace test | ||||
@@ -44,8 +45,44 @@ namespace test | |||||
private void button4_Click(object sender, EventArgs e) | 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); | 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; | |||||
} | |||||
} | |||||
} | } | ||||
} | } |