using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Text; using Modbus.IO; using Modbus.Utility; namespace FtdAdapter { /// /// Wrapper class for the FTD2XX USB resource. /// public class FtdUsbPort : IStreamResource, IDisposable { private const byte PurgeRx = 1; private uint _deviceHandle; private uint _readTimeout; private uint _writeTimeout; private int _baudRate = 9600; private int _dataBits = 8; private byte _stopBits = 1; private byte _parity; /// /// Initializes a new instance of the class. /// public FtdUsbPort() { } /// /// Gets or sets the serial baud rate. /// public int BaudRate { get { return _baudRate; } set { if (value <= 0) throw new ArgumentOutOfRangeException("BaudRate", "BaudRate must be greater than 0."); _baudRate = value; if (IsOpen) InvokeFtdMethod(() => NativeMethods.FT_SetBaudRate(_deviceHandle, (uint) BaudRate)); } } /// /// Gets or sets the standard length of data bits per byte. /// public int DataBits { get { return _dataBits; } set { if (value < 5 || value > 8) throw new ArgumentOutOfRangeException("DataBits", "Value must be greater than 4 and less than 9."); _dataBits = value; if (IsOpen) InvokeFtdMethod(() => NativeMethods.FT_SetDataCharacteristics(_deviceHandle, (byte) DataBits, (byte) StopBits, (byte) Parity)); } } /// /// Indicates that no time-out should occur. /// public int InfiniteTimeout { get { return 0; } } /// /// Gets or sets the number of milliseconds before a time-out occurs when a read operation does not finish. /// public int ReadTimeout { get { return (int) _readTimeout; } set { if (value < 0) throw new ArgumentOutOfRangeException("ReadTimeout", "Read timeout must be greater than 0."); _readTimeout = (uint) value; if (IsOpen) InvokeFtdMethod(() => NativeMethods.FT_SetTimeouts(_deviceHandle, (uint) ReadTimeout, (uint) WriteTimeout)); } } /// /// Gets or sets the number of milliseconds before a time-out occurs when a write operation does not finish. /// public int WriteTimeout { get { return (int) _writeTimeout; } set { if (value < 0) throw new ArgumentOutOfRangeException("WriteTimeout", "Write timeout must be greater than 0."); _writeTimeout = (uint) value; if (IsOpen) InvokeFtdMethod(() => NativeMethods.FT_SetTimeouts(_deviceHandle, (uint) ReadTimeout, (uint) WriteTimeout)); } } /// /// Gets or sets the standard number of stopbits per byte. /// public FtdStopBits StopBits { get { return (FtdStopBits) Enum.Parse(typeof(FtdStopBits), _stopBits.ToString(CultureInfo.InvariantCulture)); } set { _stopBits = (byte) value; if (IsOpen) InvokeFtdMethod(() => NativeMethods.FT_SetDataCharacteristics(_deviceHandle, (byte) DataBits, (byte) StopBits, (byte) Parity)); } } /// /// Gets or sets the parity-checking protocol. /// public FtdParity Parity { get { return (FtdParity) Enum.Parse(typeof(FtdParity), _parity.ToString(CultureInfo.InvariantCulture)); } set { _parity = (byte) value; if (IsOpen) InvokeFtdMethod(() => NativeMethods.FT_SetDataCharacteristics(_deviceHandle, (byte) DataBits, (byte) StopBits, (byte) Parity)); } } /// /// Gets a value indicating the open or closed status of the UsbPort object. /// public bool IsOpen { get { return _deviceHandle != 0; } } /// /// Returns the number of D2XX devices connected to the system. /// [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Operation is expensive.")] public static int GetDeviceCount() { return CreateDeviceInfoList(); } /// /// Gets an array of the currently connected device's device info. /// /// An array of FtdDeviceInfo objects. /// public static FtdDeviceInfo[] GetDeviceInfos() { List deviceInfos = new List(); for (int i = 0; i < CreateDeviceInfoList(); i++) deviceInfos.Add(GetDeviceInfo((uint) i)); return deviceInfos.ToArray(); } /// /// Gets the device info at the specified device index. /// /// Index of the device. /// An FtdDeviceInfo instance. public static FtdDeviceInfo GetDeviceInfo(uint index) { uint flags = 0, type = 0, id = 0, locId = 0, handle = 0; byte[] serial = new byte[16]; byte[] description = new byte[64]; CreateDeviceInfoList(); InvokeFtdMethod(() => NativeMethods.FT_GetDeviceInfoDetail(index, ref flags, ref type, ref id, ref locId, serial, description, ref handle)); return new FtdDeviceInfo(flags, type, id, locId, Encoding.ASCII.GetString(serial).Split('\0')[0], Encoding.ASCII.GetString(description).Split('\0')[0]); } /// /// Opens the device by index. /// /// Must be 0 if only one device is attached. For multiple devices 1, 2 etc. public void OpenByIndex(uint index) { if (IsOpen) throw new InvalidOperationException("Port is already open."); InvokeFtdMethod(() => NativeMethods.FT_Open(index, ref _deviceHandle)); InitializeUsbPort(); } /// /// Opens the device by location id. /// /// The location id. public void OpenByLocationId(uint locationId) { if (IsOpen) throw new InvalidOperationException("Port is already open."); InvokeFtdMethod(() => NativeMethods.FT_OpenEx(locationId, (uint) OpenExFlags.ByLocation, ref _deviceHandle)); InitializeUsbPort(); } /// /// Opens the device by serial number. /// /// The serial number. public void OpenBySerialNumber(string serialNumber) { if (serialNumber == null) throw new ArgumentNullException("serialNumber"); if (IsOpen) throw new InvalidOperationException("Port is already open."); InvokeFtdMethod(() => NativeMethods.FT_OpenEx(Encoding.ASCII.GetBytes(serialNumber), (uint) OpenExFlags.BySerialNumber, ref _deviceHandle)); InitializeUsbPort(); } /// /// Opens the device by description. /// /// The description. public void OpenByDescription(string description) { if (description == null) throw new ArgumentNullException("description"); if (IsOpen) throw new InvalidOperationException("Port is already open."); InvokeFtdMethod(() => NativeMethods.FT_OpenEx(Encoding.ASCII.GetBytes(description), (uint) OpenExFlags.ByDescription, ref _deviceHandle)); InitializeUsbPort(); } /// /// Closes the port connection. /// public void Close() { try { InvokeFtdMethod(() => NativeMethods.FT_Close(_deviceHandle)); } finally { _deviceHandle = 0; } } /// /// Writes a specified number of bytes to the port from an output buffer, starting at the specified offset. /// /// The byte array that contains the data to write to the port. /// The offset in the buffer array to begin writing. /// The number of bytes to write. public void Write(byte[] buffer, int offset, int count) { if (!IsOpen) throw new InvalidOperationException("Port not open."); if (buffer == null) throw new ArgumentNullException("buffer", "Argument buffer cannot be null."); if (offset < 0) throw new ArgumentOutOfRangeException("offset", "Argument offset must be greater than 0."); if (count < 0) throw new ArgumentOutOfRangeException("count", "Argument count must be greater than 0."); if ((buffer.Length - offset) < count) throw new ArgumentException("Invalid buffer size."); uint bytesWritten = 0; byte[] buf = new byte[count]; Array.Copy(buffer, offset, buf, 0, count); InvokeFtdMethod(() => NativeMethods.FT_Write(_deviceHandle, buf, (uint) count, ref bytesWritten)); if (count != 0 && bytesWritten == 0) throw new TimeoutException("The operation has timed out."); if (bytesWritten != count) throw new IOException("Not all bytes written to stream."); } /// /// Reads a number of bytes from the UsbPort input buffer and writes those bytes into a byte array at the specified offset. /// /// The byte array to write the input to. /// The offset in the buffer array to begin writing. /// The number of bytes to read. /// The number of bytes read. public int Read(byte[] buffer, int offset, int count) { if (!IsOpen) throw new InvalidOperationException("Port not open."); if (buffer == null) throw new ArgumentNullException("buffer", "Argument buffer cannot be null."); if (offset < 0) throw new ArgumentOutOfRangeException("offset", "Argument offset cannot be less than 0."); if (count < 0) throw new ArgumentOutOfRangeException("count", "Argument count cannot be less than 0."); if ((buffer.Length - offset) < count) throw new ArgumentException("Invalid buffer size."); uint numBytesReturned = 0; byte[] buf = new byte[count]; InvokeFtdMethod(() => NativeMethods.FT_Read(_deviceHandle, buf, (uint) count, ref numBytesReturned)); Array.Copy(buf, 0, buffer, offset, numBytesReturned); if (count != 0 && numBytesReturned == 0) throw new TimeoutException("The operation has timed out."); return (int) numBytesReturned; } /// /// Purges the receive buffer. /// public void DiscardInBuffer() { if (!IsOpen) throw new InvalidOperationException("Port is not open."); InvokeFtdMethod(() => NativeMethods.FT_Purge(_deviceHandle, PurgeRx)); } /// /// Set flow control. /// ///Type of flow control ///XON symbol ///XOFF symbol [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "1#x")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "2#x")] public void SetFlowControl(FtdFlowControl flowControl, byte xOn, byte xOff) { InvokeFtdMethod(() => NativeMethods.FT_SetFlowControl(_deviceHandle, (ushort) flowControl, xOn, xOff)); } /// /// Invokes FT method and checks the FTStatus result, throw IOException if result is something other than FTStatus.OK /// internal static void InvokeFtdMethod(Func func) { FtdStatus status = func(); if (status != FtdStatus.OK) throw new IOException(Enum.GetName(typeof(FtdStatus), status)); } internal static int CreateDeviceInfoList() { uint deviceCount = 0; InvokeFtdMethod(() => NativeMethods.FT_CreateDeviceInfoList(ref deviceCount)); return (int) deviceCount; } internal void InitializeUsbPort() { BaudRate = BaudRate; SetFlowControl(FtdFlowControl.None, 0, 0); InvokeFtdMethod(() => NativeMethods.FT_SetDataCharacteristics(_deviceHandle, (byte) DataBits, (byte) StopBits, (byte) Parity)); InvokeFtdMethod(() => NativeMethods.FT_SetTimeouts(_deviceHandle, (uint) ReadTimeout, (uint) WriteTimeout)); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (disposing && IsOpen) Close(); } } }