using System; using System.IO.Ports; using System.Text; using Microsoft.SPOT; using SWModules; namespace HWModules { public class ExarUart : I2CSerialPort { private MessageQueue messageQ; private QRecord msgQEntry; private StringBuilder portNameSB = new StringBuilder(5); private bool _handlerAttached; private byte STX; private string qID; private bool _useSTX; public ExarUart(string id, QRecordType type, ProcessMessage processMessage, string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, bool useSTX, byte inSTX) : base(portName, baudRate, dataBits, parity, stopBits) { _storedTimeout = readTimeout; //WriteTimeout = writeTimeout; STX = inSTX; qID = id; _useSTX = useSTX; portNameSB.Append(portName); msgQEntry = new QRecord(MessageQueue.width, processMessage); msgQEntry.type = type; AttachDefaultDataHandler(); ErrorReceived += errorReceived; messageQ = MessageQueue.Instance; } /// /// Attaches the default message handler for serial data into the message queue /// public void AttachDefaultDataHandler() { if (!_handlerAttached) { if (_useSTX) DataReceived += dataReceivedSTX; else DataReceived += dataReceivedNoSTX; _handlerAttached = true; } } /// /// Releases the native uart handler for users that need direct port access for non message /// queue interactions. /// public void ReleaseDefaultDataHandler() { if (_handlerAttached) { if (_useSTX) DataReceived -= dataReceivedSTX; else DataReceived -= dataReceivedNoSTX; _handlerAttached = false; } } private const int bufferLength = 2056; private const byte ETX1 = 10; private const byte ETX2 = 13; private byte[] bytesRead = new byte[bufferLength]; private byte[] messageBytes = new byte[bufferLength]; private int bytesToRead; private int messageIndex = 0; private static readonly string bufferOverrunMsg = "[ERROR] BUFFER OVERRUN: "; /// /// Default handler function for data received on the serial port, puts data on the /// MessageQueue. No terminator character is required for push into the Queue. /// /// /// public void dataReceivedNoSTX(object sender, EventArgs e) //TODO P2 need several unit tests { ReadPort(); for (int i = 0; i < bytesToRead; i++) { messageBytes[messageIndex] = bytesRead[i]; if (messageBytes[messageIndex] == ETX1 || messageBytes[messageIndex] == ETX2) { //Fix for empty message, don't enqueue! if (messageIndex != 0) { sbTemp.Clear(); sbTemp.Append(SafeEncoding.GetChars(messageBytes)); EnqueueMessage(); } } else messageIndex = messageIndex + 1; //TODO P1 need to check messageIndex against bufferLength in all drivers and need to unit test this block and the one in the STX event handler //Make sure messageIndex doesn't exceed bufferLength if (CheckForOverrun()) break; } if (BytesToRead != 0) dataReceivedNoSTX(this, EventArgs.Empty); if (messageIndex != 0) EnqueueMessage(); } /// /// Default handler function for data received on the serial port, putting data on the /// MessageQueue. A terminator property must be set to use this function. /// /// /// public void dataReceivedSTX(object sender, EventArgs e) //TODO P2 need a unit test { ReadPort(); for (int i = 0; i < bytesToRead; i++) { messageBytes[messageIndex++] = bytesRead[i]; if (messageIndex-1 == 0) //Check to make sure first character is = STX { if (messageBytes[0] != STX) //only move on to byte index 1 after we get a STX messageIndex = 0; } else { if (messageBytes[messageIndex - 1] == ETX1) { EnqueueMessage(); } } //TODO P1 need to check messageIndex against bufferLength in all drivers //Make sure messageIndex doesn't exceed bufferLength if (CheckForOverrun()) break; } if (BytesToRead != 0) dataReceivedSTX(this, EventArgs.Empty); if (messageIndex != 0) EnqueueMessage(); } /// /// Override of the I2CSerialPort open function to automatically set the time that was /// stored in the constructor. /// /// true on success public override bool Open() { bool retval = base.Open(); TimeOut = _storedTimeout; //Set the timeout now that the bridge is open return retval; } private bool CheckForOverrun() { if (messageIndex >= bufferLength - 2) { LogBufferOverrun(); return true; } return false; } private void ReadPort() { bytesToRead = BytesToRead; try { Read(bytesRead, 0, bytesToRead); } catch { LogReadError(); } } private void EnqueueMessage() { msgQEntry.Update(); msgQEntry.errorCode = 0; Array.Clear(msgQEntry.byteArray, 0, msgQEntry.byteArray.Length); msgQEntry.length = messageIndex + 1; Array.Copy(messageBytes, msgQEntry.byteArray, msgQEntry.length); messageQ.Enqueue(msgQEntry); messageIndex = 0; Array.Clear(messageBytes, 0, messageBytes.Length); } private void LogReadError() { sbTemp.Clear(); sbTemp.Append("ERROR: Read at port: "); sbTemp.Append(portNameSB); sbTemp.Append(" " + qID); EngrLogger.writeToColumns(sbTemp.ToString()); //TODO P2 probably needs an error handler } private void LogBufferOverrun() { msgQEntry.Update(); msgQEntry.errorCode = -1; sbTemp.Clear(); sbTemp.Append(bufferOverrunMsg); sbTemp.Append(" "); sbTemp.Append(portNameSB); sbTemp.Append(" "); sbTemp.Append(qID); sbTemp.Append(" "); sbTemp.Append(SafeEncoding.GetChars(messageBytes)); EngrLogger.writeToColumns(sbTemp); messageIndex = 0; Array.Clear(messageBytes, 0, messageBytes.Length); } StringBuilder sbTemp = new StringBuilder(64); private int _storedTimeout; public void errorReceived(object sender, EventArgs e) { sbTemp.Clear(); sbTemp.Append("Native UART Error Received, at port: "); sbTemp.Append(portNameSB); EngrLogger.writeToColumns(sbTemp); //TODO P2 Probably needs an error handler } } }