diff --git a/src/Api/Readers/TWN4ReaderDevice/Protocols/TWN4ReaderDevice.Io.cs b/src/Api/Readers/TWN4ReaderDevice/Protocols/TWN4ReaderDevice.Io.cs new file mode 100644 index 0000000..3a1d7ed --- /dev/null +++ b/src/Api/Readers/TWN4ReaderDevice/Protocols/TWN4ReaderDevice.Io.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Elatec.NET.Helpers.ByteArrayHelper.Extensions; + +namespace Elatec.NET +{ + public partial class TWN4ReaderDevice + { + #region API_IO / Host I/O Functions + + public static readonly byte API_IO = 1; + + /// + /// Writes a single byte to the requested host channel. + /// + /// Communication endpoint to write to. + /// Byte value to send. + public async Task WriteByteAsync(IoChannel channel, byte value) + { + await CallFunctionAsync(new byte[] { API_IO, 0, (byte)channel, value }); + } + + /// + /// Reads a single byte from the requested host channel. + /// + /// Communication endpoint to read from. + /// The byte read from the reader buffer. + public async Task ReadByteAsync(IoChannel channel) + { + var parser = await CallFunctionAsync(new byte[] { API_IO, 1, (byte)channel }); + return parser.ParseByte(); + } + + /// + /// Checks whether the requested channel buffer is empty for the given direction. + /// + /// Communication endpoint to inspect. + /// Direction of the buffer to test. + /// if the buffer is empty; otherwise . + public async Task TestEmptyAsync(IoChannel channel, IoDirection direction) + { + var parser = await CallFunctionAsync(new byte[] { API_IO, 2, (byte)channel, (byte)direction }); + return parser.ParseBool(); + } + + /// + /// Checks whether the requested channel buffer is already full for the given direction. + /// + /// Communication endpoint to inspect. + /// Direction of the buffer to test. + /// if the buffer is full; otherwise . + public async Task TestFullAsync(IoChannel channel, IoDirection direction) + { + var parser = await CallFunctionAsync(new byte[] { API_IO, 3, (byte)channel, (byte)direction }); + return parser.ParseBool(); + } + + /// + /// Retrieves the configured buffer size for the selected channel and direction. + /// + /// Communication endpoint to query. + /// Direction of the buffer to query. + /// The buffer size in bytes. + public async Task GetBufferSizeAsync(IoChannel channel, IoDirection direction) + { + var parser = await CallFunctionAsync(new byte[] { API_IO, 4, (byte)channel, (byte)direction }); + return parser.ParseUInt16(); + } + + /// + /// Retrieves the number of bytes currently stored in the requested channel buffer. + /// + /// Communication endpoint to query. + /// Direction of the buffer to query. + /// The amount of buffered data in bytes. + public async Task GetByteCountAsync(IoChannel channel, IoDirection direction) + { + var parser = await CallFunctionAsync(new byte[] { API_IO, 5, (byte)channel, (byte)direction }); + return parser.ParseUInt16(); + } + + /// + /// Applies serial framing parameters for the selected COM channel and direction. + /// + /// Communication endpoint to configure. + /// Direction for which the parameters apply. + /// Baud rate to configure. + /// Parity constant as defined by the TWN4 firmware. + /// Stop bits constant as defined by the TWN4 firmware. + /// Number of data bits. + /// if the configuration was accepted; otherwise . + /// Thrown when is zero. + public async Task SetComParametersAsync(IoChannel channel, IoDirection direction, uint baudRate, byte parity, byte stopBits, byte dataBits) + { + if (dataBits == 0) + { + throw new ArgumentOutOfRangeException(nameof(dataBits)); + } + + var payload = new List { API_IO, 6, (byte)channel, (byte)direction }; + payload.AddUInt32(baudRate); + payload.Add(parity); + payload.Add(stopBits); + payload.Add(dataBits); + + var parser = await CallFunctionAsync(payload.ToArray()); + return parser.ParseBool(); + } + + /// + /// Returns the current USB device state. + /// + /// The USB device state reported by the firmware. + public async Task GetUsbDeviceStateAsync() + { + var parser = await CallFunctionAsync(new byte[] { API_IO, 7 }); + return (UsbDeviceState)parser.ParseByte(); + } + + /// + /// Returns the host channel the reader currently uses for upstream communication. + /// + /// The configured host channel. + public async Task GetHostChannelAsync() + { + var parser = await CallFunctionAsync(new byte[] { API_IO, 8 }); + return (IoChannel)parser.ParseByte(); + } + + /// + /// Triggers a remote wakeup on the USB host. + /// + public async Task UsbRemoteWakeupAsync() + { + await CallFunctionAsync(new byte[] { API_IO, 9 }); + } + + /// + /// Writes multiple bytes to the requested host channel. + /// + /// Communication endpoint to write to. + /// Payload to send. + /// when the reader acknowledged the frame; otherwise . + /// Thrown when is . + public async Task WriteBytesAsync(IoChannel channel, byte[] data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + var payload = new List { API_IO, 10, (byte)channel, (byte)data.Length }; + payload.AddRange(data); + var parser = await CallFunctionAsync(payload.ToArray()); + return parser.ParseBool(); + } + + /// + /// Reads the requested number of bytes from the specified host channel. + /// + /// Communication endpoint to read from. + /// Maximum number of bytes to read. + /// Acknowledgement and payload returned by the reader. + public async Task ReadBytesAsync(IoChannel channel, byte length) + { + var parser = await CallFunctionAsync(new byte[] { API_IO, 11, (byte)channel, length }); + var acknowledged = parser.ParseBool(); + var data = parser.ParseFlexByteArray(); + + return new ReadBytesResult + { + Acknowledged = acknowledged, + Data = data + }; + } + + public class ReadBytesResult + { + /// + /// Indicates whether the firmware accepted the request. + /// + public bool Acknowledged { get; set; } + + /// + /// Data returned by the reader. + /// + public byte[] Data { get; set; } + } + + #endregion + } +} diff --git a/src/Api/Readers/TWN4ReaderDevice/TWN4ReaderDevice.System.cs b/src/Api/Readers/TWN4ReaderDevice/TWN4ReaderDevice.System.cs index 247db26..38171eb 100644 --- a/src/Api/Readers/TWN4ReaderDevice/TWN4ReaderDevice.System.cs +++ b/src/Api/Readers/TWN4ReaderDevice/TWN4ReaderDevice.System.cs @@ -87,7 +87,7 @@ public async Task GetDeviceTypeAsync() /// See TWN4 API Reference. public async Task SleepAsync(uint ticks, uint flags) { - List bytes = new List { API_RF, 7 }; + List bytes = new List { API_SYS, 7 }; bytes.AddUInt32(ticks); bytes.AddUInt32(flags); var parser = await CallFunctionAsync(bytes.ToArray()); @@ -210,7 +210,7 @@ public class VersionInfo #region API_PERIPH / Periphery Functions - public static readonly byte API_PERIPH = 1; + public static readonly byte API_PERIPH = 2; // Not supported: SYSFUNC(API_PERIPH, 0, bool SysSetGpioConfig(byte bits, byte pull_up_down, byte output_type)) diff --git a/src/Internal/DataAccess/Constants.cs b/src/Internal/DataAccess/Constants.cs index 6c8c19c..856c668 100644 --- a/src/Internal/DataAccess/Constants.cs +++ b/src/Internal/DataAccess/Constants.cs @@ -434,6 +434,48 @@ public enum ReaderError : uint BErr = 305 } + #region Host I/O + + /// + /// Directions supported by the host channel buffers. + /// + public enum IoDirection : byte + { + Out = 0, + In = 1 + } + + /// + /// Communication channels available for host I/O helpers. + /// + public enum IoChannel : byte + { + Usb = 0, + Com1 = 1, + Com2 = 2, + CcidData = 3, + CcidControl = 4, + I2C = 5, + Rng = 6, + Spi = 7, + Host = 8, + None = 0xFF + } + + /// + /// USB device lifecycle states reported by the reader. + /// + public enum UsbDeviceState : byte + { + NotInitialized = 0, + Default = 1, + Addressed = 2, + Configured = 3, + Suspended = 4 + } + + #endregion + #region GPIOs /// diff --git a/tests/Elatec.NET.Tests/TWN4ReaderDeviceTests.cs b/tests/Elatec.NET.Tests/TWN4ReaderDeviceTests.cs index ee18de7..06d060d 100644 --- a/tests/Elatec.NET.Tests/TWN4ReaderDeviceTests.cs +++ b/tests/Elatec.NET.Tests/TWN4ReaderDeviceTests.cs @@ -49,5 +49,58 @@ public async Task GetVersionStringAsync_ParsesResponseAndSetsLegicFlag() Assert.Equal(versionString, result); Assert.True(device.IsTWN4LegicReader); } + + [Fact] + public async Task GetBufferSizeAsync_UsesIoApiAndParsesUInt16() + { + var transport = new FakeReaderTransport("COM4"); + transport.QueueResponseBytes(0x00, 0x10, 0x27); + var device = new TWN4ReaderDevice("COM4", _ => transport); + + var result = await device.GetBufferSizeAsync(IoChannel.Com1, IoDirection.In); + + Assert.Equal((ushort)10000, result); + Assert.Single(transport.WrittenLines, "01040101"); + } + + [Fact] + public async Task WriteBytesAsync_SendsPayloadLength() + { + var transport = new FakeReaderTransport("COM5"); + transport.QueueResponseBytes(0x00, 0x01); + var device = new TWN4ReaderDevice("COM5", _ => transport); + + var acked = await device.WriteBytesAsync(IoChannel.Usb, new byte[] { 0xAA, 0xBB, 0xCC }); + + Assert.True(acked); + Assert.Single(transport.WrittenLines, "010A0003AABBCC"); + } + + [Fact] + public async Task ReadBytesAsync_ParsesAcknowledgementAndPayload() + { + var transport = new FakeReaderTransport("COM6"); + transport.QueueResponseBytes(0x00, 0x01, 0x02, 0xAA, 0xBB); + var device = new TWN4ReaderDevice("COM6", _ => transport); + + var result = await device.ReadBytesAsync(IoChannel.Usb, 0x02); + + Assert.True(result.Acknowledged); + Assert.Equal(new byte[] { 0xAA, 0xBB }, result.Data); + Assert.Single(transport.WrittenLines, "010B0002"); + } + + [Fact] + public async Task SleepAsync_UsesSysApi() + { + var transport = new FakeReaderTransport("COM7"); + transport.QueueResponseBytes(0x00, 0x01); + var device = new TWN4ReaderDevice("COM7", _ => transport); + + var result = await device.SleepAsync(5, 1); + + Assert.Equal(0x01, result); + Assert.Single(transport.WrittenLines, "00070500000001000000"); + } } }