Skip to content

Instantly share code, notes, and snippets.

@ChrisMoney
Last active August 11, 2016 21:40
Show Gist options
  • Save ChrisMoney/0ac23b428ac976d30ddf5a4477d7899c to your computer and use it in GitHub Desktop.
Save ChrisMoney/0ac23b428ac976d30ddf5a4477d7899c to your computer and use it in GitHub Desktop.
C# : Send low level hexadecimal, binary command to K100 debit card dispenser listening on a serial port.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using IntercardLogging;
using System.IO;
using System.Net;
using System.IO.Ports;
using System.Reflection;
using System.Text;
using IntercardLogging;
namespace Dispenser
{
public interface K100A
{
void Process(K100ADriver.Command cmd);
//void Open();
//void Close();
}
public interface IDispenserCom : IDisposable
{
void Open();
void Close();
bool IsOpen { get; }
void Write(byte[] buffer);
void WriteByte(byte value);
int ReadByte();
}
public class K100ADriver : K100A, IDispenser
{
private const byte STX = 0x02;
private const byte ETX = 0x03;
private const byte ACK = 0x06;
private const byte NAK = 0x15;
private const byte ENQ = 0x05;
private const byte EOT = 0x04;
public CommandErrorCode LastError { get; private set; }
IDispenserCom _com;
private static readonly K100ADriver k100ADriver = new K100ADriver();
private static SerialPort _currentPort;
public static SerialPort K100Port {
get { return _currentPort; }
}
public static K100ADriver MakeFromSerialPort(string portName)
{
var thisPort = new SerialPort(portName, 9600, Parity.None, 8, StopBits.One);
thisPort.Open();
// get something back from machine
string version = string.Empty;
thisPort.ReadTimeout = 500;
thisPort.WriteTimeout = 500;
SerialHelper helper = new SerialHelper(thisPort);
_currentPort = thisPort;
K100ADriver dispenser = new K100ADriver();
dispenser._com = helper;
// check connection
K100A k100a = new K100ADriver();
if (!CheckConnection(k100a, out version))
{
thisPort.Close();
}
return dispenser;
}
public static K100ADriver MakeFromUSB()
{
CH375 driver = new CH375();
driver.Open();
K100ADriver dispenser = new K100ADriver();
return dispenser;
}
public K100ADriver()
{
LastError = CommandErrorCode.OK;
}
public void Open()
{
if (!_currentPort.IsOpen)
_currentPort.Open();
}
public void Close()
{
if (_currentPort.IsOpen)
_currentPort.Close();
}
public bool ResetAndGetVersion(out string version)
{
version = string.Empty;
var cmd = new Command(0x30, 0x34);
Process(cmd);
if (!cmd.IsSuccessful)
return false;
version = Encoding.ASCII.GetString(cmd.DataOut);
return true;
}
public static bool CheckConnection(K100A handler, out string version)
{
version = string.Empty;
var cmd = new Command(0x30, 0x34);
handler.Process(cmd);
if (!cmd.IsSuccessful)
return false;
version = Encoding.ASCII.GetString(cmd.DataOut);
return true;
}
public bool InitializeAndEject(K100A handler)
{
var cmd = new Command(0x33, 0x34);
handler.Process(cmd);
return cmd.IsSuccessful;
}
public bool Reset(K100A handler, ResetAction position)
{
var cmd = new K100ADriver.Command(0x30, position);
handler.Process(cmd);
return cmd.IsSuccessful;
}
public bool GetPosition(K100A handler, out CardPositionStatus cardPosition, out StackerStatus stacker)
{
cardPosition = 0;
stacker = 0;
var cmd = new K100ADriver.Command(0x31, 0x30);
handler.Process(cmd);
if (!cmd.IsSuccessful)
return false;
cardPosition = (CardPositionStatus)cmd.DataOut[0];
stacker = (StackerStatus)cmd.DataOut[1];
return true;
}
public bool GetSensors(K100A handler, out SensorStates states)
{
states = new SensorStates();
var cmd = new K100ADriver.Command(0x31, 0x31);
handler.Process(cmd);
if (!cmd.IsSuccessful)
return false;
byte[] d = cmd.DataOut;
states.C_S1 = d[0] == 0x31;
states.C_S2 = d[1] == 0x31;
states.C_S3 = d[2] == 0x31;
states.C_S4 = d[3] == 0x31;
states.C_S5 = d[4] == 0x31;
states.C_S6 = d[5] == 0x31;
states.B_S1 = d[6] == 0x31;
states.B_S2 = d[7] == 0x31;
states.LowCardSensor = d[8] == 0x31;
return true;
}
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;
}
public bool GetVoltages(K100A handler, System.IO.Ports.SerialPort sp, out SensorVoltages voltages)
{
voltages = new SensorVoltages();
var cmd = new K100ADriver.Command(0x31, 0x32);
handler.Process(cmd);
if (!cmd.IsSuccessful)
return false;
byte[] d = cmd.DataOut;
voltages.C_S1 = 5f / 1024 * BitConverter.ToUInt16(d, 0);
voltages.C_S2 = 5f / 1024 * BitConverter.ToUInt16(d, 2);
voltages.C_S3 = 5f / 1024 * BitConverter.ToUInt16(d, 4);
voltages.C_S4 = 5f / 1024 * BitConverter.ToUInt16(d, 6);
voltages.C_S5 = 5f / 1024 * BitConverter.ToUInt16(d, 8);
voltages.C_S6 = 5f / 1024 * BitConverter.ToUInt16(d, 10);
return true;
}
public bool SetCardEntry(K100A handler, CardEntryMode mode)
{
var cmd = new K100ADriver.Command(0x32, mode);
handler.Process(cmd);
return cmd.IsSuccessful;
}
public bool MoveCard(K100A handler, MoveCardPosition position)
{
var cmd = new K100ADriver.Command(0x33, position);
handler.Process(cmd);
return cmd.IsSuccessful;
}
public bool DetectICCardType(K100A handler, out IC_CardType cardType)
{
cardType = IC_CardType.Unknown;
var cmd = new K100ADriver.Command(0x34, 0x30);
handler.Process(cmd);
if (cmd.IsSuccessful)
cardType = (IC_CardType)cmd.DataOut[0];
return cmd.IsSuccessful;
}
public bool ReadCard(K100A handler, out byte[] acct)
{
// Read card on track 2
var cmd = new K100ADriver.Command(0x37, 0x31);
handler.Process(cmd);
byte[] data = cmd.DataOut;
// out entire command and do not convert to string until after it is returned;
acct = cmd.DataOut;
return cmd.IsSuccessful;
}
public void Process(Command cmd)
{
try
{
if (!_currentPort.IsOpen)
_currentPort.Open();
LastError = CommandErrorCode.OK;
byte[] cmdPacket = cmd.CommandPacket;
const int maxRetry = 2;
int respCode = 0;
int retryCount = 0;
CommandState state = CommandState.TryCommand;
while (retryCount < maxRetry && state != CommandState.Complete)
{
switch (state)
{
case CommandState.TryCommand:
_currentPort.Write(cmdPacket, 0, cmdPacket.Length);
// get response from dispenser
try { respCode = _currentPort.ReadByte(); }
catch (TimeoutException) { respCode = -1; }
// if command fails, try again
if (respCode == -1 || respCode == NAK)
retryCount++;
else if (respCode != ACK)
throw new Exception("Unexpected response value: " + respCode);
else
{
state = CommandState.TryENQ;
retryCount = 0;
}
continue;
case CommandState.TryENQ:
// try enquiry
_currentPort.BaseStream.WriteByte(ENQ);
try { respCode = _currentPort.ReadByte(); }
catch (TimeoutException) { respCode = -1; }
if (respCode == -1)
retryCount++;
else if (respCode != STX)
throw new Exception("Unexpected response value: " + respCode);
else
{
state = CommandState.ReadResponse;
retryCount = 0;
}
continue;
// read response
case CommandState.ReadResponse:
try
{
byte lenHi = (byte)_currentPort.ReadByte();
byte lenLo = (byte)_currentPort.ReadByte();
// command expected back
byte expectedBcc = (byte)(STX ^ lenLo ^ lenHi ^ ETX);
byte[] arr = new byte[lenHi << 8 | lenLo];
for (int i = 0; i < arr.Length; i++)
{
byte b = (byte)_currentPort.ReadByte();
arr[i] = b;
expectedBcc ^= b;
}
byte etx = (byte)_currentPort.ReadByte();
if (etx != ETX)
throw new Exception("Unexpected value at ETX: " + etx);
// Block Check Character: XOR operation for every byte from STX to ETX
byte bcc = (byte)_currentPort.ReadByte();
if (bcc != expectedBcc || arr[1] != cmd.CM || arr[2] != cmd.PM)
{
state = CommandState.TryENQ;
retryCount++;
continue;
}
if (arr[0] != 'P' && arr[0] != 'N')
throw new Exception("Unexpected Command success byte: " + arr[0]);
cmd.IsSuccessful = (arr[0] == 'P');
if (cmd.IsSuccessful)
{
cmd.DataOut = new byte[arr.Length - 3];
Array.Copy(arr, 3, cmd.DataOut, 0, cmd.DataOut.Length);
}
else
{
LastError = (CommandErrorCode)arr[6];
cmd.ErrorCode = LastError;
}
state = CommandState.Complete;
continue;
}
catch (TimeoutException)
{
state = CommandState.TryENQ;
retryCount++;
continue;
}
}
}
if (retryCount >= maxRetry)
{
_currentPort.BaseStream.WriteByte(ENQ);
try
{
respCode = _currentPort.ReadByte();
throw new TimeoutException();
}
catch (TimeoutException)
{
throw;
}
}
}
finally
{
}
}
public void Dispose()
{
_currentPort.Dispose();
_currentPort = null;
}
private enum CommandState
{
TryCommand,
TryENQ,
ReadResponse,
TryEOT,
Complete
}
public class SerialHelper : IDispenserCom
{
SerialPort SerialPort { get; set; }
public SerialHelper(SerialPort serialPort)
{
SerialPort = serialPort;
}
public void Open() { SerialPort.Open(); }
public void Close() { SerialPort.Close(); }
public bool IsOpen { get { return SerialPort.IsOpen; }}
public void Write(byte[] buffer) { SerialPort.Write(buffer, 0, buffer.Length); }
public void WriteByte(byte value) { SerialPort.BaseStream.WriteByte(value); }
public int ReadByte() { return SerialPort.ReadByte(); }
public void Dispose() { SerialPort.Dispose(); }
}
public class Command
{
public byte CM { get; set; }
public byte PM { get; set; }
public byte[] DataIn { get; set; }
public byte[] DataOut { get; set; }
public Func<object[], byte[]> EncodePayload { get; set; }
public Func<byte[], object[]> ParsePayload { get; set; }
public bool IsSuccessful { get; set; }
public CommandErrorCode? ErrorCode { get; set; }
public byte[] CommandPacket
{
get
{
short cmdLen = 2;
if (DataIn != null)
cmdLen += (short)DataIn.Length;
List<byte> buffer = StandardConcat(STX, cmdLen, CM, PM, DataIn, ETX);
byte bcc = 0;
for (int i = 0; i < buffer.Count; i++)
// XOR check sum for command packet
bcc ^= buffer[i];
buffer.Add(bcc);
return buffer.ToArray();
}
}
public Command(byte cm, byte pm, Func<object[], byte[]> encodePayload = null)
{
CM = cm;
PM = pm;
EncodePayload = encodePayload;
ParsePayload = ParsePayload;
}
public Command(byte cm, Enum pm, Func<object[], byte[]> encodePayload = null)
{
CM = cm;
PM = (byte)Convert.ToInt32(pm);
EncodePayload = encodePayload;
ParsePayload = ParsePayload;
}
public void SetData(params object[] values)
{
if (values == null || values.Length == 0)
DataIn = null;
else if (EncodePayload != null)
DataIn = EncodePayload(values);
else
DataIn = StandardConcat(values).ToArray();
}
public object[] ReadResponse()
{
if (ParsePayload == null)
return new object[] { DataOut };
return ParsePayload(DataOut);
}
private static List<byte> StandardConcat(params object[] values)
{
List<byte> buffer = new List<byte>();
foreach (object o in values)
buffer.AddRange(StandardEncode(o));
return buffer;
}
private static byte[] StandardEncode(object o)
{
if (o == null)
return new byte[0];
if (o is Enum)
return new byte[] { (byte)Convert.ToInt32(o) };
if (o is byte || o is sbyte || o is char)
return new byte[] { (byte)o };
if (o is int || o is uint)
// order bytes by Big Endian
return BitConverter.GetBytes(IPAddress.HostToNetworkOrder((int)o));
if (o is short || o is ushort)
return BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)o));
if (o is long || o is ulong)
return BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)o));
if (o is byte[])
return (byte[])o;
if (o is IEnumerable<byte>)
return ((IEnumerable<byte>)o).ToArray();
throw new InvalidDataException("Unexpected value type: " + o.GetType().Name);
}
}
/// <summary>
/// Used to determine if port was written to
/// </summary>
/// <param name="cmd"></param>
/// <param name="sp"></param>
/// <param name="bytesToSend"></param>
public void WriteToSerial(ushort cmd, SerialPort sp, byte[] bytesToSend = null)
{
List<byte> buffer = new List<byte>();
buffer.Add(0x02);
int commandLen = 2;
if (bytesToSend != null)
commandLen += bytesToSend.Length;
buffer.Add((byte)(commandLen >> 8));
buffer.Add((byte)(commandLen & 0xFF));
buffer.Add((byte)(cmd >> 8));
buffer.Add((byte)(cmd & 0xFF));
if (bytesToSend != null)
buffer.AddRange(bytesToSend);
buffer.Add(0x03);
byte bcc = 0;
for (int i = 0; i < buffer.Count; i++)
bcc ^= buffer[i];
buffer.Add(bcc);
byte[] arr = buffer.ToArray();
sp.Write(arr, 0, arr.Length);
}
// Interface method
public void PositionCardToRead()
{
var cmd = new Command(0x30, ResetAction.Mag_Position);
K100A handler = new K100ADriver();
handler.Process(cmd);
}
// Interface method
public void Read()
{
var cmd = new Command(0x37, ResetAction.Read);
K100A handler = new K100ADriver();
handler.Process(cmd);
}
// Interface method
public void Dispense()
{
var cmd = new Command(0x30, ResetAction.Eject_Card);
K100A handler = new K100ADriver();
handler.Process(cmd);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment