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