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
}
}
}