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 StringBuilder portNameSB = new StringBuilder(8);
private string qID;
private byte STX;
private bool UseSTX;
private bool handlerAttached;
private byte ETX = (byte)'\n';
public NativeUART(string id, 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;
qID = id;
UseSTX = useSTX;
STX = inSTX;
ETX = newETX;
portNameSB.Append(portName);
msgQEntry = new QRecord(MessageQueue.width, processMessage);
//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 = 512;
private byte[] bytesRead = new byte[bufferLength];
private byte[] messageBytes = new byte[bufferLength];
private int bytesToRead;
private int messageIndex = 0;
private static readonly string bufferOverrunMsg = "BUFFER OVERRUN ERROR: ";
///
/// 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
{
if (messageBytes[messageIndex - 1] == ETX)
{
EnqueueMessage();
}
else
{
messageIndex = messageIndex + 1;
//sbTemp.Clear();
//sbTemp.Append(UTF8Encoding.UTF8.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);
Array.Copy(messageBytes, msgQEntry.byteArray, messageIndex + 1);
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
}
}
}