using System; using System.IO.Ports; using System.Text; using SWModules; using Microsoft.SPOT; namespace HWModules { public class NativeUART : SerialPort { private MessageQueue messageQ; private QRecord msgQEntry; private string qID; private StringBuilder portNameSB = new StringBuilder(8); private byte STX; private bool UseSTX; private bool handlerAttached; private byte ETX = (byte)'\n'; public NativeUART(string id, QRecordType type, ProcessMessage processMessage, string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, bool useSTX, byte inSTX, byte newETX = (byte)'\n') : base(portName, baudRate, parity, dataBits, stopBits) { //Property Assignment ReadTimeout = readTimeout; WriteTimeout = writeTimeout; UseSTX = useSTX; STX = inSTX; ETX = newETX; qID = id; portNameSB.Append(portName); msgQEntry = new QRecord(MessageQueue.width, processMessage); msgQEntry.type = type; //Attach Handlers AttachDefaultDataHandler(); ErrorReceived += errorReceived; //Grab Singleton 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 = 2048; 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. /// /// /// /// private static Type thisType; public void dataReceivedNoSTX(object sender, SerialDataReceivedEventArgs e) //TODO P2 need several unit tests { ReadPort(); for (int i = 0; i < bytesToRead; i++) { messageBytes[messageIndex] = bytesRead[i]; //sbTemp.Clear(); //sbTemp.Append("RX: "); //sbTemp.Append(SafeEncoding.GetChars(messageBytes)); //Debug.Print(sbTemp.ToString()); if (messageBytes[messageIndex] == ETX) { 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; } } /// /// 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, SerialDataReceivedEventArgs e) //TODO P2 need a unit test { ReadPort(); for (int i = 0; i < bytesToRead; i++) { messageBytes[messageIndex] = bytesRead[i]; if (messageIndex == 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 = messageIndex + 1; } else { //TODO P0 make sure this is really wrong //if (messageBytes[messageIndex - 1] == ETX) if (messageBytes[messageIndex] == ETX) { EnqueueMessage(); } else { messageIndex = messageIndex + 1; //sbTemp.Clear(); //sbTemp.Append(SafeEncoding.GetChars(messageBytes)); //Debug.Print(sbTemp.ToString()); } } //TODO P1 need to check messageIndex against bufferLength in all drivers //Make sure messageIndex doesn't exceed bufferLength if (CheckForOverrun()) break; } } 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 Error 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, 0, 128)); EngrLogger.writeToColumns(sbTemp); messageIndex = 0; Array.Clear(messageBytes, 0, messageBytes.Length); } StringBuilder sbTemp = new StringBuilder(512); public void errorReceived(object sender, SerialErrorReceivedEventArgs e) { sbTemp.Clear(); sbTemp.Append("Native UART Error Received, at port: "); sbTemp.Append(portNameSB); EngrLogger.writeToColumns(sbTemp); //TODO P2 Probably needs an error handler } } }