using System; using Microsoft.SPOT; using System.Text; using System.Threading; namespace abstractState { /*StateBase - from which all other states inherit * Includes sbTemp and SV for inherited states to use */ abstract public class StateBase { //ABSTRACT METHODS public abstract int doStateEntryActions(); public abstract int doStateActions(); public abstract Program.CPFStates checkEvents(); public abstract int doExitActions(bool timedOut); public abstract Program.CPFStates doTimeoutAction(); public abstract int handleErrors(int returnVal, int currentAction); //INHERITED METHODS protected static int basicStateEntry() { EngrLogger.quickWTC("State Entry"); Elmo.stopMotor(); //EngrLogger.writeToColumns(sbStateEntry); SV.RunPVPID = false; SV.CheckStuckPressure = true; ActionReturn = 0; return (1); } protected static int basicStateExit(bool timedOut) { //timedOut exit actions if (timedOut) { EngrLogger.quickWTC("State timed out"); } //normal state exit actions else { EngrLogger.quickWTC("Normal state exit"); SV.CheckStuckPressure = true; Elmo.stopMotor(); } return (1); } //Evaluates return code of a given action, and if successful incriments currentAtion. //If action is incomplete, or fails, returns initial value of currentAction. //Errors are to be handled higher up because this is a common and inherited method that is context agnostic. protected static int checkReturn(int returnVal, int currentAction) { if (returnVal > 0) //action has completed sucessfully, or is still in process { return (returnVal); } else if (returnVal == 0) { return (currentAction); } if (returnVal == -1) //error in action { //Program.cpfState[Program.currentStateIndex].handleReturnedErrors(status, currentAction); //return ((int)currentAction); //return currentAction (dont incriment) //handleReturnedErrors should simply return the next action, and do fixing inside return (Program.cpfState[(int)Program.CurrentState].handleErrors(returnVal, currentAction)); } else { return (-1); //error, should never get here } } //COMMON PROPERTIES //This is the execution constraint time out that the state must complete in public int ECTimeout { get; protected set; } //TODO should this be in SV? Who needs access? public static int ProfileNum { get; protected set; } public static int ActionReturn { get; protected set; } //flag indicates whether data is to be dumped off SBE public static bool DumpCPData { get; protected set; } public readonly StringBuilder stateName = new StringBuilder(32); protected static StringBuilder sbTemp = new StringBuilder(128); } //InitializeMission - jogs bellows, inits SBE41, optode, FLBB public class InitMission : StateBase { public InitMission(string name) { stateName.Clear(); stateName.Append(name); ECTimeout = 50 * 1000; //50 seconds } private enum Action : int { //list sequential actions here checkBellowsMovement = 1, moveBellowsInsideLimits, sendSBE41_DS, sendOptodeGetAll, initFLBB, lastAction, size = lastAction } private Action currentAction; public override int doStateEntryActions() { basicStateEntry(); //state-specific entry actions below currentAction = (Action)1; return (1); } // IF/ELSE blocks are preserved in switch statement and not factored into inherited methods because 1)actions are not always sequential, and 2) how we handle errors may be context dependant, so errors should be handled at point where currentAction is decided/switched public override int doStateActions() { switch (currentAction) { #region Sequential state actions case (Action.checkBellowsMovement): //do action, and store its status. ActionReturn = checkBellowsMovement(); break; case (Action.moveBellowsInsideLimits): ActionReturn = moveBellowsInsideLimits(); break; case (Action.sendSBE41_DS): ActionReturn = sendSBE41_DS(); break; case (Action.sendOptodeGetAll): ActionReturn = sendOptodeGetAll(); break; case (Action.initFLBB): ActionReturn = initFLBB(); break; case (Action.lastAction): break; #endregion } //examine the return of the action, this will either 1) adjust currentAction, 2) keep it the same (action isn't complete), or 3) call handleErrors method currentAction = (Action)checkReturn(ActionReturn, (int)currentAction); return (1); } public override Program.CPFStates checkEvents() { if (currentAction == Action.lastAction) return (Program.CPFStates.initProfile); else return (Program.CPFStates.initMission); } //TODO add exit actions public override int doExitActions(bool timedOut) { if (timedOut) { basicStateExit(true); } else { basicStateExit(false); } return (1); } public override Program.CPFStates doTimeoutAction() { //move to next state return (Program.CPFStates.initProfile); } //InitMission error handler public override int handleErrors(int returnVal, int currentAction) { throw new NotImplementedException(); } //=====State-specific methods below===== //methods should return an int in the range [-1:Action.lastAction]. // -1: Error // 0: Action not complete // >0: an integar cast of the next action enumeration //TODO build logic that actually checks if the bellows is moving! //TODO should moveBellowsInsideLimits be a method that exists within Elmo class? //TODO below callCount is just for initial testing, impliment fully/cleanly private static int callCount = 0; private static int checkBellowsMovement() { EngrLogger.quickWTC("Checking bellows movement."); if (callCount > 3) { callCount = 0; //return the int of the next action return ((int)Action.moveBellowsInsideLimits); } else { callCount++; return (0); } } private static int moveBellowsInsideLimits() { //Just in case the bellows was outside the limits, put it inside the limits //if (BuoyancyControl.bellowsPosition > (BuoyancyControl.bellowsUpperLimit - 2.0)) //{ // sbTemp.Clear(); // sbTemp.Append("Retracting bellows inside limits"); // EngrLogger.writeToColumns(sbTemp); // if (!Elmo.retracting) // Elmo.retractBellows(10000); // return (0); //action not complete, return 0 //} //else if (BuoyancyControl.bellowsPosition < (BuoyancyControl.bellowsLowerLimit + 2.0)) //{ // sbTemp.Clear(); // sbTemp.Append("Extending bellows inside limits"); // EngrLogger.writeToColumns(sbTemp); // if (!Elmo.extending) // Elmo.extendBellows(10000); // return (0); //action not complete, return 0 //} if (callCount > 3) { callCount = 0; //return the int of the next action return ((int)Action.moveBellowsInsideLimits); } else { Elmo.stopMotor(); EngrLogger.quickWTC("Bellows inside limits"); //return int of the next action to be done return ((int)Action.sendSBE41_DS); } } private static int sendSBE41_DS() { EngrLogger.quickWTC("Sending SBE41 ds command."); SBE41.sendDS(); //return int of the next action return ((int)Action.sendOptodeGetAll); } private static int sendOptodeGetAll() { EngrLogger.quickWTC("Sending Optode GetALL command."); Optode.getAll(); return ((int)Action.initFLBB); } private static int initFLBB() { EngrLogger.quickWTC("Initializing FLBB."); FLBB.init(); //go to int of next action return ((int)Action.lastAction); } } //InitializeProfile - sets execuition timeout constraint public class InitProfile : StateBase { public InitProfile(string name) { stateName.Clear(); stateName.Append(name); ECTimeout = 10 * 1000; //5 seconds } private enum Action : int { incrementProfile = 1, lastAction, size = lastAction } private Action currentAction; public override int doStateEntryActions() { basicStateEntry(); currentAction = (Action)1; //set/reset this value before float enters/reenters a descent mode SV.PressureTableNum = 0; return (1); } public override int doStateActions() { switch (currentAction) { #region Sequential state actions case (Action.incrementProfile): //do action, and store its status. ActionReturn = incrementProfileNum(); break; case (Action.lastAction): break; #endregion } //examine the return of the action, continue currentAction = (Action)checkReturn(ActionReturn, (int)currentAction); return (1); } public override Program.CPFStates checkEvents() { if (currentAction == Action.lastAction) return (Program.CPFStates.surfaceOpsSetBellows); else return (Program.CPFStates.initProfile); } //TODO add exit actions public override int doExitActions(bool timedOut) { if (timedOut) { basicStateExit(true); } else { basicStateExit(false); } return (1); } public override Program.CPFStates doTimeoutAction() { return (Program.CPFStates.surfaceOpsSetBellows); } public override int handleErrors(int returnVal, int currentAction) { throw new NotImplementedException(); } //increment profile number private static int incrementProfileNum() { Program.ProfileNumber++; return ((int)Action.lastAction); } } public class SurfaceOpsSetBellows : StateBase { public SurfaceOpsSetBellows(string name) { stateName.Clear(); stateName.Append(name); ECTimeout = 60 * 1000; } private enum Action : int { setBellows = 1, lastAction, size = lastAction } private Action currentAction; public override int doStateEntryActions() { basicStateEntry(); EngrLogger.quickWTC("Inflating bellows to SOps positino"); currentAction = (Action)1; return (1); } public override int doStateActions() { switch (currentAction) { case (Action.setBellows): ActionReturn = surfaceOpsSetBellows(); break; case (Action.lastAction): break; } currentAction = (Action)checkReturn(ActionReturn, (int)currentAction); return (1); } public override Program.CPFStates checkEvents() { //is bellows at required position? if ((currentAction == Action.lastAction) && (Program.ProfileNumber == 1)) return (Program.CPFStates.preMissionDelay); else if ((currentAction == Action.lastAction) && (Program.ProfileNumber > 1)) return (Program.CPFStates.dumpCPData); else return (Program.CPFStates.surfaceOpsSetBellows); } public override int doExitActions(bool timedOut) { if (timedOut) { basicStateExit(true); } else { basicStateExit(false); } return (1); } public override Program.CPFStates doTimeoutAction() { return (Program.CPFStates.surfaceOps); } public override int handleErrors(int returnVal, int currentAction) { throw new NotImplementedException(); } private static int surfaceOpsSetBellows() { //if (BuoyancyControl.bellowsPosition > (configFile.SOSetBellowsPosition + 0.5)) //{ // if (!Elmo.retracting) // { // Elmo.retractBellows(configFile.SOSetBellowsJV); // } // return (0); //} //else if (BuoyancyControl.bellowsPosition < (configFile.SOSetBellowsPosition - 0.5)) //{ // if (!Elmo.extending) // { // Elmo.extendBellows(configFile.SOSetBellowsJV); // } // return (0); //} //else EngrLogger.quickWTC("Pretned bellows inside limits"); return ((int)Action.lastAction); } } public class SurfaceOps : StateBase { public SurfaceOps(string name) { stateName.Clear(); stateName.Append(name); ECTimeout = 600 * 1000; //seconds } private enum Action : int { //list sequential actions here sendSBDD2 = 1, readGpsI2C, sendCSQ, loadSBDWT, sendSBDI, lastAction, size = lastAction } private Action currentAction; //TODO check for sucessful return of stateEntry methods public override int doStateEntryActions() { basicStateEntry(); currentAction = (Action)1; //State-specific entry items below Optode.doSample(); FLBB.sendRunCmd(); return (1); } //TODO Add individual timers on each function? //TODO Reset currentAction on last action. public override int doStateActions() { switch (currentAction) { #region SurOps Action Sequence case (Action.sendSBDD2): ActionReturn = sendSBDD2(); break; case (Action.readGpsI2C): ActionReturn = readGPSI2C(); break; case (Action.sendCSQ): ActionReturn = sendCSQ(); break; case (Action.loadSBDWT): ActionReturn = loadSBDWT(); break; case (Action.sendSBDI): ActionReturn = sendSBDI(); break; case (Action.lastAction): //cycle back through sequence currentAction = Action.sendSBDD2; break; default: break; #endregion } currentAction = (Action)checkReturn(ActionReturn, (int)currentAction); return (1); } public override Program.CPFStates checkEvents() { //if we receive the $GO command over serial if (SV.SurfaceOpsGo) { return (Program.CPFStates.ABRetractFast); } //stay in SO if in Recovery Mode. Note that above eval has already happened, //so we're confidant that float hasn't gotten $GO command. else if (SV.SORecoveryMode) { return (Program.CPFStates.surfaceOps); } //if time is synced, and telemtry is done, continue else if ((timeSynced) && (telemetryDone)) { return (Program.CPFStates.ABRetractFast); } //default is to stay in SurfaceOps else { return (Program.CPFStates.surfaceOps); } } public override Program.CPFStates doTimeoutAction() { if (SV.SORecoveryMode) return (Program.CPFStates.surfaceOps); else { return (Program.CPFStates.ABRetractFast); } } public override int doExitActions(bool timedOut) { if (timedOut) { basicStateExit(true); //reset these so next time in SO they can be evaluated timeSynced = false; telemetryDone = false; SV.SurfaceOpsGo = false; } else { basicStateExit(false); } return (1); } public override int handleErrors(int returnVal, int currentAction) { throw new NotImplementedException(); } private static int sendSBDD2() { if (IModem.sendSBDD2() > 0) return ((int)Action.readGpsI2C); else //error return (-1); } private static int readGPSI2C() { if (EVK7GPS.readI2C() > 0) return ((int)Action.sendCSQ); else //error return (-1); } private static int sendCSQ() { if (IModem.sendCSQ() > 0) { return ((int)Action.loadSBDWT); } else { return (-1); } } private static int loadSBDWT() { if (IModem.loadSBDWT() > 0) { return ((int)Action.sendSBDI); } else { return (-1); } } private static int sendSBDI() { if (IModem.sendSBDI() > 0) { return ((int)Action.lastAction); } else { return (-1); } } //SurfaceOps is the only place these are used; don't need to be in SV. private static bool timeSynced = false; private static bool telemetryDone = false; } public class PreMissionDelay : StateBase { private enum Action : int { wait = 1, lastAction, size = lastAction } private Action currentAction; public PreMissionDelay(string name) { stateName.Clear(); stateName.Append(name); ECTimeout = 15 * 60 * 1000; //15 minutes } public override int doStateEntryActions() { basicStateEntry(); currentAction = (Action)1; return (1); } public override int doStateActions() { switch (currentAction) { case (Action.wait): ActionReturn = wait(); break; case (Action.lastAction): //cycle back to wait, even tho we shouldn't get here currentAction = Action.wait; break; } currentAction = (Action)checkReturn(ActionReturn, (int)currentAction); return (1); } public override Program.CPFStates checkEvents() { //TODO ensure that if $GO is received here, that flag is not reset at entry to surfaceOps, //as intended behavior would be leave SO once actions are complete and continue to ABSetFast if (SV.SurfaceOpsGo) return (Program.CPFStates.surfaceOps); else return (Program.CPFStates.preMissionDelay); } public override int doExitActions(bool timedOut) { if (timedOut) { basicStateExit(true); } else { basicStateExit(false); } return (1); } public override Program.CPFStates doTimeoutAction() { return (Program.CPFStates.surfaceOps); } public override int handleErrors(int returnVal, int currentAction) { throw new NotImplementedException(); } private static int wait() { return ((int)Action.wait); } } public class DumpCPData : StateBase { public DumpCPData(string name) { stateName.Clear(); stateName.Append(name); ECTimeout = 15 * 60 * 1000; //15 minutes } private enum Action : int { sendCRLF = 1, sendDD, restartCommandTimer, lastAction, size = lastAction } private Action currentAction; public override int doStateEntryActions() { basicStateEntry(); currentAction = (Action)1; //disable CheckStuckPressure to prevent blocking of upload; SV.CheckStuckPressure = false; return (1); } public override int doStateActions() { switch (currentAction) { case (Action.sendCRLF): ActionReturn = sendCRLFWait(); break; case (Action.sendDD): ActionReturn = sendDDWait(); break; case (Action.restartCommandTimer): ActionReturn = restartCommandTimer(); break; case (Action.lastAction): break; } currentAction = (Action)checkReturn(ActionReturn, (int)currentAction); return (1); } public override Program.CPFStates checkEvents() { if (currentAction == Action.lastAction) { return (Program.CPFStates.surfaceOps); } else { return (Program.CPFStates.dumpCPData); } } public override int doExitActions(bool timedOut) { if (timedOut) { basicStateExit(true); dumpCPDataatempts = 0; } else { basicStateExit(false); dumpCPDataatempts = 0; } dumpCPDataatempts = 0; return (1); } public override Program.CPFStates doTimeoutAction() { return (Program.CPFStates.surfaceOps); } public override int handleErrors(int returnVal, int currentAction) { throw new NotImplementedException(); } private static int dumpCPDataatempts = 0; private static int sendCRLFWait() { if (SBE41.waitForResponse(SBE41.commands.crlf)) { sbTemp.Clear(); sbTemp.Append("Got CR/LF response, sending dd"); EngrLogger.writeToColumns(sbTemp); return ((int)Action.sendDD); } else { sbTemp.Clear(); sbTemp.Append("Didn't get CR/LF response, exiting dumpCPData"); EngrLogger.writeToColumns(sbTemp); return ((int)Action.restartCommandTimer); } } private static int sendDDWait() { if (SBE41.waitForResponse(SBE41.commands.dd)) { sbTemp.Clear(); sbTemp.Append("God dd response; Upload sucessful."); EngrLogger.writeToColumns(sbTemp); return ((int)Action.restartCommandTimer); } else { sbTemp.Clear(); sbTemp.Append("Didn't get dd response, restarting command timer"); EngrLogger.writeToColumns(sbTemp); return ((int)Action.restartCommandTimer); } } private static int restartCommandTimer() { SBE41.sendCRLF(); //don't need to wait for response here (According to GM legacy code) Thread.Sleep(1000); SBE41.stopSBE41Timer = false; SBE41.restartTimer(500, configFile.SBE41SamplePeriod); return ((int)Action.lastAction); } } public class ABRetractFast : StateBase { public ABRetractFast(string name) { stateName.Clear(); stateName.Append(name); ECTimeout = 15 * 1000; } private enum Action : int { setBellows = 1, lastAction, size = lastAction } private Action currentAction; public override int doStateEntryActions() { basicStateEntry(); currentAction = (Action)1; return (1); } //TODO convert to sequence like other examples public override int doStateActions() { switch (currentAction) { case (Action.setBellows): //TODO make so only goes to lastAction when it has completed it's movement ActionReturn = setBellowsABFast(); break; case (Action.lastAction): //do nothing here break; default: break; } currentAction = (Action)checkReturn(ActionReturn, (int)currentAction); return (1); } public override Program.CPFStates checkEvents() { //if bellows has arrived at ABSetFast position //if ((BuoyancyControl.bellowsPosition <= configFile.ABSetBellowsPosition + 0.5) && (BuoyancyControl.bellowsPosition >= configFile.ABSetBellowsPosition - 0.5)) if (currentAction == Action.lastAction) { return (Program.CPFStates.ABRetractSlow); } //stay in absetFast else { return (Program.CPFStates.ABRetractFast); } } public override int doExitActions(bool timedOut) { if (timedOut) { basicStateExit(true); } else { basicStateExit(false); }; return (1); } public override Program.CPFStates doTimeoutAction() { return (Program.CPFStates.ABRetractSlow); } public override int handleErrors(int returnVal, int currentAction) { throw new NotImplementedException(); } private static int setBellowsABFast() { ////TODO impliment with new method in Elmo class //if (BuoyancyControl.bellowsPosition > (configFile.ABSetBellowsPosition + 0.5)) //{ // if (!Elmo.retracting) // { // Elmo.retractBellows(configFile.ABSetBellowsJV); // } // return (0); //not finished //} //else if (BuoyancyControl.bellowsPosition < (configFile.ABSetBellowsPosition - 0.5)) //{ // if (!Elmo.extending) // { // Elmo.extendBellows(configFile.ABSetBellowsJV); // } // return (0); //not finished //} //else EngrLogger.quickWTC("Pretned bellows has been set to fast position"); return ((int)Action.lastAction); } } public class ABRetractSlow : StateBase { public ABRetractSlow(string name) { stateName.Clear(); stateName.Append(name); ECTimeout = 2 * 60 * 1000; //2 min } private enum Action : int { setBellows = 1, lastAction, size = lastAction } private Action currentAction; public override int doStateEntryActions() { basicStateEntry(); currentAction = (Action)1; return (1); } public override int doStateActions() { switch (currentAction) { case (Action.setBellows): ActionReturn = retractBellowsSlow(); break; case (Action.lastAction): //do nothing here break; default: break; } currentAction = (Action)checkReturn(ActionReturn, (int)currentAction); return (1); } public override Program.CPFStates checkEvents() { //Increment threshold counter on successive tests otherwise reset counter to 0 if (SV.Pressure >= configFile.ABPressureThreshold) ABRetractThresholdCount = ABRetractThresholdCount + 1; else ABRetractThresholdCount = 0; //if 3 sucessive counts below threshold have been recorded, transistion to descend if (ABRetractThresholdCount >= 3) { return (Program.CPFStates.descend); } //otherwise stay else { return (Program.CPFStates.ABRetractSlow); } } public override int doExitActions(bool timedOut) { if (timedOut) { basicStateExit(true); //reset privates ABRetractThresholdCount = 0; } else { basicStateExit(false); ABRetractThresholdCount = 0; } return (1); } public override Program.CPFStates doTimeoutAction() { return (Program.CPFStates.descend); } public override int handleErrors(int returnVal, int currentAction) { throw new NotImplementedException(); } private static int retractBellowsSlow() { if (!Elmo.retracting) { Elmo.retractBellows(configFile.ABRetractJV); } return ((int)Action.setBellows); } private static int ABRetractThresholdCount = 0; } //Utilities - common methods that different states/classes might want access to public class Utilities { } }