Index: trunk/FACT++/src/StateMachineDimControl.cc
===================================================================
--- trunk/FACT++/src/StateMachineDimControl.cc	(revision 14132)
+++ trunk/FACT++/src/StateMachineDimControl.cc	(revision 14132)
@@ -0,0 +1,503 @@
+#include "StateMachineDimControl.h"
+
+#include "Dim.h"
+#include "Event.h"
+#include "RemoteControl.h"
+#include "Configuration.h"
+#include "Converter.h"
+
+using namespace std;
+
+// ------------------------------------------------------------------------
+
+bool StateMachineDimControl::fIsServer = false;
+
+string StateMachineDimControl::Line(const string &txt, char fill)
+{
+    const int n = (55-txt.length())/2;
+
+    ostringstream out;
+    out << setfill(fill);
+    out << setw(n) << fill << ' ';
+    out << txt;
+    out << ' ' << setw(n) << fill;
+
+    if (2*n+txt.length()+2 != 57)
+        out << fill;
+
+    return out.str();
+}
+
+int StateMachineDimControl::ChangeState(int qos, const Time &time, int scriptdepth, string scriptfile, string user)
+{
+    ostringstream pid;
+    pid << getpid();
+
+    string msg;
+    switch (qos)
+    {
+    case -3: msg = "End";   break;
+    case -2: msg = "Load";  break;
+    case -1: msg = "Start"; break;
+    default:
+        {
+            ostringstream out;
+            out << "Label " << qos;
+            msg = out.str();
+        }
+    }
+
+    if (qos<0)
+        msg += "-"+to_string(scriptdepth);
+
+    msg += ": "+scriptfile+" ["+user+":"+pid.str()+"]";
+
+    if (fDebug)
+        Write(time, Line(msg, qos<-1 ? '=' :'-'), 90);
+
+    if (qos==-3)
+        fScriptUser = fUser;
+
+    return qos+3;
+}
+
+int StateMachineDimControl::Write(const Time &time, const std::string &txt, int qos)
+{
+    if (txt=="")
+    {
+        // Post an anonymous event to the event loop
+        Event evt("");
+        evt.AssignFunction(bind(&StateMachineDimControl::ChangeState, this,
+                                qos, time, Readline::GetScriptDepth(),
+                                Readline::GetScript(), fScriptUser));
+        return PostEvent(evt);
+    }
+
+    // if (qos<fVerbosity)
+    //     return 0;
+
+    return StateMachineDim::Write(time, txt, qos);
+}
+
+int StateMachineDimControl::StartScript(const EventImp &imp, const string &cmd)
+{
+    string opt(imp.GetString());
+
+    const map<string,string> data = Tools::Split(opt, true);
+
+    if (imp.GetSize()==0 || opt.size()==0 || opt[0]==0)
+    {
+        Error("File name missing in DIM_CONTROL/START");
+        return GetCurrentState();
+    }
+
+    if (fDebug)
+        Debug("Start '"+opt+"' received.");
+
+    if (fDebug)
+        Debug("Received data: "+imp.GetString());
+
+    const auto user = data.find("user");
+    fScriptUser = user==data.end() ? fUser : user->second;
+
+    if (fDebug)
+    {
+        for (auto it=data.begin(); it!=data.end(); it++)
+            Debug("   Arg: "+it->first+" = "+it->second);
+    }
+
+    Readline::SetExternalInput(cmd+imp.GetString());
+    return 1;
+}
+
+int StateMachineDimControl::StopScript()
+{
+    Readline::StopScript();
+    InterpreterV8::JsStop();
+    return GetCurrentState();
+}
+
+void StateMachineDimControl::SendDimCommand(const string &server, string str, ostream &lout)
+{
+    const lock_guard<mutex> guard(fMutex);
+
+    if (fServerList.find(server)==fServerList.end())
+        throw runtime_error("Server '"+server+"' not online.");
+
+    str = Tools::Trim(str);
+
+    // Find the delimiter between the command name and the data
+    size_t p0 = str.find_first_of(' ');
+    if (p0==string::npos)
+        p0 = str.length();
+
+    // Get just the command name separated from the data
+    const string name = str.substr(0, p0);
+
+    // Compile the command which will be sent to the state-machine
+    for (auto is=fServiceList.begin(); is!=fServiceList.end(); is++)
+    {
+        if (is->server!=server || is->service!=name)
+            continue;
+
+        if (!is->iscmd)
+            throw runtime_error("'"+server+"/"+name+" not a command.");
+
+        // Avoid compiler warning of unused parameter
+        lout << flush;
+
+        // Convert the user entered data according to the format string
+        // into a data block which will be attached to the event
+#ifndef DEBUG
+        ostringstream sout;
+        const Converter conv(sout, is->format, false);
+#else
+        const Converter conv(lout, is->format, false);
+#endif
+        if (!conv)
+            throw runtime_error("Couldn't properly parse the format... ignored.");
+
+#ifdef DEBUG
+        lout << kBlue << server << '/' << name;
+#endif
+        const vector<char> v = conv.GetVector(str.substr(p0));
+#ifdef DEBUG
+        lout << kBlue << " [" << v.size() << "]" << endl;
+#endif
+        const string cmd = server + '/' + name;
+        const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
+        if (!rc)
+            throw runtime_error("ERROR - Sending command "+cmd+" failed.");
+
+        return;
+    }
+
+    throw runtime_error("Unkown server '"+server+"'");
+}
+
+int StateMachineDimControl::PrintStates(std::ostream &out, const std::string &serv)
+{
+    const lock_guard<mutex> guard(fMutex);
+
+    int rc = 0;
+    for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
+    {
+        if (!serv.empty() && *it!=serv)
+            continue;
+
+        out << kRed << "----- " << *it << " -----" << endl;
+
+        int cnt = 0;
+        for (auto is=fStateDescriptionList.begin(); is!=fStateDescriptionList.end(); is++)
+        {
+            const string &server = is->first.first;
+
+            if (server!=*it)
+                continue;
+
+            const int32_t &state   = is->first.second;
+            const string  &name    = is->second.first;
+            const string  &comment = is->second.second;
+
+            out << kBold   << setw(5) << state << kReset << ": ";
+            out << kYellow << name;
+            if (!comment.empty())
+                out << kBlue   << " (" << comment << ")";
+            out << endl;
+
+            cnt++;
+        }
+
+        if (cnt==0)
+            out << "   <no states>" << endl;
+        else
+            rc++;
+
+        out << endl;
+    }
+
+    return rc;
+}
+
+int StateMachineDimControl::PrintDescription(std::ostream &out, bool iscmd, const std::string &serv, const std::string &service)
+{
+    const lock_guard<mutex> guard(fMutex);
+
+    int rc = 0;
+    for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
+    {
+        if (!serv.empty() && *it!=serv)
+            continue;
+
+        out << kRed << "----- " << *it << " -----" << endl << endl;
+
+        for (auto is=fServiceList.begin(); is!=fServiceList.end(); is++)
+        {
+            if (is->server!=*it)
+                continue;
+
+            if (!service.empty() && is->service!=service)
+                continue;
+
+            if (is->iscmd!=iscmd)
+                continue;
+
+            rc++;
+
+            out << " " << is->service;
+            if (!is->format.empty())
+                out << '[' << is->format << ']';
+
+            const auto id = fServiceDescriptionList.find(*it+"/"+is->service);
+            if (id!=fServiceDescriptionList.end())
+            {
+                const vector<Description> &v = id->second;
+
+                for (auto j=v.begin()+1; j!=v.end(); j++)
+                    out << " <" << j->name << ">";
+                out << endl;
+
+                if (!v[0].comment.empty())
+                    out << "    " << v[0].comment << endl;
+
+                for (auto j=v.begin()+1; j!=v.end(); j++)
+                {
+                    out << "    " << kGreen << j->name;
+                    if (!j->comment.empty())
+                        out << kReset << ": " << kBlue << j->comment;
+                    if (!j->unit.empty())
+                        out << kYellow << " [" << j->unit << "]";
+                    out << endl;
+                }
+            }
+            out << endl;
+        }
+        out << endl;
+    }
+
+    return rc;
+}
+
+int StateMachineDimControl::HandleStateChange(const string &server, DimDescriptions *dim)
+{
+    fMutex.lock();
+    const State state = dim->description();
+    fCurrentStateList[server] = make_pair(dim->state(), state.index==DimState::kNotAvailable?"":state.name);
+    fMutex.unlock();
+
+    return GetCurrentState();
+}
+
+pair<int32_t, string> StateMachineDimControl::GetServerState(const std::string &server)
+{
+    const lock_guard<mutex> guard(fMutex);
+
+    const auto it = fCurrentStateList.find(server);
+    return it==fCurrentStateList.end() ? make_pair(-256, string()) : it->second;
+}
+
+int StateMachineDimControl::HandleStates(const string &server, DimDescriptions *dim)
+{
+    fMutex.lock();
+    for (auto it=dim->states.begin(); it!=dim->states.end(); it++)
+        fStateDescriptionList[make_pair(server, it->index)] = make_pair(it->name, it->comment);
+    fMutex.unlock();
+
+    return GetCurrentState();
+}
+
+int StateMachineDimControl::HandleDescriptions(DimDescriptions *dim)
+{
+    fMutex.lock();
+
+    for (auto it=dim->descriptions.begin(); it!=dim->descriptions.end(); it++)
+        fServiceDescriptionList[it->front().name].assign(it->begin(), it->end());
+
+    fMutex.unlock();
+
+    return GetCurrentState();
+}
+
+std::vector<Description> StateMachineDimControl::GetDescription(const std::string &service)
+{
+    const lock_guard<mutex> guard(fMutex);
+
+    const auto it = fServiceDescriptionList.find(service);
+    return it==fServiceDescriptionList.end() ? vector<Description>() : it->second;
+}
+
+int StateMachineDimControl::HandleServerAdd(const string &server)
+{
+    if (server!="DIS_DNS")
+    {
+        DimDescriptions *d = new DimDescriptions(server);
+
+        fDimDescriptionsList.push_back(d);
+        d->SetCallback(bind(&StateMachineDimControl::HandleStateChange, this, server, d));
+        d->SetCallbackStates(bind(&StateMachineDimControl::HandleStates, this, server, d));
+        d->SetCallbackDescriptions(bind(&StateMachineDimControl::HandleDescriptions, this, d));
+        d->Subscribe(*this);
+    }
+
+    // Make a copy of the list to be able to
+    // lock the access to the list
+    fMutex.lock();
+    fServerList.insert(server);
+    fMutex.unlock();
+
+    return GetCurrentState();
+}
+
+int StateMachineDimControl::HandleServerRemove(const string &server)
+{
+    fMutex.lock();
+    fServerList.erase(server);
+    fMutex.unlock();
+
+    return GetCurrentState();
+}
+
+vector<string> StateMachineDimControl::GetServerList()
+{
+    vector<string> rc;
+
+    fMutex.lock();
+
+    rc.reserve(fServerList.size());
+    for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
+        rc.push_back(*it);
+
+    fMutex.unlock();
+
+    return rc;
+}
+
+vector<string> StateMachineDimControl::GetCommandList(const string &server)
+{
+    const lock_guard<mutex> guard(fMutex);
+
+    const string  s = server.substr(0, server.length()-1);
+
+    if (fServerList.find(s)==fServerList.end())
+        return vector<string>();
+
+    vector<string> rc;
+
+    for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
+        if (it->iscmd && it->server==s)
+            rc.push_back(server+it->service);
+
+    return rc;
+}
+
+vector<string> StateMachineDimControl::GetCommandList()
+{
+    vector<string> rc;
+
+    fMutex.lock();
+
+    for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
+        if (it->iscmd)
+            rc.push_back(it->server+"/"+it->service);
+
+    fMutex.unlock();
+    return rc;
+}
+
+
+int StateMachineDimControl::HandleAddService(const Service &svc)
+{
+    // Make a copy of the list to be able to
+    // lock the access to the list
+    fMutex.lock();
+    fServiceList.insert(svc);
+    fMutex.unlock();
+
+    return GetCurrentState();
+}
+
+bool StateMachineDimControl::HasServer(const std::string &server)
+{
+    fMutex.lock();
+    const bool rc = fServerList.find(server)!=fServerList.end();
+    fMutex.unlock();
+
+    return rc;
+}
+
+StateMachineDimControl::StateMachineDimControl(ostream &out) : StateMachineDim(out, fIsServer?"DIM_CONTROL2":"")
+{
+    fDim.Subscribe(*this);
+    fDimList.Subscribe(*this);
+
+    fDimList.SetCallbackServerAdd   (bind(&StateMachineDimControl::HandleServerAdd,    this, placeholders::_1));
+    fDimList.SetCallbackServerRemove(bind(&StateMachineDimControl::HandleServerRemove, this, placeholders::_1));
+    fDimList.SetCallbackServiceAdd  (bind(&StateMachineDimControl::HandleAddService,   this, placeholders::_1));
+
+    // State names
+    AddStateName(0, "Idle",    "No script currently in processing.");
+    AddStateName(1, "Load",    "Script being loaded.");
+    AddStateName(2, "Started", "Script execution started.");
+
+    AddEvent("START", "C", 0)
+        (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".js "))
+        ("Start a JavaScript");
+
+    AddEvent("EXECUTE", "C", 0)
+        (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".x "))
+        ("Execute a batch script");
+
+    AddEvent("STOP", "C")
+        (bind(&StateMachineDimControl::StopScript, this))
+        ("Stop a runnning batch script or JavaScript");
+}
+
+StateMachineDimControl::~StateMachineDimControl()
+{
+    for (auto it=fDimDescriptionsList.begin(); it!=fDimDescriptionsList.end(); it++)
+        delete *it;
+}
+
+int StateMachineDimControl::EvalOptions(Configuration &conf)
+{
+    fDebug = conf.Get<bool>("debug");
+    fUser  = conf.Get<string>("user");
+    fScriptUser = fUser;
+
+    // fVerbosity = 40;
+
+    // if (conf.Has("verbosity"))
+    //     fVerbosity = conf.Get<uint32_t>("verbosity");
+
+    // if (conf.Get<bool>("quiet"))
+    //     fVerbosity = 90;
+
+    const int cnt = conf.Get<bool>("stop")+conf.Has("start")+conf.Has("batch");
+
+    if (fIsServer)
+    {
+        if (cnt>0)
+        {
+            Error("--start, --batch, --stop are mutually exclusive to --server.");
+            return 2;
+        }
+        return -1;
+    }
+
+    if (cnt>1)
+    {
+        Error("--start, --batch and --stop are all mutually exclusive.");
+        return 1;
+    }
+
+    if (conf.Get<bool>("stop"))
+        return Dim::SendCommand("DIM_CONTROL/STOP", fUser) + 1;
+
+    if (conf.Has("start"))
+        return Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user="+fUser) + 1;
+
+    if (conf.Has("batch"))
+        return Dim::SendCommand("DIM_CONTROL/EXECUTE", conf.Get<string>("batch")+" user="+fUser) + 1;
+
+    return -1;
+}
Index: trunk/FACT++/src/StateMachineDimControl.h
===================================================================
--- trunk/FACT++/src/StateMachineDimControl.h	(revision 14132)
+++ trunk/FACT++/src/StateMachineDimControl.h	(revision 14132)
@@ -0,0 +1,72 @@
+#ifndef FACT_StateMachineDimControl
+#define FACT_StateMachineDimControl
+
+#include <set>
+
+#include "DimState.h"
+#include "StateMachineDim.h"
+
+class Configuration;
+
+class StateMachineDimControl : public StateMachineDim
+{
+    std::mutex fMutex;
+
+    std::vector<DimDescriptions*> fDimDescriptionsList;
+
+    std::set<std::string> fServerList;
+    std::set<Service> fServiceList;
+    std::map<std::string, std::vector<std::string>> fCommandList;
+    std::map<std::string, std::pair<int32_t,  std::string>> fCurrentStateList;
+    std::map<std::pair<std::string, int32_t>, std::pair<std::string, std::string>> fStateDescriptionList;
+    std::map<std::string, std::vector<Description>> fServiceDescriptionList;
+
+    DimVersion fDim;
+    DimDnsServiceList fDimList;
+
+    int  fVerbosity;
+    bool fDebug;
+
+    std::string fUser;
+    std::string fScriptUser;
+
+    std::string Line(const std::string &txt, char fill);
+
+public:
+    static bool fIsServer;
+
+    int ChangeState(int qos, const Time &time=Time(), int scriptdepth=-1, std::string scriptfile="", std::string user="");
+    int Write(const Time &time, const std::string &txt, int qos=kMessage);
+
+    int StartScript(const EventImp &imp, const std::string &cmd);
+    int StopScript();
+
+    int HandleStateChange(const std::string &server, DimDescriptions *state);
+    int HandleDescriptions(DimDescriptions *state);
+    int HandleStates(const std::string &server, DimDescriptions *state);
+    int HandleServerAdd(const std::string &server);
+    int HandleServerRemove(const std::string &server);
+    int HandleAddService(const Service &svc);
+
+    bool HasServer(const std::string &server);
+
+    std::vector<std::string> GetServerList();
+    std::vector<std::string> GetCommandList(const std::string &server);
+    std::vector<std::string> GetCommandList();
+    std::vector<Description> GetDescription(const std::string &service);
+
+    int PrintStates(std::ostream &out, const std::string &serv="");
+    int PrintDescription(std::ostream &out, bool iscmd, const std::string &serv="", const std::string &service="");
+
+    std::pair<int32_t, std::string> GetServerState(const std::string &server);
+
+    void SendDimCommand(const std::string &server, std::string str, std::ostream &lout);
+
+public:
+    StateMachineDimControl(std::ostream &out=std::cout);
+    ~StateMachineDimControl();
+
+    int EvalOptions(Configuration &conf);
+};
+
+#endif
