using System; using System.IO; using System.Text; using System.IO.Ports; using System.Threading; using Microsoft.SPOT; namespace CPF { public class BTConsole { private static SerialPort consolePort = new SerialPort("COM1", 115200, Parity.None, 8, StopBits.One); private static XModem Modem = new XModem(consolePort, XModem.Variants.XModem1K); private static StructQueue.qStruct qS = new StructQueue.qStruct(); public bool BTSend = true; public virtual void OpenConsolePort() { if (!consolePort.IsOpen) { consolePort.Open(); } consolePort.DiscardInBuffer(); consolePort.DiscardOutBuffer(); consolePort.DataReceived += new SerialDataReceivedEventHandler(consolePortDataReceived); qS.byteArray = new byte[StructQueue.byteArrayWidth]; } private static StringBuilder uploadStartComment = new StringBuilder(128); private static StringBuilder sbFUS = new StringBuilder("File Upload Successful"); private static StringBuilder sbFUF = new StringBuilder("File Upload Failed"); private static StringBuilder uploadStartPrefix = new StringBuilder(128); private static StringBuilder uploadPrefix2 = new StringBuilder(128); public static MemoryStream DataMemoryStream = new MemoryStream(); // Grows dynamically public static byte[] DataArray = new byte[0]; private static FileStream uploadFile; private static int uploadFileLength = 0; private static int maxFileSize = 524288; private static Object BTLock = new Object(); public virtual void detachEventHandler() { //Detach the "regular" console port event handler so the XModem class attach its event handler consolePort.DataReceived -= consolePortDataReceived; } private static StringBuilder sbWaiting = new StringBuilder("Waiting 30 seconds. Setup your PC to receive a file using XModem-1K protocol"); private static StringBuilder sbDoneWaitingWhole = new StringBuilder("Starting XMODEM whole file upload"); private static StringBuilder sbDoneWaitingPieces = new StringBuilder("Starting XMODEM file upload in pieces"); public virtual void uploadDataFile(StringBuilder uploadFileName) { Debug.Print("Starting uploadDataFile"); uploadFile = new FileStream(uploadFileName.ToString(), FileMode.Open, FileAccess.Read); uploadFileLength = (int)uploadFile.Length; //Detach the "regular" console port event handler so the XModem class can attach its event handler consolePort.DataReceived -= consolePortDataReceived; SendLine(sbWaiting); qS.timeStamp = DateTime.Now; qS.state = Program.CPFState; qS.qRecordType = StructQueue.QRecordType.BTConsole; Array.Copy(UTF8Encoding.UTF8.GetBytes(sbWaiting.ToString()), qS.byteArray, sbWaiting.Length); lock (BTLock) { Program.sQueue.Enqueue(qS); } Thread.Sleep(30000); qS.timeStamp = DateTime.Now; qS.state = Program.CPFState; qS.qRecordType = StructQueue.QRecordType.BTConsole; if (uploadFileLength < maxFileSize) { DataArray = new byte[uploadFileLength]; uploadFile.Read(DataArray, 0, uploadFileLength); SendLine(sbDoneWaitingWhole); Array.Copy(UTF8Encoding.UTF8.GetBytes(sbDoneWaitingWhole.ToString()), qS.byteArray, sbDoneWaitingWhole.Length); lock (BTLock) { Program.sQueue.Enqueue(qS); } SendFileWhole(); } else { SendLine(sbDoneWaitingPieces); Array.Copy(UTF8Encoding.UTF8.GetBytes(sbDoneWaitingPieces.ToString()), qS.byteArray, sbDoneWaitingPieces.Length); lock (BTLock) { Program.sQueue.Enqueue(qS); } SendFilePieces(); } uploadFile.Close(); Array.Clear(DataArray, 0, DataArray.Length); //Re-attach "regular" console port event handler consolePort.DataReceived += consolePortDataReceived; } public virtual void uploadDataFileWhole(StringBuilder uploadFileName) { //The XModem SendFileWhole method expects the whole file to be in a byte array in memory //There is a SendFilePieces for really big files Debug.Print("Starting uploadDataFileWhole"); uploadFile = new FileStream(uploadFileName.ToString(), FileMode.Open, FileAccess.Read); uploadFileLength = (int)uploadFile.Length; DataArray = new byte[uploadFileLength]; uploadFile.Read(DataArray, 0, uploadFileLength); //Detach the "regular" console port event handler so the XModem class attach its event handler consolePort.DataReceived -= consolePortDataReceived; SendLine(sbWaiting); qS.timeStamp = DateTime.Now; qS.state = Program.CPFState; qS.qRecordType = StructQueue.QRecordType.BTConsole; Array.Copy(UTF8Encoding.UTF8.GetBytes(sbWaiting.ToString()), qS.byteArray, sbWaiting.Length); lock (BTLock) { Program.sQueue.Enqueue(qS); } Thread.Sleep(30000); SendLine(sbDoneWaitingWhole); qS.timeStamp = DateTime.Now; qS.state = Program.CPFState; qS.qRecordType = StructQueue.QRecordType.BTConsole; Array.Copy(UTF8Encoding.UTF8.GetBytes(sbDoneWaitingWhole.ToString()), qS.byteArray, sbDoneWaitingWhole.Length); lock (BTLock) { Program.sQueue.Enqueue(qS); } SendFileWhole(); //Re-attach "regular" console port event handler consolePort.DataReceived += consolePortDataReceived; } protected static StringBuilder sbTemp = new StringBuilder(512); private static byte[] byteTemp = new byte[1024]; public virtual void SendLine(StringBuilder sbLineToSend) { if (BTSend) { sbTemp.Clear(); sbTemp.Append(sbLineToSend); sbTemp.AppendLine(); Array.Clear(byteTemp, 0, byteTemp.Length); Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), byteTemp, sbTemp.Length); consolePort.Write(byteTemp, 0, byteTemp.Length); } } public static void CloseConsolePort() { consolePort.Close(); } private static byte[] consolePortReadBytes = new byte[64]; private static byte[] consolePortInBytes = new byte[128]; private static byte[] header = UTF8Encoding.UTF8.GetBytes("CON"); private static byte[] message = new byte[256]; private static byte STX = 36; //Start of text = "$" private static byte ETX = 10; //End of text = LF private static int messageNum = 0; private static int consolePortBytesToRead = 0; private static int consolePortInBytesIndex = 0; private static object consoleLock = new object(); private static void consolePortDataReceived(object sender, SerialDataReceivedEventArgs e) { consolePortBytesToRead = consolePort.BytesToRead; try { consolePort.Read(consolePortReadBytes, 0, consolePortBytesToRead); } catch { Debug.Print("Console Read Exception"); //TODO do something smarter here } for (int i = 0; i < consolePortBytesToRead; i++) { consolePortInBytes[consolePortInBytesIndex] = consolePortReadBytes[i]; if (consolePortInBytesIndex == 0) //Check to make sure first character is = STX { if (consolePortInBytes[0] == STX) //only move on to byte index 1 after we get a STX { consolePortInBytesIndex = consolePortInBytesIndex + 1; } } else { if (consolePortInBytes[consolePortInBytesIndex] == ETX) { Array.Clear(qS.byteArray, 0, qS.byteArray.Length); Array.Copy(consolePortInBytes, qS.byteArray, consolePortInBytesIndex); qS.timeStamp = DateTime.Now; qS.state = Program.CPFState; qS.qRecordType = StructQueue.QRecordType.BTConsole; lock (consoleLock) { Program.sQueue.Enqueue(qS); } sbTemp.Clear(); sbTemp.Append(UTF8Encoding.UTF8.GetChars(consolePortInBytes)); Debug.Print(sbTemp.ToString()); consolePortInBytesIndex = 0; messageNum = messageNum + 1; Array.Clear(consolePortInBytes, 0, consolePortInBytes.Length); Array.Clear(consolePortReadBytes, 0, consolePortReadBytes.Length); } else { consolePortInBytesIndex = consolePortInBytesIndex + 1; } } } } //******************************************************************************************************************************** private static StringBuilder sbTestConsoleMenu = new StringBuilder("\nMain Menu-----------------------------------\n" + "s.ample: r.etract bladder: e.xtend bladder\n" + "t. sediment trap: c.ont sampling : q.uit\n"); //******************************************************************************************************************************** private static StringBuilder sbSedTrapConsoleMenu = new StringBuilder("\nSediment Trap------------------------------\n" + "s.ample: p.assthru: w.akeup: o.off \n" + "h.ome : a.dvance : q.uit\n"); //******************************************************************************************************************************** private static StringBuilder sbUnknownInput = new StringBuilder("Unknown Input\n"); //******************************************************************************************************************************** public virtual void run_test_console() { //Detach the "regular" console port event handler consolePort.DataReceived -= consolePortDataReceived; consolePort.DiscardInBuffer(); consolePort.DiscardOutBuffer(); consolePortReadBytes[0] = 0; while (consolePortReadBytes[0] != 96) //while != '`' { //display menu SendLine(sbTestConsoleMenu); consolePort.Flush(); Array.Clear(consolePortReadBytes, 0, consolePortReadBytes.Length); while ((consolePortBytesToRead = consolePort.BytesToRead) == 0) { Thread.Sleep(50); }; try { consolePort.Read(consolePortReadBytes, 0, consolePortBytesToRead); } catch { Debug.Print("Console Read Exception"); //TODO do something smarter here } //sbTemp.Clear(); //sbTemp.Append("You typed: "); //sbTemp.Append(UTF8Encoding.UTF8.GetChars(consolePortReadBytes)); //Debug.Print(sbTemp.ToString()); Debug.Print(consolePortReadBytes[0].ToString()); switch (consolePortReadBytes[0]) { case (115): //s.ample Program.PTH_TEST(); Program.sampleA2D(); break; case(99): //c.ontinuous sampling while(true) { Program.PTH_TEST(); Program.sampleA2D(); } case (114): //r.etract bellows Program.retractBellows(); break; case (101): //e.xtend bellows Program.extendBellows(); break; case (116): //t. sediment trap run_sediment_trap_console(); consolePortReadBytes[0] = 0; break; default: SendLine(sbUnknownInput); break; } //end switch on consolePortReadBytes[0] } //Re-attach "regular" console port event handler consolePort.DataReceived += consolePortDataReceived; } //end run_test_console() //******************************************************************************************************************************** public virtual void run_sediment_trap_console() { //Detach the "regular" console port event handler consolePort.DataReceived -= consolePortDataReceived; consolePort.DiscardInBuffer(); consolePort.DiscardOutBuffer(); consolePortReadBytes[0] = 0; while (consolePortReadBytes[0] != 96) //while != '`' { //display menu SendLine(sbSedTrapConsoleMenu); consolePort.Flush(); Array.Clear(consolePortReadBytes, 0, consolePortReadBytes.Length); while ((consolePortBytesToRead = consolePort.BytesToRead) == 0) { Thread.Sleep(50); }; try { consolePort.Read(consolePortReadBytes, 0, consolePortBytesToRead); } catch { Debug.Print("Console Read Exception"); //TODO do something smarter here } //sbTemp.Clear(); //sbTemp.Append("You typed: "); //sbTemp.Append(UTF8Encoding.UTF8.GetChars(consolePortReadBytes)); //Debug.Print(sbTemp.ToString()); Debug.Print(consolePortReadBytes[0].ToString()); switch (consolePortReadBytes[0]) { case (115): //s.ample SedTrap.getPrompt(); Thread.Sleep(1000); SedTrap.nextpos(60); break; case (112): //p.assthru to sed trap Program.btConsole.passthru(1); consolePortReadBytes[0] = 0; break; case (119): //w.akeup SedTrap.pwrOn(); break; case (111): //o.ff SedTrap.pwrOff(); break; case (104): //h.ome SedTrap.home(60); break; case (97): //a.dvance SedTrap.advance(20); break; default: SendLine(sbUnknownInput); break; } //end switch on consolePortReadBytes[0] } //Re-attach "regular" console port event handler //consolePort.DataReceived += consolePortDataReceived; } //end run_sediment_trap_console() //******************************************************************************************************************************** public virtual void passthru(int device) { //Detach the "regular" console port event handler consolePort.DataReceived -= consolePortDataReceived; consolePort.DiscardInBuffer(); consolePort.DiscardOutBuffer(); consolePortReadBytes[0] = 0; if (device == 1) //talk to sed trap { while (consolePortReadBytes[0] != 96) //while != '`' { //display menu //SendLine(sbTestConsoleMenu); consolePort.Flush(); Array.Clear(consolePortReadBytes, 0, consolePortReadBytes.Length); while ((consolePortBytesToRead = consolePort.BytesToRead) == 0) { Thread.Sleep(50); Program.g400I2C.Config = SedTrap.configSedTrap; I2CUartBridge.get_i2c_buffer(Program.g400I2C); } try { consolePort.Read(consolePortReadBytes, 0, consolePortBytesToRead); Program.g400I2C.Config = SedTrap.configSedTrap; I2CUartBridge.i2cprint(Program.g400I2C, consolePortReadBytes, consolePortBytesToRead); } catch { Debug.Print("Console Read Exception"); //TODO do something smarter here } //sbTemp.Clear(); //sbTemp.Append("You typed: "); //sbTemp.Append(UTF8Encoding.UTF8.GetChars(consolePortReadBytes)); //Debug.Print(sbTemp.ToString()); Debug.Print(consolePortReadBytes[0].ToString()); } } //Re-attach "regular" console port event handler //consolePort.DataReceived += consolePortDataReceived; } //end run_test_console() //******************************************************************************************************************************** public static void ReceiveFileWhole() { // Passing an empty MemoryStream object to the XModem.Receive() method indicates that the user wishes to receive the entire // file in one big lump. NOTE: If using this technique, your device must have enough contiguous RAM to support the expected // file size. Contiguous RAM is the largest memory that can be allocated to a single object. It is usually much smaller // than a device's "total" RAM. // Start the XModem receive process. This method blocks until the transfer has terminated (either through successful // end-of-file, or due to errors such as timeouts, excessive retries, etc). You can examine the termination reason to // determine under what conditions the transfer has stopped. // XModem.TerminationReasonEnum terminationReason = Modem.Receive(DataMemoryStream); //if (terminationReason == XModem.TerminationReasonEnum.EndOfFile) //{ // Debug.Print("LUMP FILE RECEIVE SUCCESSFUL!"); // // Transfer successful, so convert MemoryStream to byte array // DataArray = DataMemoryStream.ToArray(); // // Because XModem packets are a fixed size, if the original transmitted file does not fill a packet completely, // // padding bytes are added until the packet is the required size. The padding byte is usually the SUB symbol // // (byte value 26). You may strip away these padding bytes if desired. This may be a problem, however, // // if one or more SUB bytes dangling at the end is actually a legitimate part of the file (rare, but theoretically // // possible). // // In this example, we will assume that consecutive dangling SUB bytes are never a legitimate part of the file, // // and we can strip them away so that the received file will match its original size. // DataArray = Modem.TrimPaddingBytesFromEnd(DataArray); //} //else //{ // // Something went wrong during the transfer. You may examine the termination reason and respond accordingly. // Debug.Print("LUMP FILE RECEIVE FAILED!"); //} } public virtual bool SendFileWhole() { int numBytesSuccessfullySent = 0; bool success = false; numBytesSuccessfullySent = Modem.Send(DataArray); if (numBytesSuccessfullySent == DataArray.Length && Modem.TerminationReason == XModem.TerminationReasonEnum.EndOfFile) { SendLine(sbFUS); qS.timeStamp = DateTime.Now; qS.state = Program.CPFState; qS.qRecordType = StructQueue.QRecordType.BTConsole; Array.Copy(UTF8Encoding.UTF8.GetBytes(sbFUS.ToString()), qS.byteArray, sbFUS.Length); lock (BTLock) { Program.sQueue.Enqueue(qS); } success = true; } else { SendLine(sbFUF); qS.timeStamp = DateTime.Now; qS.state = Program.CPFState; qS.qRecordType = StructQueue.QRecordType.BTConsole; Array.Copy(UTF8Encoding.UTF8.GetBytes(sbFUF.ToString()), qS.byteArray, sbFUF.Length); lock (BTLock) { Program.sQueue.Enqueue(qS); } success = false; } return (success); } public virtual void SendFilePieces() { // In this example, this is the number of bytes to read from the file during each pass, to be packaged for sending. // This value is completely arbitrary. It may be as small as 1 byte all the way up to a very large value (within device // memory capacity). This is absolutely independent of XModem packet size. The XMODEM.AddToOutboundPacket() method // accepts arrays of any size and packetizes arguments automatically, whether they are larger or smaller than // the chosen packet size. int desiredBlockSize = maxFileSize; // Tracks our position in the file int fileOffset = 0; // Instruct the modem to begin the send process. // We MUST call this method first before calling XMODEM.AddToOutboundPacket() otherwise an exception will be thrown. Modem.Send(); // Loop until all bytes have been read from "persistent storage". while (fileOffset < uploadFileLength) { int numFileBytesRemaining = uploadFileLength - fileOffset; // Determine the number of bytes that we can pull from the source file to populate the current block. int numBytesToPull; if (numFileBytesRemaining >= desiredBlockSize) { // There are enough remaining bytes in the file that we can fill a block completely numBytesToPull = desiredBlockSize; } else { // The number of bytes remaining is smaller than the desired block size, so only // use the available number numBytesToPull = numFileBytesRemaining; } // Instantiate the block that will hold the bytes from persistent storage byte[] block = new byte[numBytesToPull]; // Pretend that we are pulling the data from a file in persistent storage //Array.Copy(DataArray, fileOffset, block, 0, block.Length); //Do this to read from a file instead of pretending uploadFile.Seek(fileOffset, SeekOrigin.Begin); uploadFile.Read(block, 0, numBytesToPull); // Add the current block of data to the outbound packet. This is a blocking method call. // If the modem still does not have enough bytes for a packet, the fresh data is added to the pending packet, // and this method returns immediately. // If the modem has collected enough data for one or more packets, it transmits each packet and blocks // until transmission is complete. Note that this process can be relatively quick if the Receiver responds // quickly, but may be slow if packets have to be re-transmitted or the Receiver does not respond, requiring // timeouts to expire. int numBytesSuccessfullySentDuringThisCall = Modem.AddToOutboundPacket(block); // Determine if the latest packet has been transmitted successfully, or whether an error or timeout has // resulted in the file transfer being cancelled. if (Modem.TerminationReason == XModem.TerminationReasonEnum.TransferStillActiveNotTerminated) { // So far so good, so proceed normally fileOffset += numBytesToPull; } else { // Something bad happened while attempting to send the latest packet Debug.Print("PIECEMEAL FILE SEND FAILED!"); return; } } // Once all bytes have been packetized, we should inform the Receiver that the file is complete. // The EndFile() method automatically transmits any pending packets followed by the end-of-file byte. // We MUST do this to finish the file-send process properly. This is a blocking method call // which blocks until the Receiver acknowledges the end-of-file. Modem.EndFile(); // Examine the ultimate outcome of our file send attempt if (Modem.TerminationReason == XModem.TerminationReasonEnum.EndOfFile) Debug.Print("PIECEMEAL FILE SEND SUCCESSFUL!"); else Debug.Print("PIECEMEAL FILE SEND FAILED!"); } } }