1 | // **************************************************************************
|
---|
2 | /** @class StateMachineDim
|
---|
3 |
|
---|
4 | This class implements a StateMachine within a Dim network. It redirects
|
---|
5 | all output posted via MessageImp to a service called "NAME/MESSAGE"
|
---|
6 | while name is the name of the machine given in the constructor. In
|
---|
7 | addition two services are offered: NAME/STATE and NAME/VERSION.
|
---|
8 | NAME/STATE propagates any state change to the netork.
|
---|
9 |
|
---|
10 | When constructing the Dim network is started and while dstruction it is
|
---|
11 | stopped.
|
---|
12 |
|
---|
13 | @todo
|
---|
14 | Proper support for versioning
|
---|
15 |
|
---|
16 | */
|
---|
17 | // **************************************************************************
|
---|
18 | #include "StateMachineDim.h"
|
---|
19 |
|
---|
20 | #include "tools.h"
|
---|
21 |
|
---|
22 | #include "EventDim.h"
|
---|
23 | #include "ServiceDim.h"
|
---|
24 |
|
---|
25 | using namespace std;
|
---|
26 |
|
---|
27 | const int StateMachineDim::fVersion = 42;
|
---|
28 |
|
---|
29 | // --------------------------------------------------------------------------
|
---|
30 | //
|
---|
31 | //! The constrcutor first initialized DimStart with the given machine name.
|
---|
32 | //! DimStart is just a wrapper which constructor calls DimServer::start()
|
---|
33 | //! to ensure that initializing the Dim sub-system, is the first what is
|
---|
34 | //! done.
|
---|
35 | //!
|
---|
36 | //! The second objet instantiated is the MessageDim class which offers
|
---|
37 | //! the MESSAGE service used to broadcast logging messages to other
|
---|
38 | //! Dim clients.
|
---|
39 | //!
|
---|
40 | //! After this the services STATE and VERSION are setup. STATE will
|
---|
41 | //! be used to broadcast the state of the machine. Version broadcasts
|
---|
42 | //! the global version number of the StateMachineDim implementation
|
---|
43 | //!
|
---|
44 | //! After redirecting the handler which handels dim's EXIT command
|
---|
45 | //! to ourself (it will then call StateMachineDim::exitHandler) and
|
---|
46 | //! adding human readable state names for the default states
|
---|
47 | //! implemented by StateMachingImp the state is set to kSM_Initializing.
|
---|
48 | //! Warning: The EXIT handler is global!
|
---|
49 | //!
|
---|
50 | //! @param name
|
---|
51 | //! The name with which the dim-services should be prefixed, e.g.
|
---|
52 | //! "DRIVE" will lead to "DRIVE/SERVICE". It is also propagated
|
---|
53 | //! to DimServer::start()
|
---|
54 | //!
|
---|
55 | //! @param out
|
---|
56 | //! A refrence to an ostream which allows to redirect the log-output
|
---|
57 | //! to something else than cout. The default is cout. The reference
|
---|
58 | //! is propagated to fLog
|
---|
59 | //!
|
---|
60 | //! @todo
|
---|
61 | //! - Shell the VERSION be set from the derived class?
|
---|
62 | //
|
---|
63 | StateMachineDim::StateMachineDim(ostream &out, const std::string &name)
|
---|
64 | : DimLog(out, name), DimStart(name, DimLog::fLog), StateMachineImp(out, name),
|
---|
65 | fDescriptionStates(name+"/STATE_LIST", "",
|
---|
66 | "Provides a list with descriptions for each service."
|
---|
67 | "|StateList[string]:A \\n separated list of the form id:name=description"),
|
---|
68 | fSrvState(name+"/STATE", "",
|
---|
69 | "Provides the state of the state machine as quality of service."
|
---|
70 | "|Text[string]:A human readable string sent by the last state change.")
|
---|
71 | // fSrvVersion((name+"/VERSION").c_str(), const_cast<int&>(fVersion)),
|
---|
72 | {
|
---|
73 | SetDefaultStateNames();
|
---|
74 | }
|
---|
75 |
|
---|
76 | // --------------------------------------------------------------------------
|
---|
77 | //
|
---|
78 | //! Overwrite StateMachineImp::AddTransition to create a EventDim
|
---|
79 | //! instead of an Event object. The event name propagated to the EventDim
|
---|
80 | //! is fName+"/"+name.
|
---|
81 | //!
|
---|
82 | //! For parameter description see StateMachineImp.
|
---|
83 | //!
|
---|
84 | EventImp *StateMachineDim::CreateEvent(const string &name, const string &fmt)
|
---|
85 | {
|
---|
86 | return new EventDim(GetName()+"/"+name, fmt, this);
|
---|
87 | }
|
---|
88 |
|
---|
89 | EventImp *StateMachineDim::CreateService(const string &name)
|
---|
90 | {
|
---|
91 | return new ServiceDim(name, this);
|
---|
92 | }
|
---|
93 |
|
---|
94 | // --------------------------------------------------------------------------
|
---|
95 | //
|
---|
96 | //! Overwrite StateMachineImp::AddStateName. In addition to storing the
|
---|
97 | //! state locally it is also propagated through Dim in the STATE_LIST
|
---|
98 | //! service.
|
---|
99 | //!
|
---|
100 | //! @param state
|
---|
101 | //! Number of the state to which a name should be assigned
|
---|
102 | //!
|
---|
103 | //! @param name
|
---|
104 | //! A name which should be assigned to the state, e.g. "Tracking"
|
---|
105 | //!
|
---|
106 | //! @param doc
|
---|
107 | //! A explanatory text describing the state
|
---|
108 | //!
|
---|
109 | void StateMachineDim::AddStateName(const int state, const std::string &name, const std::string &doc)
|
---|
110 | {
|
---|
111 | StateMachineImp::AddStateName(state, name, doc);
|
---|
112 |
|
---|
113 | const string str0 = fDescriptionStates.itsData ? reinterpret_cast<char*>(fDescriptionStates.itsData) : "";
|
---|
114 | const string str1 = to_string(state)+':'+name+'=';
|
---|
115 |
|
---|
116 | if (str0.find(str1)!=string::npos)
|
---|
117 | return;
|
---|
118 |
|
---|
119 | fDescriptionStates.Update(str0+str1+doc+'\n');
|
---|
120 | }
|
---|
121 |
|
---|
122 | // --------------------------------------------------------------------------
|
---|
123 | //
|
---|
124 | //! Overwrite StateMachineImp::SetCurrentState. In addition to
|
---|
125 | //! calling StateMachineImo::SetCurrentState the new state is also
|
---|
126 | //! distributed via the DimService STATE.
|
---|
127 | //!
|
---|
128 | //! For parameter description see StateMachineImp.
|
---|
129 | //!
|
---|
130 | string StateMachineDim::SetCurrentState(int state, const char *txt, const std::string &cmd)
|
---|
131 | {
|
---|
132 | const string msg = StateMachineImp::SetCurrentState(state, txt, cmd);
|
---|
133 | if (msg.empty())
|
---|
134 | return "";
|
---|
135 |
|
---|
136 | fSrvState.setQuality(state);
|
---|
137 | fSrvState.Update(msg);
|
---|
138 |
|
---|
139 | return msg;
|
---|
140 | }
|
---|
141 |
|
---|
142 | // --------------------------------------------------------------------------
|
---|
143 | //
|
---|
144 | //! In the case of dim this secures HandleEvent against dim's commandHandler
|
---|
145 | //!
|
---|
146 | void StateMachineDim::Lock()
|
---|
147 | {
|
---|
148 | dim_lock();
|
---|
149 | }
|
---|
150 |
|
---|
151 | // --------------------------------------------------------------------------
|
---|
152 | //
|
---|
153 | //! In the case of dim this secures HandleEvent against dim's commandHandler
|
---|
154 | //!
|
---|
155 | void StateMachineDim::UnLock()
|
---|
156 | {
|
---|
157 | dim_unlock();
|
---|
158 | }
|
---|
159 |
|
---|
160 | void StateMachineDim::infoHandler()
|
---|
161 | {
|
---|
162 | DimInfo *inf = getInfo();
|
---|
163 | if (!inf)
|
---|
164 | return;
|
---|
165 |
|
---|
166 | const EventImp *evt = dynamic_cast<EventImp*>(inf);
|
---|
167 |
|
---|
168 | if (HasEvent(evt))
|
---|
169 | PostEvent(*evt);
|
---|
170 | }
|
---|
171 |
|
---|
172 | // --------------------------------------------------------------------------
|
---|
173 | //
|
---|
174 | //! Overwritten DimCommand::commandHandler()
|
---|
175 | //!
|
---|
176 | //! If fCurrentState is smaller than 0 or we are in kSM_FatalError state,
|
---|
177 | //! all incoming commands are ignored.
|
---|
178 | //!
|
---|
179 | //! The commandHandler will go through the list of available commands
|
---|
180 | //! (fListOfEventss). If the received command was recognized, it is added
|
---|
181 | //! via PushCommand into the fifo.
|
---|
182 | //!
|
---|
183 | //! @todo
|
---|
184 | //! - Fix the exit when cmd is not of type EventImp
|
---|
185 | //! - Fix docu
|
---|
186 | //! - Do we need a possibility to suppress a call to "HandleEvent"
|
---|
187 | //! or is a state<0 enough?
|
---|
188 | //
|
---|
189 | void StateMachineDim::commandHandler()
|
---|
190 | {
|
---|
191 | DimCommand *cmd = getCommand();
|
---|
192 | if (!cmd)
|
---|
193 | return;
|
---|
194 |
|
---|
195 | const EventImp *evt = dynamic_cast<EventImp*>(cmd);
|
---|
196 |
|
---|
197 | if (HasEvent(evt))
|
---|
198 | PostEvent(*evt);
|
---|
199 | }
|
---|
200 |
|
---|
201 | // --------------------------------------------------------------------------
|
---|
202 | //
|
---|
203 | //! Overwrites MessageImp::Update. This redirects output issued via
|
---|
204 | //! MessageImp to MessageDim object.
|
---|
205 | //
|
---|
206 | int StateMachineDim::Write(const Time &time, const string &txt, int qos)
|
---|
207 | {
|
---|
208 | return DimLog::fLog.Write(time, txt, qos);
|
---|
209 | }
|
---|
210 |
|
---|
211 | // --------------------------------------------------------------------------
|
---|
212 | //
|
---|
213 | //! exitHandler of the DimServer. The EXIT command is implemented by each
|
---|
214 | //! DimServer automatically. exitHandler calls Stop(code) and exit(-1)
|
---|
215 | //! in case the received exit-value is a special number (42). abort()
|
---|
216 | //! is called if 0x42 is received.
|
---|
217 | //!
|
---|
218 | //! @param code
|
---|
219 | //! value which is passed to Stop(code)
|
---|
220 | //
|
---|
221 | void StateMachineDim::exitHandler(int code)
|
---|
222 | {
|
---|
223 | Out() << " -- " << Time().GetAsStr() << " - EXIT(" << code << ") command received." << endl;
|
---|
224 | if (code<0) // negative values reserved for internal use
|
---|
225 | {
|
---|
226 | Out() << " -- " << Time().GetAsStr() << ": ignored." << endl;
|
---|
227 | return;
|
---|
228 | }
|
---|
229 |
|
---|
230 | Stop(code);
|
---|
231 | if (code==42)
|
---|
232 | exit(128);
|
---|
233 | if (code==0x42)
|
---|
234 | abort();
|
---|
235 | }
|
---|