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