File: StateMachine.hpp

package info (click to toggle)
indi-bresserexos2 1.0%2B20221223130124-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, trixie
  • size: 3,136 kB
  • sloc: cpp: 2,317; makefile: 2
file content (188 lines) | stat: -rw-r--r-- 5,858 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
 * StateMachine.hpp
 *
 * Copyright 2020 Kevin Krüger <kkevin@gmx.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 *
 */

#pragma once

#include <cstdint>
#include <map>
#include <unordered_set>
#include <tuple>
#include <limits>
#include <mutex>
#include "config.h"

namespace TelescopeMountControl
{
//interface template for statemachine transition notification.
template<typename StateType, typename SignalType>
class IStateNotification
{
    public:
        //every time a state change occurs this function is called.
        virtual void OnTransitionChanged(StateType fromState, SignalType signal, StateType toState) = 0;

        //If the error state is triped this callback is called.
        virtual void OnErrorStateReached(StateType fromState, SignalType signal) = 0;
};

//state machine with thread safe state transition.
template<typename StateType, typename SignalType, class NotificationInterfaceImplementation>
class StateMachine
{
        //tuple used for state signal relationship.
        typedef std::tuple<StateType, SignalType> StateSignal;

    private:
        //implementation object of the notification inferface
        NotificationInterfaceImplementation &mStateMachineNotification;

        //transition table of the state changes
        std::map<StateSignal, StateType> mTransitionTable;

        //set of states which are concidered final.
        std::unordered_set<StateType> mFinalStates;

        //start state of the state machine.
        StateType mStartState;

        //any undefined transition causes this state to be active
        StateType mErrorState;

        //the pointer of the current state.
        StateType mCurrentState;

        //make this whole thing thread safe.
        std::mutex mMutex;

    public:
        StateMachine(NotificationInterfaceImplementation &interfaceImplementation, StateType startState, StateType errorState) :
            mStateMachineNotification(interfaceImplementation),
            mStartState(startState),
            mErrorState(errorState),
            mCurrentState(mStartState)
        {

        }

        virtual ~StateMachine()
        {

        }

        //Reset the machine so it can simply restart.
        bool Reset()
        {
            std::lock_guard<std::mutex> guard(mMutex); //lock this function call to avoid concurrent modification.

            mCurrentState = mStartState;

            return true;
        }

        //mark a state as final.
        bool AddFinalState(StateType state)
        {
            std::lock_guard<std::mutex> guard(mMutex); //lock this function call to avoid concurrent modification.

            mFinalStates.insert(state);

            return false;
        }

        //Add a transition from a state to a state tripped by a signal. All types should be in range of the state and signal types.
        bool AddTransition(StateType fromState, SignalType signal, StateType toState)
        {
            std::lock_guard<std::mutex> guard(mMutex); //lock this function call to avoid concurrent modification.

            StateSignal stateSignalTupel = std::make_tuple(fromState, signal);

            if(mTransitionTable.count(stateSignalTupel) == 0) //only deterministic state machines alowed.
            {
                mTransitionTable[stateSignalTupel] = toState;
                return true;

            }

            return false;
        }

        //submit a signal to the state machine and do a transistion.
        //the notify interface gets called when a transition or indefined transition occurred.
        bool DoTransition(SignalType signal)
        {
            std::lock_guard<std::mutex> guard(mMutex); //lock this function call to avoid concurrent modification.

            StateSignal stateSignalTupel = std::make_tuple(mCurrentState, signal);

            StateType fromState = mCurrentState;

            if(mTransitionTable.count(stateSignalTupel) > 0) //transition defined.
            {
                StateType toState = mTransitionTable[stateSignalTupel];

                mCurrentState = toState;

                mStateMachineNotification.OnTransitionChanged(fromState, signal, toState);

                return true;
            }
            else //undefined transition.
            {
                mCurrentState = mErrorState;

                mStateMachineNotification.OnErrorStateReached(fromState, signal);
            }

            return false;
        }

        //returns true if the current state is a final state.
        bool IsFinalized()
        {
            std::lock_guard<std::mutex> guard(mMutex);

            if(mFinalStates.count(mCurrentState) > 0)
            {
                return true;
            }

            return false;
        }

        // Returns true if the state machine is in the error state.
        bool IsInErrorState()
        {
            std::lock_guard<std::mutex> guard(mMutex);

            return mCurrentState == mErrorState;
        }

        //returns the current state of the machine.
        StateType CurrentState()
        {
            std::lock_guard<std::mutex> guard(mMutex);

            return mCurrentState;
        }
};
}