Esempio n. 1
0
 public Sequence(Sequence baseSequence) : this()
 {
     //commands.AddRange(baseSequence.commands);
     baseSequence.commands.ForEach((BasicCommand bc) => commands.Add(new BasicCommand(bc)));
     name       = "Copy of " + baseSequence.name;
     startState = baseSequence.startState;
     endState   = baseSequence.endState;
 }
Esempio n. 2
0
 public Sequence(Sequence baseSequence)
     : this()
 {
     //commands.AddRange(baseSequence.commands);
     baseSequence.commands.ForEach ((BasicCommand bc) => commands.Add (new BasicCommand (bc)));
     name = "Copy of " + baseSequence.name;
     startState = baseSequence.startState;
     endState = baseSequence.endState;
 }
Esempio n. 3
0
        public void Start(SequencerState currentState)
        {
            Logger.Log("[Sequencer] Sequence started", Logger.Level.Debug);

            if (commands == null)
            {
                return;
            }

            if (isLocked)
            {
                Logger.Log("[Sequencer] Cannot start sequence " + name + " as it is Locked", Logger.Level.Debug);
                return;
            }

            if (currentState != startState)
            {
                Logger.Log("[Sequencer] Cannot start sequence " + name + " because its start state is not current state", Logger.Level.Debug);
                return;
            }
            //if the sequence is marked as Finished - reset it and start anew.
            if (isFinished)
            {
                Reset();
            }

            isActive = true;

            //find first unfinished command
            lastCommandIndex = commands.FindIndex(s => s.isFinished == false);
            Logger.Log("[Sequencer] First unfinished Index = " + lastCommandIndex, Logger.Level.Debug);
            if (lastCommandIndex == -1)
            {
                //there are no unfinished commands, loop if needed or SetFinished and exit
                if (isLooped)
                {
                    Reset();
                }
                else
                {
                    SetFinished();
                    return;
                }
            }
            //now we can start/continue execution
            //we execute commands until first wait command

            Resume(lastCommandIndex);

            Logger.Log("[Sequencer] Sequence Start finished, lastCommandIndex = " + lastCommandIndex, Logger.Level.Debug);
            //else we are either finished, or most likely waiting for commands to finish.
        }
        /// <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);
        }
        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 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;
        }
        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);
            }
        }
        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];
        }
Esempio n. 11
0
        public void Start(SequencerState currentState)
        {
            Logger.Log("[Sequencer] Sequence started", Logger.Level.Debug);

            if (commands == null) return;

            if (isLocked)
            {
                Logger.Log ("[Sequencer] Cannot start sequence " + name + " as it is Locked", Logger.Level.Debug);
                return;
            }

            if (currentState != startState)
            {
                Logger.Log ("[Sequencer] Cannot start sequence " + name + " because its start state is not current state", Logger.Level.Debug);
                return;
            }
            //if the sequence is marked as Finished - reset it and start anew.
            if (isFinished)
                Reset();

            isActive = true;

            //find first unfinished command
            lastCommandIndex = commands.FindIndex(s => s.isFinished == false);
            Logger.Log("[Sequencer] First unfinished Index = " + lastCommandIndex, Logger.Level.Debug);
            if (lastCommandIndex == -1)
            {
                //there are no unfinished commands, loop if needed or SetFinished and exit
                if(isLooped)
                {
                    Reset();
                }
                else
                {
                    SetFinished();
                    return;
                }
            }
            //now we can start/continue execution
            //we execute commands until first wait command

            Resume(lastCommandIndex);

            Logger.Log("[Sequencer] Sequence Start finished, lastCommandIndex = " + lastCommandIndex, Logger.Level.Debug);
            //else we are either finished, or most likely waiting for commands to finish.
        }