public async void StartAsync() { _State = SequencerState.Running; try { while (CurrentNode != null && CurrentNode != default(ISequenceNode)) { // Execute the Task and wait for it to return await CurrentNode.ExecuteAsync(); // If the node finished successfully if (CurrentNode.Completed || (CurrentNode.CanContinueOnError && !CurrentNode.RepeatOnError)) { var target = CurrentNode.Index + 1; CurrentNode = Nodes.Where(x => x.Index == target) .FirstOrDefault(); } else if (!CurrentNode.Completed && !CurrentNode.RepeatOnError && !CurrentNode.CanContinueOnError) { CurrentNode = null; } } } catch (Exception ex) { //TODO: Add Logging } _State = SequencerState.Stopped; }
public async void StopAsync() { try { await CurrentMode.StopAsync(); _State = SequencerState.Stopped; } catch (Exception ex) { // Enable logging } }
public async void PauseAsync() { try { await CurrentNode.PauseAsync(); _State = SequencerState.Paused; } catch (Exception ex) { // TODO: Add Logging } }
public Sequencer(IEnumerable <ISequenceNode> nodes) { // Order the nodes by index _Nodes = nodes.OrderBy(x => x.Index); _State = SequencerState.Stopped; // Setup the first node CurrentNode = Nodes.First(); // Initialize it so we're ready to go CurrentNode.Init() }
IEnumerator RunSequencer() { sequencerState = SequencerState.Playing; while (sequencerState != SequencerState.Stopped) { currentTime = Time.time; if (currentTime > targetTime - 0.1f) { Sixteenth(); } yield return(new WaitForSeconds(0.00001f)); } }
public void UnlockSequencer() { //upon unlocking sequencer should initiate state change to lastState to trigger any auto-starting sequences //there must be at least one state (by definition) if (currentState == null) { currentState = states[0]; } //feels a bit hacky, but it should work; //it will unlock all the sequences as a side effect. OnStateChange(currentState, currentState); isLocked = false; }
public override void OnAwake() { sequences = new List <Sequence>(); sequences.Clear(); states = new List <SequencerState>(); states.Clear(); var s = new SequencerState(); s.stateName = "Default"; states.Add(s); currentState = states[0]; }
public bool TryParseState(string s, out SequencerState st) { if (s == "" || !s.Contains(":")) { Logger.Log("TryParseState, invalid format s=" + s, Logger.Level.Debug); st = null; return(false); } var chunks = s.Split(':'); if (chunks.Count() < 2) { st = null; return(false); } st = new SequencerState(chunks[0]); st.stateName = chunks[1]; return(true); }
/// <summary>Build the dependencies tree.</summary> /// <remarks>TODO : This is buggy. We should rely on RevisionId.</remarks> internal void BuildDependencies(bool traceDependencies = false) { SequencerState state = new SequencerState(traceDependencies); Packaging.Update[] updates = _package.Updates; foreach (Packaging.Update candidate in updates) { Guid updateId = candidate.UpdateId; if (null == candidate.Prerequisites) { state.ApplyUpdate(updateId, Guid.Empty); continue; } foreach (object prerequisite in candidate.Prerequisites) { if (prerequisite is Packaging.UpdateIdReference) { Guid requiredUpdateId = ((Packaging.UpdateIdReference)prerequisite).Id; state.ConditionalyApplyUpdate(updateId, requiredUpdateId); continue; } if (prerequisite is Packaging.UpdateIdReferenceCollection) { foreach (Packaging.UpdateIdReference reference in ((Packaging.UpdateIdReferenceCollection)prerequisite).UpdateIds) { Guid requiredUpdateId = reference.Id; if (state.ConditionalyApplyUpdate(updateId, requiredUpdateId)) { break; } } continue; } throw new ApplicationException("BUG"); } } state.FinalizeState(); return; }
public void OnStateChange(SequencerState oldState, SequencerState newState) { Logger.Log("[ModuleSequencer] OnStateChange from " + oldState.stateName + " to " + newState.stateName + " starting.", Logger.Level.Debug); foreach (Sequence sq in sequences) { //first we need to stop/reset all active sequences which startState is not newState if (sq.isActive && sq.startState != null && sq.startState != newState) { sq.Pause(); sq.Reset(); Logger.Log("[ModuleSequencer] OnStateChange stopping sequence " + sq.name, Logger.Level.Debug); } //now we need to unlock previously locked sequences var activeSequences = sequences.FindAll(s => s.isActive); if (activeSequences.Count == 0) { //unlock all sequences as there are none active sequences.ForEach(((Sequence s) => s.isLocked = false)); } if (activeSequences.Count(s => s.endState != s.startState) == 0) { //the only sequences that run are ones that do not change the state, so we can unlock all sequences sequences.ForEach(((Sequence s) => s.isLocked = false)); } //check for sequences in AutoStart mode and start the ones that should trigger when we enter newState if (!sq.isActive && sq.autoStart && sq.startState != null && sq.startState == newState && !sq.isLocked) { sq.Start(currentState); Logger.Log("[ModuleSequencer] OnStateChange starting sequence " + sq.name + " due to AutoStart flag.", Logger.Level.Debug); } } Logger.Log("[ModuleSequencer] OnStateChange from " + oldState.stateName + " to " + newState.stateName + " complete.", Logger.Level.Debug); }
public override void OnStart(StartState state) { //load the current state on first FixedUpdate loadPending = true; if (sequences == null || states == null) { sequences = new List <Sequence>(); sequences.Clear(); states = new List <SequencerState>(); states.Clear(); var s = new SequencerState(); s.stateName = "Default"; states.Add(s); currentState = states[0]; Logger.Log("[Sequencer]: for some reason sequences/states is null in OnStart", Logger.Level.Debug); } }
/// <summary> /// Main heartbeat loop. /// </summary> private void FixedUpdate() { //no need to run for non-robotic crafts or if disabled if (!isEnabled || sequences == null) { return; } //only autosave every second //if (Time.time < lastSavedUT + 0.2f) // return; if (loadPending) { LoadData(); } else if (Time.time > lastSavedUT + 0.2f) { SaveData(); lastSavedUT = Time.time; } //if the sequencer is locked, there is no need to process any sequences. if (isLocked) { return; } var activeSequences = sequences.FindAll(s => s.isActive); if (activeSequences.Count == 0) { //unlock all sequences as there are none active sequences.ForEach(((Sequence s) => s.isLocked = false)); } if (activeSequences.Count(s => s.endState != s.startState) == 0) { //the only sequences that run are ones that do not change the state, so we can unlock all sequences sequences.ForEach(((Sequence s) => s.isLocked = false)); } foreach (Sequence sq in activeSequences) { if (sq.commands == null) { continue; } var affectedServos = new List <IRWrapper.IServo>(); sq.commands.FindAll(s => s.servo != null).ForEach((BasicCommand c) => affectedServos.Add(c.servo)); if (affectedServos.Any()) { sequences.FindAll(s => s.commands.Any(c => affectedServos.Contains(c.servo))).ForEach((Sequence seq) => seq.isLocked = true); //exclude current sequence from Locked List sq.isLocked = false; } //in addition lock all other sequeces that change State if (sq.endState != sq.startState) { sequences.FindAll(s => s.endState != s.startState).ForEach((Sequence seq) => seq.isLocked = true); sq.isLocked = false; } var activeCommands = sq.commands.FindAll(s => s.isActive); var activeCount = activeCommands.Count; foreach (BasicCommand bc in activeCommands) { if (bc.wait) { if (bc.ag != KSPActionGroup.None) { //we should wait until ActionGroup is executed if (HighLogic.LoadedSceneIsFlight) { if (FlightGlobals.ActiveVessel != null) { if (FlightGlobals.ActiveVessel.ActionGroups[bc.ag]) { Logger.Log("[Sequencer] ActionGroup wait finished, AG fired was " + bc.ag.ToString(), Logger.Level.Debug); bc.isFinished = true; bc.isActive = false; activeCount--; } } } else { Logger.Log("[Sequencer] ActionGroup wait auto-finished in Editor", Logger.Level.Debug); bc.isFinished = true; bc.isActive = false; activeCount--; } } else if (bc.agX > -1) { //we should wait until ActionGroupExtended is executed if (HighLogic.LoadedSceneIsFlight && ActionGroupsExtendedAPI.Instance.Installed()) { if (FlightGlobals.ActiveVessel != null) { if (ActionGroupsExtendedAPI.Instance.GetGroupState(FlightGlobals.ActiveVessel, bc.agX)) { Logger.Log("[Sequencer] ActionGroup wait finished, AG fired was " + ActionGroupsExtendedAPI.Instance.GetGroupName(bc.agX), Logger.Level.Debug); bc.isFinished = true; bc.isActive = false; activeCount--; } } } else { Logger.Log("[Sequencer] ActionGroup wait auto-finished in Editor", Logger.Level.Debug); bc.isFinished = true; bc.isActive = false; activeCount--; } } else if (bc.waitTime > 0f) { if (UnityEngine.Time.time >= bc.timeStarted + bc.waitTime) { Logger.Log("[Sequencer] Timed wait finished, waitTime was " + bc.waitTime + "s", Logger.Level.Debug); bc.isFinished = true; bc.isActive = false; activeCount--; } } } else if (Math.Abs(bc.servo.Position - bc.position) <= POSDELTA) { Logger.Log("[Sequencer] Command finished, servo = " + bc.servo.Name + ", pos = " + bc.position, Logger.Level.Debug); bc.isFinished = true; bc.isActive = false; activeCount--; } } //need to calculate if there are any active waiting commands var activeWaitCount = activeCommands.Count(t => t.wait && t.isActive); //if (activeWaitCount == 0) sq.isWaiting = false; if (activeCount <= 0) { //there are no active commands being executed, including Delays if (sq.lastCommandIndex + 1 < sq.commands.Count) { //there are still commands left to execute //need to start from first unfinished command Logger.Log("[Sequencer] Restarting sequence " + sq.name + " from first unfinished command", Logger.Level.Debug); sq.isWaiting = false; sq.Start(currentState); } else { //there are no more commands in the sequence left to execute if (sq.isLooped) { Logger.Log("[Sequencer] Looping sequence " + sq.name, Logger.Level.Debug); sq.Reset(); sq.Start(currentState); } else { Logger.Log("[Sequencer] Finished sequence " + sq.name, Logger.Level.Debug); sq.SetFinished(); //move lastCommandIndex past the last command so it does not get highlighted sq.lastCommandIndex++; //unlock all other sequences that may have been locked by this sequence if (affectedServos.Any()) { sequences.FindAll(s => s.commands.Any(c => affectedServos.Contains(c.servo))).ForEach((Sequence seq) => seq.isLocked = false); } } } } else { //there are still active commands if (activeWaitCount > 0) { //we have some waits in the queue if (sq.commands[sq.lastCommandIndex].wait && sq.commands[sq.lastCommandIndex].waitTime == 0f && sq.commands[sq.lastCommandIndex].ag == KSPActionGroup.None && sq.commands[sq.lastCommandIndex].agX == -1) { //the last executed command is to wait for all other commands to finish //if it is the only active command we are waiting for - mark it as Finished and proceeed. if (activeWaitCount == 1 && activeCount == 1) { sq.commands[sq.lastCommandIndex].isFinished = true; sq.commands[sq.lastCommandIndex].isActive = false; sq.isWaiting = false; if (sq.commands[sq.lastCommandIndex].gotoIndex != -1) { //apart from pure wait this is a Goto command if (sq.commands[sq.lastCommandIndex].gotoCounter > 0 || sq.commands[sq.lastCommandIndex].gotoCounter == -1) { //we need to set all commands before it in the sequence as not Finished and Resume from gotoIndex if (sq.commands[sq.lastCommandIndex].gotoCounter > 0) { sq.commands[sq.lastCommandIndex].gotoCounter--; } sq.commands.GetRange(sq.commands[sq.lastCommandIndex].gotoIndex, sq.commands.Count - sq.commands[sq.lastCommandIndex].gotoIndex) .ForEach(delegate(BasicCommand c) { c.isFinished = false; c.isActive = false; }); sq.Resume(sq.commands[sq.lastCommandIndex].gotoIndex); } } else { Logger.Log("[Sequencer] Restarting sequence " + sq.name + " after Wait command", Logger.Level.Debug); sq.Start(currentState); } } else { //there are some Delays among other commands in the active queue, we should wait for them to finish too //doing nothing here } } else { //last command was not a wait, but there are delays in the queue //just wait for them to complete, do nothing } } else { //there are no wait commands in the queue, we can restart the queue from lastCommandIndex+1 if (sq.lastCommandIndex + 1 < sq.commands.Count) { sq.isWaiting = false; sq.Resume(sq.lastCommandIndex + 1); } else { //do nothing, just wait for active commands to finish } } } } //now we need to look through all the sequences and if there are some that are Finished //we need to change the currentState accordingly and reset the sequence foreach (Sequence sq in sequences) { if (sq.isFinished && sq.endState != null && sq.startState != sq.endState) { var oldState = currentState; currentState = sq.endState; //reset the sequence to the original state sq.Reset(); Logger.Log("[ModuleSequencer] Sequence " + sq.name + " finished, changing current state to " + sq.endState.stateName, Logger.Level.Debug); //now we need to process OnStateChange events OnStateChange(oldState, currentState); } } }
public void LoadData() { //requires ServoGroups to be parsed if (!IRWrapper.APIReady) { //we tried loading, but the ServoController was not ready loadPending = true; return; } if (sequences == null || states == null) { return; } states.Clear(); sequences.Clear(); //first load the states var chunks = serializedStates.Split('$'); int counter = 0; foreach (string serializedState in chunks) { SequencerState s; if (TryParseState(serializedState, out s)) { states.Add(s); counter++; } } //there should always be at least one state, a default state if (states.Count == 0) { var defState = new SequencerState(); defState.stateName = "Default"; states.Add(defState); Logger.Log(string.Format("Failed loading States, creating Default State"), Logger.Level.Debug); } else { Logger.Log(string.Format("Successfully Loaded {0} States", counter), Logger.Level.Debug); } currentState = states.Find(x => x.stateID.ToString() == lastStateID); //if we could not find current state revert to first available state if (currentState == null) { currentState = states[0]; } chunks = serializedSequences.Split('$'); counter = 0; foreach (string serializedSequence in chunks) { Sequence s; if (TryParseSequence(serializedSequence, out s)) { sequences.Add(s); counter++; } } loadPending = false; Logger.Log(string.Format("Successfully Loaded {0} out of {1} Sequences", counter, chunks.Length), Logger.Level.Debug); }