#ifndef FACT_RemoteControl #define FACT_RemoteControl // ************************************************************************** /** @class RemoteControlImp @brief This implements the basic functions of a remote control via dim Through a ServiceList object this object subscribes to all available SERVICE_LISTs in the dim network. This allows to keep an up-to-date list of all servers and services. Its ProcessCommand member function allows to emit commands according to the services found in the network. Its infoHandler() is called as an update notifier from the ClientList object. **/ // ************************************************************************** #include using namespace std; class RemoteControlImp { protected: std::ostream &lout; /// Output stream for local synchrounous output std::string fCurrentServer; /// The server to which we currently cd'ed protected: // Redirect asynchronous output to the output window RemoteControlImp(std::ostream &, std::ostream &in) : lout(in) { } virtual ~RemoteControlImp() { } bool ProcessCommand(const std::string &str); virtual bool HasServer(const std::string &) { return false; } virtual bool SendDimCommand(ostream &, std::string &, const std::string &) { return false; } }; // ************************************************************************** /** @class RemoteControl @brief Implements a remote control based on a Readline class for the dim network This template implements all functions which overwrite any function from the Readline class. Since several derivatives of the Readline class implement different kind of Readline access, this class can be derived by any of them due to its template argument. However, the normal case will be deriving it from either Console or Shell. @tparam T The base class for RemoteControl. Either Readlien or a class deriving from it. This is usually either Console or Shell. **/ // ************************************************************************** #include "StateMachineDimControl.h" #include "InterpreterV8.h" #include "ReadlineColor.h" #include "Event.h" #include "tools.h" template class RemoteControl : public T, public RemoteControlImp, public InterpreterV8 { protected: StateMachineDimControl *fImp; void SetSection(int s) { if (fImp) fImp->Write(Time(), "", s); } int Write(const Time &time, const std::string &txt, int qos=MessageImp::kMessage) { if (!fImp) return 0; return fImp ? fImp->Write(time, txt, qos) : MessageImp::Write(time, txt, qos); } void exitHandler(int code) { if (dynamic_cast(fImp)) dynamic_cast(fImp)->Stop(code); else exit(code); } // ==================== Readline tab-completion ===================== static void append(std::string &str) { str.append("/"); } static void chop(std::string &str) { const size_t p = str.find_first_of('/'); if (p!=string::npos) str = str.substr(p+1); } // This funtion defines which generator should be called. // If it returns 0 the standard reaqdline generator are called. // Otherwise set the right generator with rl_completion_matches. char **Completion(const char *text, int start, int) { // Get the whole buffer before the tab-position const string b = string(T::GetBuffer()); const string s = b.substr(0, start); const string l = Tools::Trim(s.c_str()); if (l.empty()) { if (fCurrentServer.empty()) { const size_t p1 = b.find_first_of(' '); const size_t p2 = b.find_first_of('/'); if (p1==string::npos && p2!=string::npos) return T::Complete(GetCommandList(), text); std::vector v = GetServerList(); for_each(v.begin(), v.end(), RemoteControl::append); return T::Complete(v, text); } else { std::vector v = GetCommandList(fCurrentServer); for_each(v.begin(), v.end(), RemoteControl::chop); return T::Complete(v, text); } } return T::Complete(GetCommandList(l), text); } void EventHook() { if (fImp && !fImp->HasServer(fCurrentServer)) fCurrentServer = ""; T::EventHook(); } // ===== Interface to access the DIM network through the StateMachine ==== bool HasServer(const std::string &server) { return fImp ? fImp->HasServer(server) : false; } vector GetServerList() const { return fImp ? fImp->GetServerList() : vector(); } vector GetCommandList(const string &server) const { return fImp ? fImp->GetCommandList(server) : vector(); } vector GetCommandList() const { return fImp ? fImp->GetCommandList() : vector(); } int PrintStates(std::ostream &out, const std::string &serv="") const { return fImp ? fImp->PrintStates(out, serv) : 0; } int PrintDescription(std::ostream &out, bool iscmd, const std::string &serv="", const std::string &service="") const { return fImp ? fImp->PrintDescription(out, iscmd, serv, service) : 0; } bool SendDimCommand(ostream &out, std::string &server, const std::string &str) { try { if (fImp) fImp->SendDimCommand(server, str, out); //lout << kGreen << "Command emitted successfully to " << server << "." << endl; return true; } catch (const runtime_error &e) { lout << kRed << e.what() << endl; return false; } } // ============ Pseudo-callback interface for the JavaScrip engine ======= virtual void JsLoad(const std::string &) { SetSection(-2); } virtual void JsStart(const std::string &) { SetSection(-1); } virtual void JsEnd(const std::string &) { UnsubscribeAll(); SetSection(-3); } virtual bool JsSend(const std::string &str) { return ProcessCommand(str); } virtual void JsOut(const std::string &msg) { lout << msg << endl; } virtual void JsPrint(const std::string &msg) { if (fImp) fImp->Comment(msg.empty()?" ":msg); } virtual void JsException(const std::string &str) { if (fImp) fImp->Error(str.empty()?" ":str); } pair JsState(const std::string &server) { return fImp ? fImp->GetServerState(server) : make_pair(-256, string()); } void JsSleep(uint32_t ms) { const Time timeout = Time()+boost::posix_time::millisec(ms==0?1:ms); T::Lock(); while (timeout>Time() && !T::IsScriptStopped()) usleep(1); T::Unlock(); } int JsWait(const string &server, int32_t state, uint32_t ms) { if (!fImp) { lout << kRed << "RemoteControl class not fully initialized." << endl; T::StopScript(); return -1; } if (!HasServer(server)) { lout << kRed << "Server '" << server << "' not found." << endl; T::StopScript(); return -1; } T::Lock(); const Time timeout = ms<=0 ? Time(Time::none) : Time()+boost::posix_time::millisec(ms); int rc = 0; while (timeout>Time() && !T::IsScriptStopped()) { const pair st = fImp->GetServerState(server); if (st.first==-256) { lout << kRed << "Server '" << server << "' disconnected." << endl; T::StopScript(); return -1; } if (st.first==state) { rc = 1; break; } usleep(1); } T::Unlock(); return rc; } vector JsDescription(const string &service) { return fImp ? fImp->GetDescription(service) : vector(); } // Keep a copy of the data for access by V8 map fEvents; // For unsibscription map> fData; std::mutex fMutex; pair JsGetEvent(const std::string &service) { const lock_guard lock(fMutex); const auto it = fData.find(service); return it==fData.end() ? make_pair(uint64_t(0), (Event*)0) : make_pair(it->second.first, &it->second.second); } void Handle(const EventImp &evt, const string &service) { const lock_guard lock(fMutex); const auto it = fData.find(service); if (it==fData.end()) fData[service] = make_pair(0, static_cast(evt)); else { it->second.first++; it->second.second = static_cast(evt); } } void *JsSubscribe(const std::string &service) { if (!fImp) return 0; // Do not subscribe twice if (fEvents.find(service)!=fEvents.end()) return 0; return fEvents[service] = &fImp->Subscribe(service)(fImp->Wrap(bind(&RemoteControl::Handle, this, placeholders::_1, service))); } bool JsUnsubscribe(const std::string &service) { if (!fImp) return false; const auto it = fEvents.find(service); if (it==fEvents.end()) return false; fImp->Unsubscribe(it->second); fEvents.erase(it); return true; } void UnsubscribeAll() { for (auto it=fEvents.begin(); it!=fEvents.end(); it++) fImp->Unsubscribe(it->second); fEvents.clear(); } // =========================================================================== public: // Redirect asynchronous output to the output window RemoteControl(const char *name) : T(name), RemoteControlImp(T::GetStreamOut(), T::GetStreamIn()), fImp(0) { } bool PrintGeneralHelp() { T::PrintGeneralHelp(); lout << " " << kUnderline << "Specific commands:\n"; lout << kBold << " h,help " << kReset << "List help text for given server or command.\n"; lout << kBold << " svc,services " << kReset << "List all services in the network.\n"; lout << kBold << " st,states " << kReset << "List all states in the network.\n"; lout << kBold << " > " << kReset << "Echo to the output stream\n"; lout << kBold << " .s " << kReset << "Wait for the state-machine to change to the given state.\n"; lout << " " " .s [ [ [