// ************************************************************************** /** @class StateMachineDim @brief Class for a state machine implementation within a DIM network This class implements a StateMachine within a Dim network. It redirects all output posted via MessageImp to a service called "NAME/MESSAGE" while name is the name of the machine given in the constructor. In addition two services are offered: NAME/STATE and NAME/VERSION. NAME/STATE propagates any state change to the netork. When constructing the Dim network is started and while dstruction it is stopped. @todo Proper support for versioning */ // ************************************************************************** #include "StateMachineDim.h" #include #include "tools.h" #include "Time.h" #include "EventDim.h" using namespace std; using boost::lexical_cast; const int StateMachineDim::fVersion = 42; // -------------------------------------------------------------------------- // //! The constrcutor first initialized DimStart with the given machine name. //! DimStart is just a wrapper which constructor calls DimServer::start() //! to ensure that initializing the Dim sub-system, is the first what is //! done. //! //! The second objet instantiated is the MessageDim class which offers //! the MESSAGE service used to broadcast logging messages to other //! Dim clients. //! //! After this the services STATE and VERSION are setup. STATE will //! be used to broadcast the state of the machine. Version broadcasts //! the global version number of the StateMachineDim implementation //! //! After redirecting the handler which handels dim's EXIT command //! to ourself (it will then call StateMachineDim::exitHandler) and //! adding human readable state names for the default states //! implemented by StateMachingImp the state is set to kSM_Initializing. //! Warning: The EXIT handler is global! //! //! @param name //! The name with which the dim-services should be prefixed, e.g. //! "DRIVE" will lead to "DRIVE/SERVICE". It is also propagated //! to DimServer::start() //! //! @param out //! A refrence to an ostream which allows to redirect the log-output //! to something else than cout. The default is cout. The reference //! is propagated to fLog //! //! @todo //! - Shell the VERSION be set from the derived class? // StateMachineDim::StateMachineDim(ostream &out, const std::string &name) : StateMachine(out, name), DimStart(name, *this), fLog(name, out), fDescriptionStates((name+"/STATE_LIST").c_str(), const_cast(""), "Provides a list with descriptions for each service." "|StateList[string]:A \\n separated list of the form id:name=description"), fSrvState((name+"/STATE").c_str(), const_cast(""), "Provides the state of the state machine as quality of service." "|Text[string]:A human readable string sent by the last state change.") // fSrvVersion((name+"/VERSION").c_str(), const_cast(fVersion)), { SetDefaultStateNames(); } // -------------------------------------------------------------------------- // //! Overwrite StateMachineImp::AddTransition to create a EventDim //! instead of an Event object. The event name propagated to the EventDim //! is fName+"/"+name. //! //! For parameter description see StateMachineImp. //! EventImp *StateMachineDim::CreateEvent(int targetstate, const char *name, const char *fmt) { return new EventDim(targetstate, GetName()+"/"+name, fmt, this); } // -------------------------------------------------------------------------- // //! Overwrite StateMachineImp::AddStateName. In addition to storing the //! state locally it is also propagated through Dim in the STATE_LIST //! service. //! //! @param state //! Number of the state to which a name should be assigned //! //! @param name //! A name which should be assigned to the state, e.g. "Tracking" //! //! @param doc //! A explanatory text describing the state //! void StateMachineDim::AddStateName(const int state, const std::string &name, const std::string &doc) { StateMachineImp::AddStateName(state, name, doc); const string str0 = reinterpret_cast(fDescriptionStates.itsData); const string str1 = lexical_cast(state)+':'+name+'='; if (str0.find(str1)!=string::npos) return; fDescriptionStates.setData(const_cast((str0+str1+doc+'\n').c_str())); fDescriptionStates.updateService(); } // -------------------------------------------------------------------------- // //! Overwrite StateMachineImp::SetCurrentState. In addition to //! calling StateMachineImo::SetCurrentState the new state is also //! distributed via the DimService STATE. //! //! For parameter description see StateMachineImp. //! string StateMachineDim::SetCurrentState(int state, const char *txt, const std::string &cmd) { const string msg = StateMachineImp::SetCurrentState(state, txt, cmd); if (msg.empty()) return ""; fSrvState.setQuality(state); fSrvState.setData(const_cast(msg.c_str())); fSrvState.updateService(); return msg; } // -------------------------------------------------------------------------- // //! Overwritten DimCommand::commandHandler() //! //! If fCurrentState is smaller than 0 or we are in kSM_FatalError state, //! all incoming commands are ignored. //! //! The commandHandler will go through the list of available commands //! (fListOfEventss). If the received command was recognized, it is added //! via PushCommand into the fifo. //! //! @todo //! - Fix the exit when cmd is not of type EventImp //! - Fix docu //! - Do we need a possibility to suppress a call to "HandleEvent" //! or is a state<0 enough? // void StateMachineDim::commandHandler() { DimCommand *cmd = getCommand(); if (!cmd) return; const EventImp *evt = dynamic_cast(cmd); // FIXME: In the case of HandleEvent there is no need to copy the data // FIMXE: Currentyl the time is not copied // FIXME: Handle Event is not thread safe (Dim + Console) if (HasEvent(evt)) PostEvent(*evt); } // -------------------------------------------------------------------------- // //! Overwrites MessageImp::Update. This redirects output issued via //! MessageImp to MessageDim object. // int StateMachineDim::Write(const Time &time, const string &txt, int qos) { return fLog.Write(time, txt, qos); } // -------------------------------------------------------------------------- // //! exitHandler of the DimServer. The EXIT command is implemented by each //! DimServer automatically. exitHandler calls Stop(code) and exit(-1) //! in case the received exit-value is a special number (42). abort() //! is called if 0x42 is received. //! //! @param code //! value which is passed to Stop(code) // void StateMachineDim::exitHandler(int code) { Out() << " -- " << Time().GetAsStr() << ": EXIT(" << code << ") command received." << endl; if (code<0) // negative values reserved for internal use { Out() << " -- " << Time().GetAsStr() << ": ignored." << endl; return; } Stop(code); if (code==42) exit(-1); if (code==0x42) abort(); }