// ************************************************************************** /** @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 "tools.h" #include "Time.h" #include "EventDim.h" using namespace std; 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), fSrvState(Form("%s/STATE", name.c_str()).c_str(), const_cast("")), fSrvVersion(Form("%s/VERSION", name.c_str()).c_str(), const_cast(fVersion)) { // WARNING: This exit handler is GLOBAL! DimServer::addExitHandler(this); } // -------------------------------------------------------------------------- // //! 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::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 char *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(); }