using System; using System.IO; using System.Text; using System.Threading; using HWModules; using Microsoft.SPOT; using SWModules; namespace SensorModules { public static class SciLogger { private static CTD1 ctd1; private static FLBB1 flbb1; private static MSC1 msc1; private static Optode1 optode1; private static Radiometer1 radiometer1; public static event SendLineDelegate SendConsoleLine; private static FileQueue _fileQ; private static QRecord _qRecord; private static FileStream msgFile; private static readonly StringBuilder msgFileName = new StringBuilder(64); private static FileStream logFile; private static readonly StringBuilder logFileName = new StringBuilder(64); private static FileStream isusFile; private static readonly StringBuilder isusFileName = new StringBuilder(64); private static FileStream duraFile; private static readonly StringBuilder duraFileName = new StringBuilder(64); public static StringBuilder CurrentState { get; set; } private static byte[] colNames = UTF8Encoding.UTF8.GetBytes( "\r\nComment\tDateTime\tpres_dBar\ttemp_degC\tsal_ppt\tTopt_degC\tTPhase_deg\tRPhase_deg\tpH_V\tNO3_V\tFSig_counts\tBbSig_counts\tTSig_counts\tOCRCh1_uWPerCM2PerNM\tOCRCh2\tOCRCh3\tOCRCh4\r\n"); private static StringBuilder sbTemp = new StringBuilder(2056); private static StringBuilder sbTemp2 = new StringBuilder(2056); private static readonly byte[] Crlf = {13, 10}; private static readonly byte[] Eot = {0x3C, 0x45, 0x4F, 0x54, 0x3E, 0x0D, 0x0A}; //"\r\n" private static readonly byte[] Eop = {0x3C, 0x45, 0x4F, 0x50, 0x3E, 0x20, 0x3A, 0x20, }; //" : " private const byte tab = (byte)Literals.tabChar; private static StringBuilder gpsPositionSB = new StringBuilder("Lon Lat numSats gpsFixTime TTFF:\t no fix"); //private static bool msgFileOpen = false; public static void closeFiles() { clearFileQueue(); closeIsusFile(); closeMsgFile(); closeDuraFile(); closeLogFile(); EngrLogger.sdVol.FlushAll(); } public static void openNewFiles() { openNewDuraFile(); openNewLogFile(); openNewMsgFile(); openNewIsusFile(); } public static void openNewMsgFile() { if (ctd1 == null) { ctd1 = CTD1.Instance; flbb1 = FLBB1.Instance; msc1 = MSC1.Instance; optode1 = Optode1.Instance; radiometer1 = Radiometer1.Instance; } if (_fileQ == null) _fileQ = new FileQueue(); closeMsgFile(); msgFileFooter.Clear(); msgFileFooter.Append("Profile details\r\n"); msgFileFooter.Append("Date Time\tComment\tPres (dBar)\tBel Pos (mm)\tBus Volt\tBus Cur (mA)\r\n"); cpData.Clear(); cpData.Append("\r\nCP Data\r\n"); msgFileName.Clear(); msgFileName.Append(EngrLogger.fileNameBase); msgFileName.Append(".msg"); try { msgFile = new FileStream(msgFileName.ToString(), FileMode.OpenOrCreate, FileAccess.Write); if (!msgFileOpen) throw new Exception("File Open Error"); } catch { EngrLogger.writeToColumns("[ERROR] opening .msg file"); SV.ForceRecoveryState = true; SV.ForceRecoveryReason.Clear(); SV.ForceRecoveryReason.Append("Error opening msg file"); } } /// /// Open a new Isus Data file, give it the same name as the current science file, /// but with a .isus extension. /// public static void openNewIsusFile() { closeIsusFile(); isusFileName.Clear().Append(EngrLogger.fileNameBase).Append(".isus"); try { isusFile = new FileStream(isusFileName.ToString(), FileMode.OpenOrCreate, FileAccess.Write); if (!isusFileOpen) throw new Exception("File Open Error, ISUS"); EngrLogger.Comment("Opening new .isus file: "+isusFileName); } catch { SV.ForceRecoveryState = true; SV.ForceRecoveryReason.Clear(); SV.ForceRecoveryReason.Append("Error opening isus file"); EngrLogger.Comment("[ERROR] opening .isus file."); } } /// /// Open a new DuraFet file, give it the same name as the current science file, /// but with a .dura extension /// public static void openNewDuraFile() { closeDuraFile(); duraFileName.Clear().Append(EngrLogger.fileNameBase).Append(".dura"); try { duraFile = new FileStream(duraFileName.ToString(), FileMode.OpenOrCreate, FileAccess.Write); if (!duraFileOpen) throw new Exception("File Open Error, DURA"); EngrLogger.Comment("Opening new .dura file: "+duraFileName); } catch { SV.ForceRecoveryState = true; EngrLogger.Comment("[ERROR] opening .dura file."); SV.ForceRecoveryReason.Clear(); SV.ForceRecoveryReason.Append("Error opening dura file"); } } private static StringBuilder sbDataFileHeader = new StringBuilder(64); private const string FmtDateTime = "ddd MMM dd HH:mm:ss yyyy"; /// /// Build the first line of either a .dura or .isus data /// file, which includes the profile number and start datetime /// as provided. /// /// public static void appendMscDataFileTimeStamp(DateTime timeStamp, bool isEndofProfile=false) { sbDataFileHeader.Clear().Append("Profile: "); sbDataFileHeader.Append(SV.FloatID).Append('.'); sbDataFileHeader.Append(SV.ProfileNum.ToString("D6")); sbDataFileHeader.Append(Literals.spaceString); sbDataFileHeader.Append(timeStamp.ToString(FmtDateTime)); sbDataFileHeader.Append(Literals.crlfString); if (isusFileOpen) { if (isEndofProfile) isusFile.Write(Eop,0,Eop.Length); isusFile.Write(SafeEncoding.UTF8.GetBytes(sbDataFileHeader.ToString()), 0, sbDataFileHeader.Length); } if (duraFileOpen) { if (isEndofProfile) duraFile.Write(Eop,0,Eop.Length); duraFile.Write(SafeEncoding.UTF8.GetBytes(sbDataFileHeader.ToString()), 0, sbDataFileHeader.Length); } } /// /// Close the Durafet Data File /// public static void closeDuraFile() { if (!duraFileOpen) return; duraFile.Write(Crlf, 0, Crlf.Length); duraFile.Write(Eot, 0, Eot.Length); duraFile.Write(Crlf, 0, Crlf.Length); EngrLogger.writeToColumns("Closing .dura File"); EnqueueFileForXfer(duraFileName.ToString()); duraFile.Close(); } /// /// Close the Isus Data File /// public static void closeIsusFile() { if (!isusFileOpen) return; isusFile.Write(Crlf, 0, Crlf.Length); isusFile.Write(Eot, 0, Eot.Length); isusFile.Write(Crlf, 0, Crlf.Length); EngrLogger.writeToColumns("Closing .isus File"); EnqueueFileForXfer(isusFileName.ToString()); isusFile.Close(); } /// /// Closes the Science file, and enqueues it for transmission. /// public static void closeMsgFile() { if (!msgFileOpen) return; msgFile.Write(Crlf, 0, Crlf.Length); msgFile.Write(Encoding.UTF8.GetBytes(gpsPositionSB.ToString()), 0, gpsPositionSB.Length); msgFile.Write(Crlf, 0, Crlf.Length); writeMsgFooter(); EngrLogger.writeToColumns("Closing .msg File"); EnqueueFileForXfer(msgFileName.ToString()); msgFile.Close(); } private static bool logFileOpen { get { if (logFile == null) return false; return logFile.CanWrite; } } private static bool msgFileOpen { get { if (msgFile == null) return false; return msgFile.CanWrite; } } private static bool isusFileOpen { get { if (isusFile == null) return false; return isusFile.CanWrite; } } private static bool duraFileOpen { get { if (duraFile == null) return false; return duraFile.CanWrite; } } public static void openNewLogFile() { if (logFileOpen) closeLogFile(); logFileName.Clear(); logFileName.Append(EngrLogger.fileNameBase); logFileName.Append(".log"); try { logFile = new FileStream(logFileName.ToString(), FileMode.OpenOrCreate, FileAccess.Write); if (!logFileOpen) { sbTemp.Clear(); sbTemp.Append("[ERROR] opening log file"); EngrLogger.writeToColumns(sbTemp); } } catch { EngrLogger.writeToColumns("[ERROR] opening .log File"); } } public static void closeLogFile() { if (logFile == null) return; if (!logFileOpen) return; EngrLogger.writeToColumns("Closing .log File"); logFile.Close(); EnqueueFileForXfer(logFileName.ToString()); } /// /// Allow other objects to add lines to SciFile Footer /// private static StringBuilder msgFileFooter = new StringBuilder(1024); public static void appendToMsgFooter(StringBuilder newLine) { msgFileFooter.Append("# "); msgFileFooter.Append(newLine); msgFileFooter.Append("\r\n"); } /// /// Write SciFile footer /// private static void writeMsgFooter() { EngrLogger.writeToColumns("Writing Footer to msg File"); msgFile.Write(Encoding.UTF8.GetBytes(msgFileFooter.ToString()), 0, msgFileFooter.Length); msgFile.Flush(); } /// /// Allow other objects to add lines to SciFile Header /// public static void appendToMsgHeader(StringBuilder newLine) { msgFileHeader.Append("# "); msgFileHeader.Append(newLine); msgFileHeader.Append(Literals.crlfString); } private static StringBuilder msgFileHeader = new StringBuilder(1024); /// /// Write out the header to file /// public static void writeMsgHeader() { EngrLogger.writeToColumns("Writing Header to msg File"); if (msgFileHeader.Length == 0) { sbTemp.Clear().Append("CPF"); appendToMsgHeader(sbTemp); } msgFile.Write(SafeEncoding.UTF8.GetBytes(msgFileHeader.ToString()), 0, msgFileHeader.Length); msgFile.Write(colNames, 0, colNames.Length); msgFileHeader.Clear(); msgFile.Flush(); } /// /// Append the cp data /// /// byte array containing the segment of CP data to be copied into the file. public static void appendCPData(byte[] inBytes) { sbTemp.Clear(); sbTemp.Append(SafeEncoding.GetChars(inBytes)); cpData.Append(sbTemp); sbTemp.Clear(); sbTemp.Append("Appending CP Data: "); sbTemp.Append(SafeEncoding.GetChars(inBytes)); EngrLogger.writeToColumns(sbTemp.Replace(Literals.tabChar, Literals.commaChar)); } private static StringBuilder cpData = new StringBuilder(2048); /// /// Write out all stored CP data to the file. /// public static void writeCPData() { EngrLogger.writeToColumns("Writing CP data to msg File"); appendCPData(UTF8Encoding.UTF8.GetBytes("End of CP data\r\n\r\n")); msgFile.Write(UTF8Encoding.UTF8.GetBytes(cpData.ToString()), 0, cpData.Length); msgFile.Flush(); } /// /// Sample types for byte array storage and output /// [Flags] public enum Instruments : uint { Ctd = 0x0001, Optode = 0x0002, Ph = 0x0004, NO3 = 0x0008, Flbb = 0x0010, Radiometer = 0x0020, Reserved1 = 0x0040, Reserved2 = 0x0080, Reserved3 = 0x0100, Reserved4 = 0x0200, Reserved5 = 0x0400, Reserved6 = 0x0800, Reserved8 = 0x1000, Reserved9 = 0x2000, Reserved10 = 0x4000, Reserved11 = 0x8000, AllInstruments = 0x3F, // modify when adding new instrument None = 0x0000, } /// /// Determine if an instrument should be sampled. /// /// /// /// public static bool sampling(Instruments instr) { return (instr & instrumentsToSample) > 0; } public static bool posted(Instruments instr) { return (instr & instrumentsPosted) > 0; } private static bool shouldPost(Instruments instr) { return ((instr & instrumentsToSample) > 0) && !posted(instr); } private static StringBuilder ctdSB = new StringBuilder(32); private static StringBuilder optodeSB = new StringBuilder(64); private static StringBuilder phSB = new StringBuilder(16); private static StringBuilder no3SB = new StringBuilder(16); private static StringBuilder flbbSB = new StringBuilder(16); private static StringBuilder radiometerSB = new StringBuilder(32); private static StringBuilder startStateSB = new StringBuilder(32); private static Instruments instrumentsToSample; private static Instruments instrumentsPosted; //private static bool gotCTD = false; //private static bool gotOptode = false; //private static bool gotPH = false; //private static bool gotNO3 = false; //private static bool gotFLBB = false; //private static bool gotRadiometer = false; //public static bool getCTD = false; //public static bool getOptode = false; //public static bool getPH = false; public static bool waitForCTDThenGetPH = false; //public static bool getNO3 = false; public static bool waitForCTDThenGetNO3 = false; //public static bool getFLBB = false; //public static bool getRadiometer = false; private static int missedCTD = 0; private static int missedOptode = 0; private static int missedPH = 0; private static int missedNO3 = 0; private static int missedFLBB = 0; private static int missedOCR = 0; //public static bool sampleInProgress = false; private static StringBuilder sampleStartTimeStamp = new StringBuilder(32); private static void resetSampleStrings() { ctdSB.Clear(); ctdSB.Append("NaN\tNaN\tNaN"); optodeSB.Clear(); optodeSB.Append("NaN\tNaN\tNaN"); phSB.Clear(); phSB.Append("NaN"); radiometerSB.Clear(); radiometerSB.Append("NaN\tNaN\tNaN\tNaN"); no3SB.Clear(); no3SB.Append("NaN"); flbbSB.Clear(); flbbSB.Append("NaN\tNaN\tNaN"); } //TODO P2 need to make sure SciLogger is not active in recovery mode public static void startNewSampleCycle(Instruments instrumentsList) { if ((SV.CurrentState == CPFStates.anchor) || (SV.CurrentState == CPFStates.park) || (SV.CurrentState == CPFStates.ascend) || (SV.CurrentState == CPFStates.surfaceOps)) { if (instrumentsPosted > 0) { EngrLogger.writeToColumns("SciLogger is writing previous sample before starting new one"); writeSample(); } instrumentsToSample = instrumentsList; instrumentsPosted = Instruments.None; EngrLogger.writeToColumns("Starting New SciLogger Sample Cycle"); startStateSB.Clear(); if (SV.CurrentState == CPFStates.anchor) startStateSB.Append("AnchorPt:"); else if (SV.CurrentState == CPFStates.park) startStateSB.Append("ParkPt:"); else if (SV.CurrentState == CPFStates.surfaceOps) startStateSB.Append("AirCal:"); else if (SV.CurrentState == CPFStates.ascend) startStateSB.Append("AscendPt:"); else if (SV.CurrentState == CPFStates.descend) startStateSB.Append("DescendPt:"); else startStateSB.Append("UnknownPt:"); if (sampling(Instruments.Ctd)) { ctdSB.Clear(); ctdSB.Append("NaN\tNaN\tNaN"); //Don't take a PTS sample in ascend mode because SBE41 is in CP mode and already outputting a PTS sample if (!SV.InCPMode) { if (SV.CurrentState == CPFStates.surfaceOps) { ctd1.sendCRLF(); Thread.Sleep(1000); EngrLogger.writeToColumns("Sci Logger sending FastPressure to CTD"); ctd1.sendFastPressure(); } else if (ctd1.CommandModeTimerEnabled) { // This will flip a flag to request a PTS on the next inquiry to the CTD. EngrLogger.Comment("Sci Logger requesting PTS on next sample"); ctd1.OnNextRequest(true); } else { ctd1.sendCRLF(); Thread.Sleep(1000); EngrLogger.writeToColumns("Sci Logger sending PTS to CTD"); ctd1.sendPTS(); } } } if (sampling(Instruments.Optode)) { optodeSB.Clear(); optodeSB.Append("NaN\tNaN\tNaN"); EngrLogger.writeToColumns("Sci Logger sending sample Optode"); optode1.doSample(); } //TODO P2 need to create cases for pH only and NO3 only if (sampling(Instruments.Ph) || sampling(Instruments.NO3)) { phSB.Clear(); phSB.Append("NaN"); no3SB.Clear(); no3SB.Append("NaN"); waitForCTDThenGetPH = true; waitForCTDThenGetNO3 = true; EngrLogger.writeToColumns("Sci Logger set wait for CTD then sample pH and NO3 true"); } else { waitForCTDThenGetPH = false; waitForCTDThenGetNO3 = false; } if (sampling(Instruments.Flbb)) { flbbSB.Clear(); flbbSB.Append("NaN\tNaN\tNaN"); EngrLogger.writeToColumns("Sci Logger sending sample FLBB"); flbb1.sendRunCmd(); } if (sampling(Instruments.Radiometer)) { radiometerSB.Clear(); radiometerSB.Append("NaN\tNaN\tNaN\tNaN"); EngrLogger.writeToColumns("Sci Logger sending sample Radiometer"); radiometer1.sendPollSample(); } sampleStartTimeStamp.Clear(); sampleStartTimeStamp.Append(DateTime.Now.ToString("yyyy-MMM-ddTHH:mm:ssZ")); } else { EngrLogger.Comment("Tried to startNewSampleCycle in wrong state"); } return; } public static void endSampleCycle() //TODO P1 Check if writeSample does exactly what this does only more correctly { EngrLogger.writeToColumns("Ending SciLogger Sample Cycle"); if (instrumentsToSample > 0) { EngrLogger.writeToColumns("SciLogger endSampleCycle is writing samples"); writeSample(); } instrumentsToSample = Instruments.None; instrumentsPosted = Instruments.None; waitForCTDThenGetPH = false; waitForCTDThenGetNO3 = false; } /// /// Write key science data to file. /// public static void writeSample() { sbTemp.Clear(); sbTemp.Append(startStateSB).Append(Literals.tabChar); sbTemp.Append(sampleStartTimeStamp).Append(Literals.tabChar); sbTemp.Append(ctdSB).Append(Literals.tabChar); sbTemp.Append(optodeSB).Append(Literals.tabChar); sbTemp.Append(phSB).Append(Literals.tabChar); sbTemp.Append(no3SB).Append(Literals.tabChar); sbTemp.Append(flbbSB).Append(Literals.tabChar); sbTemp.Append(radiometerSB); sbTemp.Append(Literals.crlfString); msgFile.Write(Encoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); msgFile.Flush(); //This is here to check the optode temperature time response EngrLogger.Comment("Sending 2nd Optode.doSample to test time response"); optode1.doSample(); resetSampleStrings(); //Write out to Engrlogger sbTemp2.Clear(); sbTemp2.Append(sbTemp); sbTemp.Clear(); sbTemp.Append("Wrote to msg file: "); sbTemp.Append(sbTemp2); EngrLogger.writeToColumns(sbTemp.Replace(Literals.tabChar, Literals.commaChar)); //TODO P2 Eventually need to do something when missedXXX exceeds some value //Really need to do this inside a well executed commmand processor //object instantiated in each instrument if (sampling(Instruments.Ctd) && !posted(Instruments.Ctd)) { missedCTD++; sbTemp.Clear(); sbTemp.Append("[ERROR] Missed CTD message "); sbTemp.Append(missedCTD); EngrLogger.writeToColumns(sbTemp); } if (sampling(Instruments.Optode) && !posted(Instruments.Optode)) { missedOptode++; sbTemp.Clear(); sbTemp.Append("[ERROR] Missed Optode message "); sbTemp.Append(missedOptode); EngrLogger.writeToColumns(5, sbTemp); } if (sampling(Instruments.Ph) && !posted(Instruments.Ph)) { missedPH++; sbTemp.Clear(); sbTemp.Append("[ERROR] Missed PH message "); sbTemp.Append(missedPH); EngrLogger.writeToColumns(5, sbTemp); } if (sampling(Instruments.NO3) && !posted(Instruments.NO3)) { missedNO3++; sbTemp.Clear(); sbTemp.Append("[ERROR] Missed NO3 message "); sbTemp.Append(missedNO3); EngrLogger.writeToColumns(5, sbTemp); } if (sampling(Instruments.Flbb) && !posted(Instruments.Flbb)) { missedFLBB++; sbTemp.Clear(); sbTemp.Append("[ERROR] Missed FLBB message "); sbTemp.Append(missedFLBB); EngrLogger.writeToColumns(5, sbTemp); } if (sampling(Instruments.Radiometer) && !posted(Instruments.Radiometer)) { missedOCR++; sbTemp.Clear(); sbTemp.Append("[ERROR] Missed OCR message "); sbTemp.Append(missedOCR); EngrLogger.writeToColumns(5, sbTemp); } instrumentsToSample = Instruments.None; instrumentsPosted = Instruments.None; } private static float postedCTDPressure = float.MaxValue; private static float postedCTDTemperature = float.MaxValue; private static float postedCTDSalinity = float.MaxValue; private static DateTime postedCTDTimestamp = new DateTime(2000, 1, 1); public static void setCTDValues(CTD1.PTSRecord ptsRecord) { postedCTDPressure = ptsRecord.pressure; postedCTDTemperature = ptsRecord.temperature; postedCTDSalinity = ptsRecord.salinity; postedCTDTimestamp = ptsRecord.timeStamp; } private static byte[] returnBytes = new byte[32]; public static void postSample(Instruments instrumentNumber, byte[] sampleBytes) { switch (instrumentNumber) { case Instruments.Ctd: #region format CTD message if (waitForCTDThenGetNO3) { EngrLogger.writeToColumns("SciLogger: Setting get NO3 true"); waitForCTDThenGetNO3 = false; } if (waitForCTDThenGetPH) { EngrLogger.writeToColumns("SciLogger: Setting get PH true"); waitForCTDThenGetPH = false; } if (sampling(Instruments.Ph) || sampling(Instruments.NO3)) { EngrLogger.writeToColumns("SciLogger: Setting MSC CTD values"); msc1.SetCTDValues(Utils.dateTimeToPosixTime(postedCTDTimestamp), postedCTDTemperature, postedCTDSalinity, postedCTDPressure); EngrLogger.writeToColumns("SciLogger: Starting PHandNO3 sequence"); msc1.startT1T2SampleSequence(); msc1.SequenceT1T2Samples(); } ctdSB.Clear(); ctdSB.Append(SafeEncoding.GetChars(sampleBytes)); instrumentsPosted |= Instruments.Ctd; break; #endregion case Instruments.Flbb: #region format FLBB message flbbSB.Clear(); sbTemp.Clear(); sbTemp.Append(SafeEncoding.GetChars(sampleBytes)); //Debug.Print("FLBB post: " + sbTemp.ToString()); //Get FL signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 3, ref returnBytes); flbbSB.Append(SafeEncoding.GetChars(returnBytes)); instrumentsPosted |= Instruments.Flbb; } catch { EngrLogger.writeToColumns("[ERROR] parsing flbb Topt"); } flbbSB.Append(Literals.tabChar); //Get BB signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 5, ref returnBytes); flbbSB.Append(SafeEncoding.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("[ERROR] parsing flbb TCPhase"); } flbbSB.Append(Literals.tabChar); //Get Temp signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 6, ref returnBytes); flbbSB.Append(SafeEncoding.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("[ERROR] parsing flbb TCPhase"); } //Debug.Print("new flbbSB: " + flbbSB.ToString()); break; #endregion case Instruments.NO3: #region format NO3 message no3SB.Clear(); no3SB.Append(SafeEncoding.GetChars(sampleBytes)); instrumentsPosted |= Instruments.NO3; break; #endregion case Instruments.Radiometer: #region format OCR message radiometerSB.Clear(); sbTemp.Clear(); sbTemp.Append(SafeEncoding.GetChars(sampleBytes)); //Debug.Print("ocr post: " + sbTemp.ToString()); //Get CH1 signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 9, 1, ref returnBytes); radiometerSB.Append(SafeEncoding.GetChars(returnBytes)); instrumentsPosted |= Instruments.Radiometer; } catch { EngrLogger.writeToColumns("[ERROR] parsing ocr Ch1"); } radiometerSB.Append(Literals.tabChar); //Get CH2 signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 9, 2, ref returnBytes); radiometerSB.Append(SafeEncoding.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("[ERROR] parsing ocr Ch2"); } radiometerSB.Append(Literals.tabChar); //Get CH3 signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 9, 3, ref returnBytes); radiometerSB.Append(SafeEncoding.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("[ERROR] parsing ocr Ch3"); } radiometerSB.Append(Literals.tabChar); //Get CH4 signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 9, 4, ref returnBytes); radiometerSB.Append(SafeEncoding.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("[ERROR] parsing ocr Ch4"); } //Debug.Print("new ocrSB: " + ocrSB.ToString()); break; #endregion case Instruments.Optode: #region format Optode message optodeSB.Clear(); sbTemp.Clear(); sbTemp.Append(SafeEncoding.GetChars(sampleBytes)); //Debug.Print("Optode post: " + sbTemp.ToString()); //Get Topt try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 3, ref returnBytes); optodeSB.Append(SafeEncoding.GetChars(returnBytes)); instrumentsPosted |= Instruments.Optode; } catch { EngrLogger.writeToColumns("[ERROR] parsing optode Topt"); } optodeSB.Append(Literals.tabChar); //Get TCPhase try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 5, ref returnBytes); optodeSB.Append(SafeEncoding.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("[ERROR] parsing optode TCPhase"); } optodeSB.Append(Literals.tabChar); //Get C2RPhase try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 7, ref returnBytes); optodeSB.Append(SafeEncoding.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("[ERROR] parsing optode C2RPhase"); } //Debug.Print("new optodeSB: " + optodeSB.ToString()); break; #endregion case Instruments.Ph: #region format pH message phSB.Clear(); phSB.Append(SafeEncoding.GetChars(sampleBytes)); instrumentsPosted |= Instruments.Ph; break; #endregion default: throw new Exception("Unknown sample type sent"); } //Check to see if everything that was supposed to post posted then write the sample if (instrumentsToSample == instrumentsPosted) { EngrLogger.writeToColumns("Everything posted, writing sample"); writeSample(); } } private static TimeSpan isDoneStartTime = new TimeSpan(); private static TimeSpan isDoneWaitSpan = new TimeSpan(0, 1, 0); //This should be set to the maximum time required to sample both pH and NO3 plus some margin private static bool firstCheck = true; public static int isPHandNO3Done() { if (firstCheck) { isDoneStartTime = Microsoft.SPOT.Hardware.Utility.GetMachineTime(); firstCheck = false; EngrLogger.writeToColumns("SciLogger isPHandNO3Done: first check"); } if (posted(Instruments.Ph) && posted(Instruments.NO3)) { firstCheck = true; EngrLogger.writeToColumns("SciLogger isPHandN03Done: got PH and NO3"); return 1; } if ((Microsoft.SPOT.Hardware.Utility.GetMachineTime() - isDoneStartTime) > isDoneWaitSpan) { //TODO P2 probably need an error handler here EngrLogger.writeToColumns("SciLogger isPHandNO3Done: timed out"); firstCheck = true; return -1; } return 0; } public static void writeMsgComment(StringBuilder inComment) { if (!msgFileOpen) return; sbTemp.Clear(); sbTemp.Append("$ "); sbTemp.Append(inComment); sbTemp.Append("\r\n"); msgFile.Write(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); msgFile.Flush(); } public static void writeToLog(StringBuilder inLine) { if (!logFileOpen) return; sbTemp.Clear(); sbTemp.Append(inLine); sbTemp.Append("\r\n"); logFile.Write(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); logFile.Flush(); } public static void printLastIsusDuraFiles() { var reopenDuraFile = duraFileOpen; var reopenIsusFile = isusFileOpen; closeDuraFile(); closeIsusFile(); var duraFileStream = new StreamReader(duraFileName.ToString()); Debug.Print("DURA FILE:"); string fileLine; while ((fileLine = duraFileStream.ReadLine()) != null) Debug.Print(fileLine); Debug.Print("ISUS FILE:"); var isusFileStream = new StreamReader(isusFileName.ToString()); while ((fileLine = isusFileStream.ReadLine()) != null) Debug.Print(fileLine); } /// /// Print the current science file to the debugger /// public static void printLastMsgFile() { StreamReader sciFileStream; string fileLine; bool reopenFile; if (msgFileOpen) { closeMsgFile(); reopenFile = true; } else reopenFile = false; sciFileStream = new StreamReader(msgFileName.ToString()); while ((fileLine = sciFileStream.ReadLine()) != null) Debug.Print(fileLine); sciFileStream.Close(); if (reopenFile) msgFile = new FileStream(msgFileName.ToString(), FileMode.OpenOrCreate, FileAccess.Write); } /// /// Enqueue's a filename in the FIFO queue that the rudics thread watches and attempts to upload from. /// /// Name Must be full path, and valid, file should not be open. public static void EnqueueFileForXfer(string name) { _qRecord.byteArray = Encoding.UTF8.GetBytes(name); _qRecord.Update(); // Add filename to transfer buffer _fileQ.Enqueue(_qRecord); sbTemp.Clear().Append("Enqueueing File for Transfer: ").Append(name); EngrLogger.writeToColumns(sbTemp); } /// /// Returns the last file closed as a QRecord, with closing time in the date field. /// /// Thrown when attempting to dequeue from empty queue public static QRecord DequeueFileName { get { return _fileQ.Dequeue(); } } private static bool numberOfCharsInByteArray(int n, char testChar, byte[] byteArray) { int numChars = 0; int index = -1; for (int i = 0; i < n; i++) { index = Array.IndexOf(byteArray, (byte)testChar, index + 1); if (index < 0) break; else numChars++; } if (numChars >= n) return true; else return false; } public static void updateLatLon(gpsUBlox.GPSPosition position) { double lat = double.NaN; double lon = double.NaN; gpsPositionSB.Clear(); gpsPositionSB.Append("# GPS fix obtained in (seconds) \t"); gpsPositionSB.Append(position.gpsTTFF.ToString()); gpsPositionSB.Append(Literals.crlfString); gpsPositionSB.Append("#Fix: \tLon\tLat\tmm/dd/yyyy\tHH:mm:sssZ\tnumSats\tnmeaLon\t\tnmeaLat\t\tgpsTimeOfFix"); gpsPositionSB.Append(Literals.crlfString); gpsPositionSB.Append("Fix:"); gpsPositionSB.Append(Literals.tabChar); lon = convertLonToDecDeg(position.lonSB.ToString()); gpsPositionSB.Append(lon.ToString("f8")); gpsPositionSB.Append(Literals.tabChar); lat = convertLatToDecDeg(position.latSB.ToString()); gpsPositionSB.Append(lat.ToString("f8")); gpsPositionSB.Append(Literals.tabChar); gpsPositionSB.Append(position.cpfDateTimeOfFix.ToString("MM/dd/yyyy\tHH:mm:ss")); gpsPositionSB.Append(Literals.tabChar); gpsPositionSB.Append(position.numSats.ToString()); gpsPositionSB.Append(Literals.tabChar); gpsPositionSB.Append(position.lonSB); gpsPositionSB.Append(Literals.tabChar); gpsPositionSB.Append(position.latSB); gpsPositionSB.Append(Literals.tabChar); gpsPositionSB.Append(position.gpsTimeOfFix); } private static double convertLatToDecDeg(string sLat) { int decPtIndex = 0; double lat = double.NaN; double deg; double min; //TODO P1 need to make sure pubx always has 2 digits of minutes try { decPtIndex = sLat.IndexOf('.'); min = double.Parse(sLat.Substring(decPtIndex - 2, sLat.Length - decPtIndex)); deg = double.Parse(sLat.Substring(0, decPtIndex - 2)); lat = deg + min / 60.0; if (sLat.Substring(sLat.Length - 1, 1) == "S") lat = -1 * lat; } catch { lat = double.NaN; } return lat; } private static double convertLonToDecDeg(string sLon) { int decPtIndex = 0; double lon = double.NaN; double deg; double min; //TODO P1 need to make sure pubx always has 2 digits of minutes try { decPtIndex = sLon.IndexOf('.'); min = double.Parse(sLon.Substring(decPtIndex - 2, sLon.Length - decPtIndex)); deg = double.Parse(sLon.Substring(0, decPtIndex - 2)); lon = deg + min / 60.0; if (sLon.Substring(sLon.Length - 1, 1) == "W") lon = -1 * lon; } catch { lon = double.NaN; } return lon; } /// /// Write a line to the .isus file, assumably some configuration information /// /// public static void writeToIsusFile(byte[] recordByteArray, int length=0) { if (!isusFileOpen) return; //Find first null if (length == 0) length = Array.IndexOf(recordByteArray, 0); if (length <= 0) return; var offset = (recordByteArray[0] == 0 ? 1 : 0); if (offset == 1) length--; isusFile.Write(recordByteArray, offset, length); } /// /// Write a line to the .dura file, assumably some configuration information /// /// public static void writeToDuraFile(byte[] recordByteArray, int length=0) { if (!duraFileOpen) return; if (length == 0) length = Array.IndexOf(recordByteArray, 0); if (length <= 0) return; var offset = (recordByteArray[0] == 0) ? 1 : 0; if (offset == 1) length--; duraFile.Write(recordByteArray, offset, length); } public static void WriteErrorCount(uint errorCount, MSC.SampleMode mode) { switch (mode) { case MSC.SampleMode.DuraFET: if (!duraFileOpen) return; if (errorCount == 0) sbTemp.Clear().Append("# No error records to log.\r\n"); else sbTemp.Clear().Append("Count of DURA errors: ").Append(errorCount).Append(Literals.crlfString); duraFile.Write(SafeEncoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); return; case MSC.SampleMode.ISUS: if (!isusFileOpen) return; if (errorCount == 0) sbTemp.Clear().Append("# No error records to log.\r\n"); else sbTemp.Clear().Append("Count of ISUS errors: ").Append(errorCount).Append(Literals.crlfString); isusFile.Write(SafeEncoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); return; } } public static void writeErrorRecord(byte[] bytes, MSC.SampleMode mode) { switch (mode) { case MSC.SampleMode.DuraFET: if (!duraFileOpen) return; sbTemp.Clear().Append(((UTF8Encoding) SafeEncoding.UTF8).GetChars(bytes)); duraFile.Write(SafeEncoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); return; case MSC.SampleMode.ISUS: if (!isusFileOpen) return; sbTemp.Clear().Append(((UTF8Encoding) SafeEncoding.UTF8).GetChars(bytes)); isusFile.Write(SafeEncoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); return; } } public static void clearFileQueue() { if (_fileQ == null) return; _fileQ.Clear(); } } }