Last active
September 23, 2023 19:29
-
-
Save ChrisMoney/56449136882104f917af9c8b8f9d6a8c to your computer and use it in GitHub Desktop.
C# Send low level hexadecimal, binary command to K720 debit card dispenser listening on a serial port.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define USE_DEBUG | |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Linq; | |
using System.Text; | |
using System.IO.Ports; | |
using Microsoft.Win32.SafeHandles; | |
using System.Reflection; | |
using System.Runtime.InteropServices; | |
using SCL011Reader; | |
using System.Management; | |
using Microsoft.Win32; | |
using System.Security.Permissions; | |
namespace Dispenser | |
{ | |
static class Program | |
{ | |
static void Main(string[] args) | |
{ | |
} | |
} | |
public class K720Driver : IDispenser | |
{ | |
public static readonly K720Driver StaticK720Driver = new K720Driver(); | |
private string _dispenserPort; | |
private string _readerPort; | |
private SafeFileHandle _dispenserHandle; | |
private SafeFileHandle _readerHandle; | |
//private IntPtr DispenserHandle { get { return GetHandle(_dispenserPort, ref _dispenserHandle); } } | |
private IntPtr DispenserHandle { get { return _dispenserHandle.DangerousGetHandle(); } } | |
//private IntPtr ReaderHandle { get { return GetHandle(_readerPort, ref _readerHandle); } } | |
private IntPtr ReaderHandle { get { return _readerHandle.DangerousGetHandle(); } } | |
private const byte MacID = 1; | |
private readonly RFIDParser _rfidParser; | |
private readonly byte[] _recordInfo; | |
private readonly CardReader _reader; | |
public DState Flags { get; private set; } | |
private bool Has(DState flag) { return (Flags & flag) == flag; } | |
public bool IsOpen { get; private set; } | |
public bool Open(string dispenserPort, string readerPort) | |
{ | |
if (IsOpen) | |
{ | |
Close(); | |
IsOpen = false; | |
return IsOpen; | |
} | |
_dispenserPort = dispenserPort; | |
var h = D1000_CommOpen(dispenserPort); | |
_dispenserHandle = new SafeFileHandle(h, true); | |
_readerPort = readerPort; | |
h = RF610_CommOpen(readerPort); | |
_readerHandle = new SafeFileHandle(h, true); | |
// check dispenser and rfid connections | |
if (_dispenserHandle.IsInvalid || _readerHandle.IsInvalid) | |
{ | |
IsOpen = false; | |
} | |
else | |
{ | |
IsOpen = true; | |
} | |
return IsOpen; | |
} | |
public void Close() | |
{ | |
if(_dispenserHandle!=null) | |
_dispenserHandle.Close(); | |
if(_readerHandle !=null) | |
_readerHandle.Close(); | |
IsOpen = false; | |
} | |
private K720Driver() | |
{ | |
_recordInfo = new byte[256]; | |
_rfidParser = RFIDParser.Intercard_Default; | |
_reader = new CardReader(this); | |
} | |
//public DState CaptureCard() | |
//{ | |
// Wait(); | |
// D1000_SendCmd(DispenserHandle, MacID, "CP", 2); | |
// GetState(); | |
// if (!Has(DState.CannotExecute)) | |
// Wait(); | |
// return Flags; | |
//} | |
public DState DispenseCard() | |
{ | |
Wait(); | |
D1000_SendCmd(DispenserHandle, MacID, "FC0", 3); | |
GetState(); | |
if (!Has(DState.CannotExecute)) | |
{ | |
Wait(); | |
D1000_SendCmd(DispenserHandle, MacID, "FC7", 3); | |
} | |
return Flags; | |
} | |
public DState Reset() | |
{ | |
D1000_SendCmd(DispenserHandle, MacID, "RS", 2); | |
return GetState(); | |
} | |
public StringBuilder GetDispenserVersion() | |
{ | |
StringBuilder version = new StringBuilder(4096); | |
D1000_GetSysVersion(DispenserHandle, MacID, version); | |
return version; | |
} | |
public DState ReadyCard() | |
{ | |
Wait(); | |
if (Has(DState.Pos1_2)) //already in position | |
return Flags; | |
//If card is is behind the read position, move forward | |
if (!Has(DState.Pos1) && (Has(DState.Pos2) || Has(DState.Pos3))) | |
{ | |
D1000_SendCmd(DispenserHandle, MacID, "FC7", 3); //Move to read position | |
Wait(); | |
} | |
if (Has(DState.Pos1)) //Card is too far forward, need to dispense, I think... | |
{ | |
bool stackEmpty = Has(DState.StackEmpty); | |
D1000_SendCmd(DispenserHandle, MacID, "FC0", 3); //Dispense | |
Wait(); | |
if (!stackEmpty) | |
{ | |
//Move the next card into read position so we don't have to wait to read. | |
D1000_SendCmd(DispenserHandle, MacID, "FC7", 3); | |
Wait(); | |
} | |
} | |
return Flags; | |
} | |
public string ScanCard(out long corpID, out long acctNo) | |
{ | |
corpID = 0; | |
acctNo = 0; | |
string status = GetEnumDescription(RF610_ULDetectCard(ReaderHandle, _recordInfo)); | |
if (status != GetEnumDescription(ReaderCode.Ok)) | |
return status; | |
if (!_rfidParser.Parse(_reader)) | |
return GetEnumDescription(ReaderCode.ReadDataError); | |
corpID = _rfidParser.Corp ?? 0; | |
acctNo = _rfidParser.Acct ?? 0; | |
return GetEnumDescription(ReaderCode.Ok); | |
} | |
public ReaderCode DetectCard() | |
{ | |
return RF610_ULDetectCard(ReaderHandle, _recordInfo); | |
} | |
public void SendCommand(string cmd) | |
{ | |
D1000_SendCmd(DispenserHandle, MacID, cmd, cmd.Length); | |
} | |
public int Check() | |
{ | |
string cmd = Command.Check; | |
return D1000_SendCmd(DispenserHandle, MacID, cmd, cmd.Length); | |
} | |
public DState GetState() | |
{ | |
StringBuilder strState = new StringBuilder(3); | |
D1000_SensorQuery(DispenserHandle, MacID, strState); | |
Flags = (DState)Int32.Parse(strState.ToString(), System.Globalization.NumberStyles.HexNumber); | |
return Flags; | |
} | |
// Interface method | |
public void PositionCardToRead() | |
{ | |
SendCommand(Command.CaptureCard); | |
} | |
// Interface Method | |
public void Read() | |
{ | |
ReadyCard(); | |
} | |
// Interface method | |
public void Dispense() | |
{ | |
SendCommand(Command.DispenseSensor2); | |
} | |
public void DispenseReadCard() | |
{ | |
SendCommand(Command.DispenseReadCard); | |
} | |
public void DispenseHoldCard() | |
{ | |
SendCommand(Command.DispenseReadCard); | |
} | |
public void DispenseMouth() | |
{ | |
SendCommand(Command.DispenseMouth); | |
} | |
private void Wait() | |
{ | |
DState curState = Flags; | |
do | |
{ | |
DState newState = GetState(); | |
if (curState != newState) | |
Log("StateChange: {0:g} -> {1:g}", curState, newState); | |
curState = newState; | |
} | |
while ((Flags & (DState.CannotExecute | DState.PreparingCard | DState.DispensingCard | DState.CapturingCard)) != 0); | |
} | |
public void WaitDetectUL() | |
{ | |
while (RF610_ULDetectCard(ReaderHandle, _recordInfo) == ReaderCode.SearchCardFailed) { } | |
Log("Card Detected"); | |
} | |
private void Log(string format, params object[] arg) | |
{ | |
Console.WriteLine(format, arg); | |
System.Diagnostics.Debug.WriteLine(format, arg); | |
} | |
public static Dictionary<string, string> ActivePorts | |
{ | |
get | |
{ | |
string[] portNames = SerialPort.GetPortNames(); | |
Dictionary<string, string> ports = portNames.ToDictionary(i => i, i => i); | |
var searcher = new ManagementObjectSearcher( | |
new ManagementScope( | |
new ManagementPath(string.Format(@"\\{0}\root\CIMV2", Environment.MachineName)), | |
new ConnectionOptions | |
{ | |
Impersonation = ImpersonationLevel.Impersonate, | |
Authentication = AuthenticationLevel.Default, | |
EnablePrivileges = true | |
}), | |
new ObjectQuery(@"Select * from Win32_PnPEntity where ClassGuid = ""{4d36e978-e325-11ce-bfc1-08002be10318}"" and ConfigManagerErrorCode = 0")); | |
using (searcher) | |
{ | |
foreach (ManagementObject obj in searcher.Get()) | |
{ | |
if (obj["Caption"] != null && obj["DeviceID"] != null) | |
{ | |
string friendlyName = (string)obj["Caption"]; | |
string path = string.Format(@"SYSTEM\CurrentControlSet\Enum\{0}\Device Parameters", obj["DeviceID"]); | |
string port = null; | |
RegistryKey baseKey = null; | |
RegistryKey serialKey = null; | |
new RegistryPermission(RegistryPermissionAccess.Read, @"HKEY_LOCAL_MACHINE\" + path).Assert(); | |
try | |
{ | |
baseKey = Registry.LocalMachine; | |
serialKey = baseKey.OpenSubKey(path, false); | |
if (serialKey != null) | |
{ | |
port = (string)serialKey.GetValue("PortName"); | |
if (port != null && ports.ContainsKey(port)) | |
ports[port] = friendlyName; | |
} | |
} | |
finally | |
{ | |
if (baseKey != null) | |
baseKey.Close(); | |
if (serialKey != null) | |
serialKey.Close(); | |
RegistryPermission.RevertAssert(); | |
} | |
} | |
} | |
} | |
return ports; | |
} | |
} | |
public class CardReader : ICardReader | |
{ | |
private K720Driver _dispenser; | |
private byte[] _keyValue; | |
public CardReader(K720Driver dispenser) | |
{ | |
_dispenser = dispenser; | |
} | |
public string CardName { get { return "Mifare Ultra Light"; } } | |
public string ERROR { get { return ""; } } | |
public string Standard { get { return "ISO 14443 A, part 3"; } } | |
public int SW { get { return 0; } } | |
public byte[] UID | |
{ | |
get | |
{ | |
var uid = new byte[7]; | |
return RF610_ULGetCardID(_dispenser.ReaderHandle, uid, _dispenser._recordInfo) == ReaderCode.Ok ? uid : null; | |
} | |
} | |
public bool Authenticate(int address, int keyType, int keyNumber) | |
{ | |
if (_keyValue == null) | |
return false; | |
byte keyChar = (byte)(keyType==0x60? '0' : '1'); | |
ReaderCode status = RF610_S50LoadSecKey(_dispenser.ReaderHandle, (byte)address, keyChar, _keyValue, _dispenser._recordInfo); | |
return status == ReaderCode.Ok; | |
} | |
public string GetVersion() | |
{ | |
StringBuilder ver = new StringBuilder(4096); | |
RF610_GetSysVersion(_dispenser.ReaderHandle, ver); | |
return ver.ToString(); | |
} | |
public bool LoadKey(int keyStructure, int keyNumber, byte[] keyValue) | |
{ | |
_keyValue = keyValue; | |
return true; | |
} | |
public byte[] Read(int blockNumber, int length) | |
{ | |
byte[] block = new byte[16]; | |
RF610_ULReadBlock(_dispenser.ReaderHandle, (byte)blockNumber, block, _dispenser._recordInfo); | |
if (block.Length != length) | |
Array.Resize(ref block, length); | |
return block; | |
} | |
public bool Write(int blockNumber, byte[] data) | |
{ | |
return RF610_ULWriteBlock(_dispenser.ReaderHandle, (byte)blockNumber, data, _dispenser._recordInfo) == ReaderCode.Ok; | |
} | |
} | |
// Get enum full string description | |
public static string GetEnumDescription(Enum enumValue) | |
{ | |
string enumValueAsString = enumValue.ToString(); | |
var type = enumValue.GetType(); | |
FieldInfo fieldInfo = type.GetField(enumValueAsString); | |
object[] attributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); | |
if (attributes.Length > 0) | |
{ | |
var attribute = (DescriptionAttribute)attributes[0]; | |
return attribute.Description; | |
} | |
return enumValueAsString; | |
} | |
private const int VERSION_MAX_LEN = 32; | |
//[DllImport("RF610_DLL.dll")] | |
//private static extern void* RF610_CommOpen(byte* Port); | |
[DllImport("RF610_DLL.dll", CharSet = CharSet.Ansi)] | |
private static extern IntPtr RF610_CommOpen(string port); | |
//[DllImport("RF610_DLL.dll")] | |
//private static extern void* RF610_CommOpenWithBaud(byte* Port, uint _data); | |
//[DllImport("RF610_DLL.dll")] | |
//private static extern int RF610_CommClose(void* ComHandle); | |
[DllImport("RF610_DLL.dll", CharSet = CharSet.Ansi)] | |
private static extern ReaderCode RF610_GetSysVersion(IntPtr handle, [MarshalAs(UnmanagedType.LPStr, SizeConst = VERSION_MAX_LEN)] [Out] StringBuilder version); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_ULDetectCard(IntPtr handle, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_ULGetCardID(IntPtr handle, byte[] uid, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_ULReadBlock(IntPtr handle, byte SectorAddr, byte[] blockData, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_ULWriteBlock(IntPtr handle, byte SectorAddr, byte[] blockData, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_ULHalt(IntPtr handle, byte[] recordInfo); | |
//[DllImport("RF610_DLL.dll")] | |
//private static extern int RF610_SetCommBaud(IntPtr handle, uint _Baud, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll", CharSet = CharSet.Ansi)] | |
private static extern ReaderCode RF610_Reset(IntPtr handle, [MarshalAs(UnmanagedType.LPStr, SizeConst = VERSION_MAX_LEN)] out string version, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_S50LoadSecKey(IntPtr handle, byte sectorAddr, byte keyType, byte[] key, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_S50ReadBlock(IntPtr handle, byte sectorAddr, byte blockAddr, byte[] blockData, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_S50WriteBlock(IntPtr handle, byte sectorAddr, byte blockAddr, byte[] blockData, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_S50InitValue(IntPtr handle, byte sectorAddr, byte blockAddr, byte[] blockData, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_S50Increment(IntPtr handle, byte sectorAddr, byte blockAddr, ref int value, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_S50Decrement(IntPtr handle, byte sectorAddr, byte blockAddr, ref int value, byte[] recordInfo); | |
[DllImport("RF610_DLL.dll")] | |
private static extern ReaderCode RF610_S50Halt(IntPtr handle, byte[] recordInfo); | |
// K720 Unmanaged Methods | |
[DllImport("D1000_DLL.dll", CharSet = CharSet.Ansi)] | |
private static extern IntPtr D1000_CommOpen(string port); | |
//[DllImport("D1000_DLL.dll")] | |
//private static extern void* D1000_CommOpen(byte* Port); | |
//[DllImport("D1000_DLL.dll")] | |
//private static extern void* D1000_CommOpenWithBaud(byte* Port, uint _data); | |
//[DllImport("D1000_DLL.dll")] | |
//private static extern IntPtr D1000_CommClose(string port); | |
[DllImport("D1000_DLL.dll")] | |
private static extern int D1000_GetDllVersion(IntPtr handle, [MarshalAs(UnmanagedType.LPStr, SizeConst = VERSION_MAX_LEN)] out string version); | |
[DllImport("D1000_DLL.dll", CharSet = CharSet.Ansi)] | |
private static extern int D1000_GetSysVersion(IntPtr handle, byte MacAddr, [Out] StringBuilder version); | |
[DllImport("D1000_DLL.dll", CharSet = CharSet.Ansi)] | |
private static extern int D1000_SendCmd(IntPtr handle, byte MacAddr, byte[] cmd, int cmdLen); | |
[DllImport("D1000_DLL.dll", CharSet = CharSet.Ansi)] | |
private static extern int D1000_SendCmd(IntPtr handle, byte MacAddr, string cmd, int cmdLen); | |
[DllImport("D1000_DLL.dll")] | |
private static extern int D1000_Query(IntPtr handle, byte MacAddr, byte[] stateInfo); | |
[DllImport("D1000_DLL.dll", EntryPoint="D1000_SensorQuery", CharSet = CharSet.Ansi)] | |
private static extern int D1000_SensorQuery(IntPtr handle, byte MacAddr, [Out] StringBuilder stateInfo); | |
} | |
[Flags] | |
public enum DState | |
{ | |
[Description("Position 1")] | |
Pos1 = 0x0001, | |
[Description("Position 2")] | |
Pos2 = 0x0002, | |
[Description("Position 1 and 2")] | |
Pos1_2 = 0x0003, | |
[Description("Position 3")] | |
Pos3 = 0x0004, | |
[Description("Stack Empty")] | |
StackEmpty = 0x0008, | |
[Description("Stack Near Empty")] | |
StackNearEmpty = 0x0010, | |
[Description("Card Jammed")] | |
CardJammed = 0x0020, | |
[Description("Card Over Lapped")] | |
CardOverlapped = 0x0040, | |
[Description("No Card Captured")] | |
NoCapturedCard = 0x0080, | |
[Description("Card Capture Error")] | |
CaptureCardError = 0x0100, | |
[Description("Dispense Card Error")] | |
DispenseCardError = 0x0200, | |
[Description("Capturing Card")] | |
CapturingCard = 0x0400, | |
[Description("Dispensing Card")] | |
DispensingCard = 0x0800, | |
[Description("Preparing Card")] | |
PreparingCard = 0x1000, | |
[Description("Preparing Card Fail")] | |
PreparingCardFail = 0x2000, | |
[Description("Cannot Execute")] | |
CannotExecute = 0x4000, | |
[Description("Keep")] | |
Keep = 0x8000 | |
} | |
public enum ReaderCode | |
{ | |
[Description("Read successfully")] | |
Ok = 0, | |
[Description("Command parameter error")] | |
CommandParameterError = 0x01, | |
[Description("Command data error")] | |
CommandDataError = 0x02, | |
[Description("Cannot execute")] | |
CannotExecute = 0x03, | |
[Description("Execution failed")] | |
ExecuteFailed = 0x04, | |
[Description("Card search failed")] | |
SearchCardFailed = 0x41, | |
[Description("Failed to read serial number")] | |
ReadSerialNumberFailed = 0x42, | |
[Description("Verify PW Error")] | |
VerifyPWError = 0x43, | |
[Description("Card select error")] | |
SelectCardError = 0x44, | |
[Description("Data read error")] | |
ReadDataError = 0x45, | |
[Description("Data write error")] | |
WriteDataError = 0x46, | |
[Description("Increment failed")] | |
IncrementFailed = 0x47, | |
[Description("Devalue failed")] | |
DevalueFailed = 0x48, | |
[Description("Bad command read")] | |
BadCommRead = -103, | |
[Description("Bad command write")] | |
BadCommWrite = -104, | |
[Description("ACK timeout")] | |
AckTimeout = -105, | |
[Description("EOT timeout")] | |
EotTimeout = -106, | |
[Description("Packet timeout")] | |
PacketTimeout = -107, | |
[Description("Wrong packet head")] | |
WrongPacketHead = -108, | |
[Description("Wrong packet length")] | |
WrongPacketLen = -109, | |
[Description("Wrong BCC")] | |
WrongBCC = -110, | |
[Description("Bad parameter")] | |
BadParameter = -201, | |
[Description("Bad com port close")] | |
BadCommClose = -202 | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
struct SensorQueryState | |
{ | |
[MarshalAs(UnmanagedType.LPStr, SizeConst = 4)] | |
private readonly string _byteChars; | |
public static implicit operator DState(SensorQueryState state) | |
{ | |
return (DState)Int32.Parse(state._byteChars, System.Globalization.NumberStyles.HexNumber); | |
} | |
public override string ToString() | |
{ | |
return ((DState)this).ToString("g"); | |
} | |
} | |
public struct Command | |
{ | |
public static string CaptureCard = "CP"; | |
public static string Check = "AP"; | |
public static string IssueCard = "DC"; | |
public static string CollectCard = "CP"; | |
public static string Reposition = "RS"; | |
public static string DispenseSensor2 = "FC6"; | |
public static string DispenseReadCard = "FC7"; | |
public static string DispenseHoldCard = "FC4"; | |
public static string DispenseMouth = "FC0"; | |
public static string AllowBuzz = "BE"; | |
public static string StopBuzz = "BD"; | |
public static string BaudRate1200 = "CS0"; | |
public static string BaudRate2400 = "CS1"; | |
public static string BaudRate4800 = "CS2"; | |
public static string BaudRate9600 = "CS3"; | |
} | |
} |
Hi @ChrisMoney , I have just purchased a K720 card dispenser and I am trying to get it to work using your snippet, but unfortunately I can't find the DLLs mentioned anywhere. Would it be possible to supply these DLLs. Also some imports such as
using SCL011Reader;
using System.Management;
are marking me as not valid. I am using the latest version of Visual Studio. Thank you very much in advance.
Unfortunately, that code doesn’t include DLLs because it’s just a Gist, and
not in full fledged repo.
…On Sat, Sep 23, 2023 at 1:19 PM anaryk ***@***.***> wrote:
***@***.**** commented on this gist.
------------------------------
Hi @ChrisMoney <https://github.com/ChrisMoney> , I have just purchased a
K720 card dispenser and I am trying to get it to work using your snippet,
but unfortunately I can't find the DLLs mentioned anywhere. Would it be
possible to supply these DLLs. Also some imports such as
using SCL011Reader;
using System.Management;
are marking me as not valid. I am using the latest version of Visual
Studio. Thank you very much in advance.
—
Reply to this email directly, view it on GitHub
<https://gist.github.com/ChrisMoney/56449136882104f917af9c8b8f9d6a8c#gistcomment-4702016>
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAKFJH25BD4TG6ZW3G3WMHDX34R4BBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFQKSXMYLMOVS2I5DSOVS2I3TBNVS3W5DIOJSWCZC7OBQXE5DJMNUXAYLOORPWCY3UNF3GS5DZVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVAZTQMJYG4ZTENVHORZGSZ3HMVZKMY3SMVQXIZI>
.
You are receiving this email because you were mentioned.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
RF610_DLL do you have these dll