LCOV - code coverage report
Current view: top level - src - mjsonrpc.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 3187 0
Test Date: 2025-11-11 10:26:08 Functions: 0.0 % 114 0

            Line data    Source code
       1              : /********************************************************************\
       2              : 
       3              :   Name:         mjsonrpc.cxx
       4              :   Created by:   Konstantin Olchanski
       5              : 
       6              :   Contents:     handler of MIDAS standard JSON-RPC requests
       7              : 
       8              : \********************************************************************/
       9              : 
      10              : #undef NDEBUG // midas required assert() to be always enabled
      11              : 
      12              : #include <stdio.h>
      13              : #include <stdlib.h>
      14              : #include <assert.h>
      15              : #include <map>
      16              : 
      17              : #include "mjson.h"
      18              : #include "midas.h"
      19              : #include "msystem.h"
      20              : #include "mstrlcpy.h"
      21              : 
      22              : #include "mjsonrpc.h"
      23              : 
      24              : #include <mutex> // std::mutex
      25              : 
      26              : //////////////////////////////////////////////////////////////////////
      27              : //
      28              : // Specifications for JSON-RPC
      29              : //
      30              : // https://tools.ietf.org/html/rfc4627 - JSON RFC
      31              : // http://www.jsonrpc.org/specification - specification of JSON-RPC 2.0
      32              : // http://www.simple-is-better.org/json-rpc/transport_http.html
      33              : //
      34              : // NB - MIDAS JSON (odb.c and mjson.cxx) encode IEEE754/854 numeric values
      35              : //      NaN and +/-Inf into JSON strings "NaN", "Infinity" and "-Infinity"
      36              : //      for reasons unknown, the JSON standard does not specify a standard
      37              : //      way for encoding these numeric values.
      38              : //
      39              : // NB - Batch requests are processed in order and the returned array of responses
      40              : //      has the resonses in exactly same order as the requests for simpler
      41              : //      matching of requests and responses - 1st response to 1st request,
      42              : //      2nd response to 2nd request and so forth.
      43              : //
      44              : //////////////////////////////////////////////////////////////////////
      45              : //
      46              : // JSON-RPC error codes:
      47              : //
      48              : // -32700       Parse error     Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.
      49              : // -32600       Invalid Request The JSON sent is not a valid Request object.
      50              : // -32601       Method not found        The method does not exist / is not available.
      51              : // -32602       Invalid params  Invalid method parameter(s).
      52              : // -32603       Internal error  Internal JSON-RPC error.
      53              : // -32000 to -32099     Server error    Reserved for implementation-defined server-errors.
      54              : //
      55              : // Typical JSON-RPC request:
      56              : //
      57              : // {
      58              : //    "jsonrpc": "2.0",
      59              : //    "method": "sum",
      60              : //    "params": { "b": 34, "c": 56, "a": 12 },
      61              : //    "id": 123
      62              : // }
      63              : //
      64              : // Typical JSON-RPC reply:
      65              : //
      66              : // {
      67              : //    "jsonrpc": "2.0",
      68              : //    "result": 102,
      69              : //    "id": 5
      70              : // }
      71              : //
      72              : // Typical error reply:
      73              : //
      74              : // {
      75              : //    "jsonrpc": "2.0",
      76              : //    "error": {
      77              : //        "code": -32600,
      78              : //        "message": "Invalid Request.",
      79              : //        "data": "'method' is missing"
      80              : //        },
      81              : //    "id": 6
      82              : //    }
      83              : // }
      84              : //
      85              : //////////////////////////////////////////////////////////////////////
      86              : //
      87              : // JSON-RPC is documented via an automatically generated JSON Schema.
      88              : //
      89              : // For more information about JSON Schemas, see:
      90              : //
      91              : // https://tools.ietf.org/html/draft-zyp-json-schema-04
      92              : // http://spacetelescope.github.io/understanding-json-schema/
      93              : // http://json-schema.org/
      94              : //
      95              : // JSON Schema examples:
      96              : // http://json-schema.org/examples.html
      97              : // http://json-schema.org/example1.html
      98              : //
      99              : // JSON Schema visualization: (schema file has to have a .json extension)
     100              : // https://github.com/lbovet/docson
     101              : //
     102              : // Non-standard proposed JSON-RPC schema is *NOT* used: (no visualization tools)
     103              : // http://www.simple-is-better.org/json-rpc/jsonrpc20-schema-service-descriptor.html
     104              : //
     105              : // Variances of MIDAS JSON-RPC Schema from standard:
     106              : //
     107              : // - optional parameters end with "?" and have an "optional:true" attribute, i.e. "bUnique?"
     108              : // - array parameters end with "[]", JSON Schema array schema is not generated yet
     109              : //
     110              : //////////////////////////////////////////////////////////////////////
     111              : 
     112              : int mjsonrpc_debug = 0; // in mjsonrpc.h
     113              : static int mjsonrpc_sleep = 0;
     114              : static int mjsonrpc_time = 0;
     115              : 
     116            0 : static double GetTimeSec()
     117              : {
     118              :    struct timeval tv;
     119            0 :    gettimeofday(&tv, NULL);
     120            0 :    return tv.tv_sec*1.0 + tv.tv_usec/1000000.0;
     121              : }
     122              : 
     123            0 : MJsonNode* mjsonrpc_make_error(int code, const char* message, const char* data)
     124              : {
     125            0 :    MJsonNode* errnode = MJsonNode::MakeObject();
     126            0 :    errnode->AddToObject("code",    MJsonNode::MakeInt(code));
     127            0 :    errnode->AddToObject("message", MJsonNode::MakeString(message));
     128            0 :    errnode->AddToObject("data",    MJsonNode::MakeString(data));
     129              : 
     130            0 :    MJsonNode* result = MJsonNode::MakeObject();
     131            0 :    result->AddToObject("error", errnode);
     132            0 :    return result;
     133              : }
     134              : 
     135            0 : MJsonNode* mjsonrpc_make_result(MJsonNode* node)
     136              : {
     137            0 :    MJsonNode* result = MJsonNode::MakeObject();
     138            0 :    result->AddToObject("result", node);
     139            0 :    return result;
     140              : }
     141              : 
     142            0 : MJsonNode* mjsonrpc_make_result(const char* name, MJsonNode* value, const char* name2, MJsonNode* value2, const char* name3, MJsonNode* value3)
     143              : {
     144            0 :    MJsonNode* node = MJsonNode::MakeObject();
     145              : 
     146            0 :    if (name)
     147            0 :       node->AddToObject(name, value);
     148            0 :    if (name2)
     149            0 :       node->AddToObject(name2, value2);
     150            0 :    if (name3)
     151            0 :       node->AddToObject(name3, value3);
     152              : 
     153            0 :    MJsonNode* result = MJsonNode::MakeObject();
     154            0 :    result->AddToObject("result", node);
     155            0 :    return result;
     156              : }
     157              : 
     158            0 : MJsonNode* mjsonrpc_make_result(const char* name, MJsonNode* value, const char* name2, MJsonNode* value2, const char* name3, MJsonNode* value3, const char* name4, MJsonNode* value4)
     159              : {
     160            0 :    MJsonNode* node = MJsonNode::MakeObject();
     161              : 
     162            0 :    if (name)
     163            0 :       node->AddToObject(name, value);
     164            0 :    if (name2)
     165            0 :       node->AddToObject(name2, value2);
     166            0 :    if (name3)
     167            0 :       node->AddToObject(name3, value3);
     168            0 :    if (name4)
     169            0 :       node->AddToObject(name4, value4);
     170              : 
     171            0 :    MJsonNode* result = MJsonNode::MakeObject();
     172            0 :    result->AddToObject("result", node);
     173            0 :    return result;
     174              : }
     175              : 
     176              : static MJsonNode* gNullNode = NULL;
     177              : 
     178            0 : const MJsonNode* mjsonrpc_get_param(const MJsonNode* params, const char* name, MJsonNode** error)
     179              : {
     180            0 :    assert(gNullNode != NULL);
     181              : 
     182              :    // NULL params is a request for documentation, return an empty object
     183            0 :    if (!params) {
     184            0 :       if (error)
     185            0 :          *error = MJsonNode::MakeObject();
     186            0 :       return gNullNode;
     187              :    }
     188              : 
     189            0 :    const MJsonNode* obj = params->FindObjectNode(name);
     190            0 :    if (!obj) {
     191            0 :       if (error)
     192            0 :          *error = mjsonrpc_make_error(-32602, "Invalid params", (std::string("missing parameter: ") + name).c_str());
     193            0 :       return gNullNode;
     194              :    }
     195              : 
     196            0 :    if (error)
     197            0 :       *error = NULL;
     198            0 :    return obj;
     199              : }
     200              : 
     201            0 : const MJsonNodeVector* mjsonrpc_get_param_array(const MJsonNode* params, const char* name, MJsonNode** error)
     202              : {
     203              :    // NULL params is a request for documentation, return NULL
     204            0 :    if (!params) {
     205            0 :       if (error)
     206            0 :          *error = MJsonNode::MakeObject();
     207            0 :       return NULL;
     208              :    }
     209              : 
     210            0 :    const MJsonNode* node = mjsonrpc_get_param(params, name, error);
     211              : 
     212              :    // handle error return from mjsonrpc_get_param()
     213            0 :    if (error && *error) {
     214            0 :       return NULL;
     215              :    }
     216              : 
     217            0 :    const MJsonNodeVector* v = node->GetArray();
     218              : 
     219            0 :    if (!v) {
     220            0 :       if (error)
     221            0 :          *error = mjsonrpc_make_error(-32602, "Invalid params", (std::string("parameter must be an array: ") + name).c_str());
     222            0 :       return NULL;
     223              :    }
     224              : 
     225            0 :    if (error)
     226            0 :       *error = NULL;
     227            0 :    return v;
     228              : }
     229              : 
     230            0 : MJSO* MJSO::MakeObjectSchema(const char* description) // constructor for object schema
     231              : {
     232            0 :    MJSO* p = new MJSO();
     233            0 :    if (description)
     234            0 :       p->AddToObject("description", MJsonNode::MakeString(description));
     235            0 :    p->AddToObject("type", MJsonNode::MakeString("object"));
     236            0 :    p->properties = MJsonNode::MakeObject();
     237            0 :    p->required = MJsonNode::MakeArray();
     238            0 :    p->AddToObject("properties", p->properties);
     239            0 :    p->AddToObject("required", p->required);
     240            0 :    return p;
     241              : }
     242              : 
     243            0 : MJSO* MJSO::MakeArraySchema(const char* description) // constructor for array schema
     244              : {
     245            0 :    MJSO* p = new MJSO();
     246            0 :    p->AddToObject("description", MJsonNode::MakeString(description));
     247            0 :    p->AddToObject("type", MJsonNode::MakeString("array"));
     248            0 :    p->items = MJsonNode::MakeArray();
     249            0 :    p->AddToObject("items", p->items);
     250            0 :    return p;
     251              : }
     252              : 
     253            0 : static std::string remove(const std::string s, char c)
     254              : {
     255            0 :    std::string::size_type pos = s.find(c);
     256            0 :    if (pos == std::string::npos)
     257            0 :       return s;
     258              :    else
     259            0 :       return s.substr(0, pos);
     260              : }
     261              : 
     262            0 : void MJSO::AddToSchema(MJsonNode* s, const char* xname)
     263              : {
     264            0 :    if (!xname)
     265            0 :       xname = "";
     266              : 
     267            0 :    bool optional = strchr(xname, '?');
     268            0 :    bool array = strchr(xname, '[');
     269              : 
     270              :    // remove the "?" and "[]" marker characters
     271            0 :    std::string name = xname;
     272            0 :    name = remove(name, '?');
     273            0 :    name = remove(name, '[');
     274            0 :    name = remove(name, ']');
     275              : 
     276            0 :    if (optional)
     277            0 :       s->AddToObject("optional", MJsonNode::MakeBool(true));
     278              : 
     279            0 :    if (array) { // insert an array schema
     280            0 :       MJSO* ss = MakeArraySchema(s->FindObjectNode("description")->GetString().c_str());
     281            0 :       s->DeleteObjectNode("description");
     282            0 :       ss->AddToSchema(s, "");
     283            0 :       s = ss;
     284              :    }
     285              : 
     286            0 :    if (items)
     287            0 :       items->AddToArray(s);
     288              :    else {
     289            0 :       assert(properties);
     290            0 :       assert(required);
     291            0 :       properties->AddToObject(name.c_str(), s);
     292            0 :       if (!optional) {
     293            0 :          required->AddToArray(MJsonNode::MakeString(name.c_str()));
     294              :       }
     295              :    }
     296            0 : }
     297              : 
     298            0 : MJSO* MJSO::I()
     299              : {
     300            0 :    return MakeObjectSchema(NULL);
     301              : }
     302              : 
     303            0 : void MJSO::D(const char* description)
     304              : {
     305            0 :    this->AddToObject("description", MJsonNode::MakeString(description));
     306            0 : }
     307              : 
     308            0 : MJSO* MJSO::Params()
     309              : {
     310            0 :    if (!params) {
     311            0 :       params = MakeObjectSchema(NULL);
     312            0 :       this->AddToSchema(params, "params");
     313              :    }
     314            0 :    return params;
     315              : }
     316              : 
     317            0 : MJSO* MJSO::Result()
     318              : {
     319            0 :    if (!result) {
     320            0 :       result = MakeObjectSchema(NULL);
     321            0 :       this->AddToSchema(result, "result");
     322              :    }
     323            0 :    return result;
     324              : }
     325              : 
     326            0 : MJSO* MJSO::PA(const char* description)
     327              : {
     328            0 :    MJSO* s = MakeArraySchema(description);
     329            0 :    this->AddToSchema(s, "params");
     330            0 :    return s;
     331              : }
     332              : 
     333            0 : MJSO* MJSO::RA(const char* description)
     334              : {
     335            0 :    MJSO* s = MakeArraySchema(description);
     336            0 :    this->AddToSchema(s, "result");
     337            0 :    return s;
     338              : }
     339              : 
     340            0 : void MJSO::P(const char* name, int mjson_type, const char* description)
     341              : {
     342            0 :    if (name == NULL)
     343            0 :       this->Add("params", mjson_type, description);
     344              :    else
     345            0 :       Params()->Add(name, mjson_type, description);
     346            0 : }
     347              : 
     348            0 : void MJSO::R(const char* name, int mjson_type, const char* description)
     349              : {
     350            0 :    if (name == NULL)
     351            0 :       this->Add("result", mjson_type, description);
     352              :    else
     353            0 :       Result()->Add(name, mjson_type, description);
     354            0 : }
     355              : 
     356            0 : void MJSO::Add(const char* name, int mjson_type, const char* description)
     357              : {
     358            0 :    MJsonNode* p = MJsonNode::MakeObject();
     359            0 :    p->AddToObject("description", MJsonNode::MakeString(description));
     360            0 :    if (mjson_type == MJSON_ARRAY)
     361            0 :       p->AddToObject("type", MJsonNode::MakeString("array"));
     362            0 :    else if (mjson_type == MJSON_OBJECT)
     363            0 :       p->AddToObject("type", MJsonNode::MakeString("object"));
     364            0 :    else if (mjson_type == MJSON_STRING)
     365            0 :       p->AddToObject("type", MJsonNode::MakeString("string"));
     366            0 :    else if (mjson_type == MJSON_INT)
     367            0 :       p->AddToObject("type", MJsonNode::MakeString("integer"));
     368            0 :    else if (mjson_type == MJSON_NUMBER)
     369            0 :       p->AddToObject("type", MJsonNode::MakeString("number"));
     370            0 :    else if (mjson_type == MJSON_BOOL)
     371            0 :       p->AddToObject("type", MJsonNode::MakeString("bool"));
     372            0 :    else if (mjson_type == MJSON_NULL)
     373            0 :       p->AddToObject("type", MJsonNode::MakeString("null"));
     374            0 :    else if (mjson_type == MJSON_ARRAYBUFFER)
     375            0 :       p->AddToObject("type", MJsonNode::MakeString("arraybuffer"));
     376            0 :    else if (mjson_type == MJSON_JSON)
     377            0 :       p->AddToObject("type", MJsonNode::MakeString("json"));
     378            0 :    else if (mjson_type == 0)
     379              :       ;
     380              :    else
     381            0 :       assert(!"invalid value of mjson_type");
     382            0 :    this->AddToSchema(p, name);
     383            0 : }
     384              : 
     385            0 : MJSO* MJSO::AddObject(const char* name, const char* description)
     386              : {
     387            0 :    MJSO* s = MakeObjectSchema(description);
     388            0 :    s->AddToObject("description", MJsonNode::MakeString(description));
     389            0 :    s->AddToObject("type", MJsonNode::MakeString("object"));
     390            0 :    this->AddToSchema(s, name);
     391            0 :    return s;
     392              : }
     393              : 
     394            0 : MJSO* MJSO::AddArray(const char* name, const char* description)
     395              : {
     396            0 :    MJSO* s = MakeArraySchema(description);
     397            0 :    s->AddToObject("description", MJsonNode::MakeString(description));
     398            0 :    s->AddToObject("type", MJsonNode::MakeString("array"));
     399            0 :    this->AddToSchema(s, name);
     400            0 :    return s;
     401              : }
     402              : 
     403            0 : MJSO::MJSO() // ctor
     404            0 :    : MJsonNode(MJSON_OBJECT)
     405              : {
     406            0 :    properties = NULL;
     407            0 :    required = NULL;
     408            0 :    items = NULL;
     409            0 :    params = NULL;
     410            0 :    result = NULL;
     411            0 : }
     412              : 
     413            0 : static MJsonNode* xnull(const MJsonNode* params)
     414              : {
     415            0 :    if (!params) {
     416            0 :       MJSO* doc = MJSO::I();
     417            0 :       doc->D("RPC method always returns null");
     418            0 :       doc->P(NULL, 0, "method parameters are ignored");
     419            0 :       doc->R(NULL, MJSON_NULL, "always returns null");
     420            0 :       return doc;
     421              :    }
     422              : 
     423            0 :    return mjsonrpc_make_result(MJsonNode::MakeNull());
     424              : }
     425              : 
     426              : /////////////////////////////////////////////////////////////////////////////////
     427              : //
     428              : // Programs start/stop code goes here
     429              : //
     430              : /////////////////////////////////////////////////////////////////////////////////
     431              : 
     432            0 : static MJsonNode* js_cm_exist(const MJsonNode* params)
     433              : {
     434            0 :    if (!params) {
     435            0 :       MJSO* doc = MJSO::I();
     436            0 :       doc->D("calls MIDAS cm_exist() to check if given MIDAS program is running");
     437            0 :       doc->P("name", MJSON_STRING, "name of the program, corresponding to ODB /Programs/name");
     438            0 :       doc->P("unique?", MJSON_BOOL, "bUnique argument to cm_exist()");
     439            0 :       doc->R("status", MJSON_INT, "return status of cm_exist()");
     440            0 :       return doc;
     441              :    }
     442              : 
     443            0 :    MJsonNode* error = NULL;
     444              : 
     445            0 :    std::string name = mjsonrpc_get_param(params, "name", &error)->GetString();
     446            0 :    if (error)
     447            0 :       return error;
     448              : 
     449            0 :    int unique = mjsonrpc_get_param(params, "unique", NULL)->GetBool();
     450              : 
     451            0 :    int status = cm_exist(name.c_str(), unique);
     452              : 
     453            0 :    if (mjsonrpc_debug)
     454            0 :       printf("cm_exist(%s,%d) -> %d\n", name.c_str(), unique, status);
     455              : 
     456            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
     457            0 : }
     458              : 
     459            0 : static MJsonNode* js_cm_shutdown(const MJsonNode* params)
     460              : {
     461            0 :    if (!params) {
     462            0 :       MJSO *doc = MJSO::I();
     463            0 :       doc->D("calls MIDAS cm_shutdown() to stop given MIDAS program");
     464            0 :       doc->P("name", MJSON_STRING, "name of the program, corresponding to ODB /Programs/name");
     465            0 :       doc->P("unique?", MJSON_BOOL, "bUnique argument to cm_shutdown()");
     466            0 :       doc->R("status", MJSON_INT, "return status of cm_shutdown()");
     467            0 :       return doc;
     468              :    }
     469              : 
     470            0 :    MJsonNode* error = NULL;
     471              : 
     472            0 :    std::string name = mjsonrpc_get_param(params, "name", &error)->GetString();
     473            0 :    if (error)
     474            0 :       return error;
     475              : 
     476            0 :    int unique = mjsonrpc_get_param(params, "unique", NULL)->GetBool();
     477              : 
     478            0 :    int status = cm_shutdown(name.c_str(), unique);
     479              : 
     480            0 :    if (mjsonrpc_debug)
     481            0 :       printf("cm_shutdown(%s,%d) -> %d\n", name.c_str(), unique, status);
     482              : 
     483            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
     484            0 : }
     485              : 
     486            0 : static MJsonNode* start_program(const MJsonNode* params)
     487              : {
     488            0 :    if (!params) {
     489            0 :       MJSO* doc = MJSO::I();
     490            0 :       doc->D("start MIDAS program defined in ODB /Programs/name");
     491            0 :       doc->P("name", MJSON_STRING, "name of the program, corresponding to ODB /Programs/name");
     492            0 :       doc->R("status", MJSON_INT, "return status of ss_system()");
     493            0 :       return doc;
     494              :    }
     495              : 
     496            0 :    MJsonNode* error = NULL;
     497              : 
     498            0 :    std::string name = mjsonrpc_get_param(params, "name", &error)->GetString(); if (error) return error;
     499              : 
     500            0 :    std::string path = "";
     501            0 :    path += "/Programs/";
     502            0 :    path += name;
     503            0 :    path += "/Start command";
     504              : 
     505              :    HNDLE hDB;
     506            0 :    cm_get_experiment_database(&hDB, NULL);
     507              : 
     508              :    char command[256];
     509            0 :    int size = sizeof(command);
     510            0 :    int status = db_get_value(hDB, 0, path.c_str(), command, &size, TID_STRING, FALSE);
     511              : 
     512            0 :    if (status == DB_SUCCESS && command[0]) {
     513            0 :       status = ss_system(command);
     514              :    }
     515              : 
     516            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
     517            0 : }
     518              : 
     519            0 : static MJsonNode* exec_script(const MJsonNode* params)
     520              : {
     521            0 :    if (!params) {
     522            0 :       MJSO* doc = MJSO::I();
     523            0 :       doc->D("execute custom script defined in ODB /Script (scripts show in the menu) or /CustomScript (scripts from custom pages)");
     524            0 :       doc->P("script?", MJSON_STRING, "Execute ODB /Script/xxx");
     525            0 :       doc->P("customscript?", MJSON_STRING, "Execute ODB /CustomScript/xxx");
     526            0 :       doc->R("status", MJSON_INT, "return status of cm_exec_script()");
     527            0 :       return doc;
     528              :    }
     529              : 
     530            0 :    std::string script = mjsonrpc_get_param(params, "script", NULL)->GetString();
     531            0 :    std::string customscript = mjsonrpc_get_param(params, "customscript", NULL)->GetString();
     532              : 
     533            0 :    std::string path;
     534              :    
     535            0 :    if (script.length() > 0) {
     536            0 :       path += "/Script";
     537            0 :       path += "/";
     538            0 :       path += script;
     539            0 :    } else if (customscript.length() > 0) {
     540            0 :       path += "/CustomScript";
     541            0 :       path += "/";
     542            0 :       path += customscript;
     543              :    }
     544              : 
     545            0 :    int status = 0;
     546              : 
     547            0 :    if (path.length() > 0) {
     548            0 :       status = cm_exec_script(path.c_str());
     549              :    }
     550              : 
     551            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
     552            0 : }
     553              : 
     554              : /////////////////////////////////////////////////////////////////////////////////
     555              : //
     556              : // ODB code goes here
     557              : //
     558              : /////////////////////////////////////////////////////////////////////////////////
     559              : 
     560            0 : static int parse_array_index_list(const char* method, const char* path, std::vector<unsigned> *list)
     561              : {
     562              :    // parse array index in form of:
     563              :    // odbpath[number]
     564              :    // odbpath[number,number]
     565              :    // odbpath[number-number]
     566              :    // or any combination of them, i.e. odbpath[1,10-15,20,30-40]
     567              : 
     568            0 :    const char*s = strchr(path, '[');
     569              : 
     570            0 :    if (!s) {
     571            0 :       cm_msg(MERROR, method, "expected an array index character \'[\' in \"%s\"", path);
     572            0 :       return DB_OUT_OF_RANGE;
     573              :    }
     574              :   
     575            0 :    s++; // skip '[' itself
     576              : 
     577            0 :    while (s && (*s != 0)) {
     578              : 
     579              :       // check that we have a number
     580            0 :       if (!isdigit(*s)) {
     581            0 :          cm_msg(MERROR, method, "expected a number in array index in \"%s\" at \"%s\"", path, s);
     582            0 :          return DB_OUT_OF_RANGE;
     583              :       }
     584              : 
     585            0 :       unsigned value1 = strtoul(s, (char**)&s, 10);
     586              :       
     587              :       // array range, 
     588            0 :       if (*s == '-') {
     589            0 :          s++; // skip the minus char
     590              : 
     591            0 :          if (!isdigit(*s)) {
     592            0 :             cm_msg(MERROR, method, "expected a number in array index in \"%s\" at \"%s\"", path, s);
     593            0 :             return DB_OUT_OF_RANGE;
     594              :          }
     595              : 
     596            0 :          unsigned value2 = strtoul(s, (char**)&s, 10);
     597              : 
     598            0 :          if (value2 >= value1)
     599            0 :             for (unsigned i=value1; i<=value2; i++)
     600            0 :                list->push_back(i);
     601              :          else {
     602              :             // this is stupid. simple loop like this
     603              :             // for (unsigned i=value1; i>=value2; i--)
     604              :             // does not work for range 4-0, because value2 is 0,
     605              :             // and x>=0 is always true for unsigned numbers,
     606              :             // so we would loop forever... K.O.
     607            0 :             for (unsigned i=value1; i!=value2; i--)
     608            0 :                list->push_back(i);
     609            0 :             list->push_back(value2);
     610              :          }
     611              :       } else {
     612            0 :          list->push_back(value1);
     613              :       }
     614              : 
     615            0 :       if (*s == ',') {
     616            0 :          s++; // skip the comma char
     617            0 :          continue; // back to the begin of loop
     618              :       }
     619              : 
     620            0 :       if (*s == ']') {
     621            0 :          s++; // skip the closing bracket
     622            0 :          s = NULL;
     623            0 :          continue; // done
     624              :       }
     625              : 
     626            0 :       cm_msg(MERROR, method, "invalid char in array index in \"%s\" at \"%s\"", path, s);
     627            0 :       return DB_OUT_OF_RANGE;
     628              :    }
     629              : 
     630              : #if 0
     631              :    printf("parsed array indices for \"%s\" size is %d: ", path, (int)list->size());
     632              :    for (unsigned i=0; i<list->size(); i++)
     633              :       printf(" %d", (*list)[i]);
     634              :    printf("\n");
     635              : #endif
     636              : 
     637            0 :    return SUCCESS;
     638              : }
     639              : 
     640            0 : static MJsonNode* js_db_get_values(const MJsonNode* params)
     641              : {
     642            0 :    if (!params) {
     643            0 :       MJSO* doc = MJSO::I();
     644            0 :       doc->D("get values of ODB data from given subtrees");
     645            0 :       doc->P("paths[]", MJSON_STRING, "array of ODB subtree paths, see note on array indices");
     646            0 :       doc->P("omit_names?", MJSON_BOOL, "omit the /name entries");
     647            0 :       doc->P("omit_last_written?", MJSON_BOOL, "omit the /last_written entries and the last_written[] result");
     648            0 :       doc->P("omit_tid?", MJSON_BOOL, "omit the tid[] result");
     649            0 :       doc->P("omit_old_timestamp?", MJSON_NUMBER, "omit data older than given ODB timestamp");
     650            0 :       doc->P("preserve_case?", MJSON_BOOL, "preserve the capitalization of ODB key names (WARNING: ODB is not case sensitive); note that this will also have side effect of setting the omit_names option");
     651            0 :       doc->R("data[]", 0, "values of ODB data for each path, all key names are in lower case, all symlinks are followed");
     652            0 :       doc->R("status[]", MJSON_INT, "return status of db_copy_json_values() or db_copy_json_index() for each path");
     653            0 :       doc->R("tid?[]", MJSON_INT, "odb type id for each path, absent if omit_tid is true");
     654            0 :       doc->R("last_written?[]", MJSON_NUMBER, "last_written value of the ODB subtree for each path, absent if omit_last_written is true");
     655            0 :       return doc;
     656              :    }
     657              : 
     658            0 :    MJsonNode* error = NULL;
     659              : 
     660            0 :    const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
     661              : 
     662            0 :    bool omit_names = mjsonrpc_get_param(params, "omit_names", NULL)->GetBool();
     663            0 :    bool omit_last_written = mjsonrpc_get_param(params, "omit_last_written", NULL)->GetBool();
     664            0 :    bool omit_tid = mjsonrpc_get_param(params, "omit_tid", NULL)->GetBool();
     665            0 :    double xomit_old_timestamp = mjsonrpc_get_param(params, "omit_old_timestamp", NULL)->GetDouble();
     666            0 :    time_t omit_old_timestamp = (time_t)xomit_old_timestamp;
     667            0 :    bool preserve_case = mjsonrpc_get_param(params, "preserve_case", NULL)->GetBool();
     668              :    
     669            0 :    MJsonNode* dresult = MJsonNode::MakeArray();
     670            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
     671            0 :    MJsonNode* tresult = MJsonNode::MakeArray();
     672            0 :    MJsonNode* lwresult = MJsonNode::MakeArray();
     673              : 
     674              :    HNDLE hDB;
     675            0 :    cm_get_experiment_database(&hDB, NULL);
     676              : 
     677            0 :    for (unsigned i=0; i<paths->size(); i++) {
     678            0 :       int status = 0;
     679              :       HNDLE hkey;
     680              :       KEY key;
     681            0 :       std::string path = (*paths)[i]->GetString();
     682              : 
     683            0 :       status = db_find_key(hDB, 0, path.c_str(), &hkey);
     684            0 :       if (status != DB_SUCCESS) {
     685            0 :          dresult->AddToArray(MJsonNode::MakeNull());
     686            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
     687            0 :          tresult->AddToArray(MJsonNode::MakeNull());
     688            0 :          lwresult->AddToArray(MJsonNode::MakeNull());
     689            0 :          continue;
     690              :       }
     691              : 
     692            0 :       status = db_get_key(hDB, hkey, &key);
     693            0 :       if (status != DB_SUCCESS) {
     694            0 :          dresult->AddToArray(MJsonNode::MakeNull());
     695            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
     696            0 :          tresult->AddToArray(MJsonNode::MakeNull());
     697            0 :          lwresult->AddToArray(MJsonNode::MakeNull());
     698            0 :          continue;
     699              :       }
     700              : 
     701            0 :       if (path.find("[") != std::string::npos) {
     702            0 :          std::vector<unsigned> list;
     703            0 :          status = parse_array_index_list("js_db_get_values", path.c_str(), &list);
     704              : 
     705            0 :          if (status != SUCCESS) {
     706            0 :             dresult->AddToArray(MJsonNode::MakeNull());
     707            0 :             sresult->AddToArray(MJsonNode::MakeInt(status));
     708            0 :             tresult->AddToArray(MJsonNode::MakeInt(key.type));
     709            0 :             lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
     710            0 :             continue;
     711              :          }
     712              : 
     713            0 :          if (list.size() > 1) {
     714            0 :             MJsonNode *ddresult = MJsonNode::MakeArray();
     715            0 :             MJsonNode *ssresult = MJsonNode::MakeArray();
     716              : 
     717            0 :             for (unsigned i=0; i<list.size(); i++) {
     718            0 :                char* buf = NULL;
     719            0 :                int bufsize = 0;
     720            0 :                int end = 0;
     721              :                
     722            0 :                status = db_copy_json_index(hDB, hkey, list[i], &buf, &bufsize, &end);
     723            0 :                if (status == DB_SUCCESS) {
     724            0 :                   ss_repair_utf8(buf);
     725            0 :                   ddresult->AddToArray(MJsonNode::MakeJSON(buf));
     726            0 :                   ssresult->AddToArray(MJsonNode::MakeInt(status));
     727              :                } else {
     728            0 :                   ddresult->AddToArray(MJsonNode::MakeNull());
     729            0 :                   ssresult->AddToArray(MJsonNode::MakeInt(status));
     730              :                }
     731              : 
     732            0 :                if (buf)
     733            0 :                   free(buf);
     734              :             }
     735              : 
     736            0 :             dresult->AddToArray(ddresult);
     737            0 :             sresult->AddToArray(ssresult);
     738            0 :             tresult->AddToArray(MJsonNode::MakeInt(key.type));
     739            0 :             lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
     740              : 
     741              :          } else {
     742            0 :             char* buf = NULL;
     743            0 :             int bufsize = 0;
     744            0 :             int end = 0;
     745              :             
     746            0 :             status = db_copy_json_index(hDB, hkey, list[0], &buf, &bufsize, &end);
     747            0 :             if (status == DB_SUCCESS) {
     748            0 :                ss_repair_utf8(buf);
     749            0 :                dresult->AddToArray(MJsonNode::MakeJSON(buf));
     750            0 :                sresult->AddToArray(MJsonNode::MakeInt(status));
     751            0 :                tresult->AddToArray(MJsonNode::MakeInt(key.type));
     752            0 :                lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
     753              :             } else {
     754            0 :                dresult->AddToArray(MJsonNode::MakeNull());
     755            0 :                sresult->AddToArray(MJsonNode::MakeInt(status));
     756            0 :                tresult->AddToArray(MJsonNode::MakeInt(key.type));
     757            0 :                lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
     758              :             }
     759              :             
     760            0 :             if (buf)
     761            0 :                free(buf);
     762              :          }
     763            0 :       } else {
     764            0 :          char* buf = NULL;
     765            0 :          int bufsize = 0;
     766            0 :          int end = 0;
     767              : 
     768            0 :          status = db_copy_json_values(hDB, hkey, &buf, &bufsize, &end, omit_names,
     769              :                                       omit_last_written, omit_old_timestamp, preserve_case);
     770              : 
     771            0 :          if (status == DB_SUCCESS) {
     772            0 :             ss_repair_utf8(buf);
     773            0 :             dresult->AddToArray(MJsonNode::MakeJSON(buf));
     774            0 :             sresult->AddToArray(MJsonNode::MakeInt(status));
     775            0 :             tresult->AddToArray(MJsonNode::MakeInt(key.type));
     776            0 :             lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
     777              :          } else {
     778            0 :             dresult->AddToArray(MJsonNode::MakeNull());
     779            0 :             sresult->AddToArray(MJsonNode::MakeInt(status));
     780            0 :             tresult->AddToArray(MJsonNode::MakeInt(key.type));
     781            0 :             lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
     782              :          }
     783              : 
     784            0 :          if (buf)
     785            0 :             free(buf);
     786              :       }
     787            0 :    }
     788              : 
     789            0 :    MJsonNode* result = MJsonNode::MakeObject();
     790              : 
     791            0 :    result->AddToObject("data", dresult);
     792            0 :    result->AddToObject("status", sresult);
     793            0 :    if (!omit_tid)
     794            0 :       result->AddToObject("tid", tresult);
     795              :    else
     796            0 :       delete tresult;
     797            0 :    if (!omit_last_written)
     798            0 :       result->AddToObject("last_written", lwresult);
     799              :    else
     800            0 :       delete lwresult;
     801              : 
     802            0 :    return mjsonrpc_make_result(result);
     803              : }
     804              : 
     805            0 : static MJsonNode* js_db_ls(const MJsonNode* params)
     806              : {
     807            0 :    if (!params) {
     808            0 :       MJSO* doc = MJSO::I();
     809            0 :       doc->D("get contents of given ODB subdirectory in the \"ls\" json encoding - similar to odbedit command \"ls -l\"");
     810            0 :       doc->P("paths[]",  MJSON_STRING, "array of ODB subtree paths");
     811            0 :       doc->R("data[]",   MJSON_OBJECT, "keys and values of ODB data for each path");
     812            0 :       doc->R("status[]", MJSON_INT, "return status of db_copy_json_ls() for each path");
     813            0 :       return doc;
     814              :    }
     815              : 
     816            0 :    MJsonNode* error = NULL;
     817              : 
     818            0 :    const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
     819              : 
     820            0 :    MJsonNode* dresult = MJsonNode::MakeArray();
     821            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
     822              : 
     823              :    HNDLE hDB;
     824            0 :    cm_get_experiment_database(&hDB, NULL);
     825              : 
     826            0 :    for (unsigned i=0; i<paths->size(); i++) {
     827            0 :       int status = 0;
     828              :       HNDLE hkey;
     829            0 :       std::string path = (*paths)[i]->GetString();
     830              : 
     831            0 :       status = db_find_key(hDB, 0, path.c_str(), &hkey);
     832            0 :       if (status != DB_SUCCESS) {
     833            0 :          dresult->AddToArray(MJsonNode::MakeNull());
     834            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
     835            0 :          continue;
     836              :       }
     837              : 
     838            0 :       char* buf = NULL;
     839            0 :       int bufsize = 0;
     840            0 :       int end = 0;
     841              : 
     842            0 :       status = db_copy_json_ls(hDB, hkey, &buf, &bufsize, &end);
     843              : 
     844            0 :       if (status == DB_SUCCESS) {
     845            0 :          ss_repair_utf8(buf);
     846            0 :          dresult->AddToArray(MJsonNode::MakeJSON(buf));
     847            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
     848              :       } else {
     849            0 :          dresult->AddToArray(MJsonNode::MakeNull());
     850            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
     851              :       }
     852              : 
     853            0 :       if (buf)
     854            0 :          free(buf);
     855            0 :    }
     856              : 
     857            0 :    return mjsonrpc_make_result("data", dresult, "status", sresult);
     858              : }
     859              : 
     860            0 : static MJsonNode* js_db_copy(const MJsonNode* params)
     861              : {
     862            0 :    if (!params) {
     863            0 :       MJSO* doc = MJSO::I();
     864            0 :       doc->D("get complete ODB data in the \"save\" json encoding, suitable for reloading with odbedit command \"load\"");
     865            0 :       doc->P("paths[]",  MJSON_STRING, "array of ODB subtree paths");
     866            0 :       doc->R("data[]",   MJSON_OBJECT, "keys and values of ODB data for each path");
     867            0 :       doc->R("status[]", MJSON_INT, "return status of db_copy_json_save() for each path");
     868            0 :       return doc;
     869              :    }
     870              : 
     871            0 :    MJsonNode* error = NULL;
     872              : 
     873            0 :    const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
     874              : 
     875            0 :    MJsonNode* dresult = MJsonNode::MakeArray();
     876            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
     877              : 
     878              :    HNDLE hDB;
     879            0 :    cm_get_experiment_database(&hDB, NULL);
     880              : 
     881            0 :    for (unsigned i=0; i<paths->size(); i++) {
     882            0 :       int status = 0;
     883              :       HNDLE hkey;
     884            0 :       std::string path = (*paths)[i]->GetString();
     885              : 
     886            0 :       status = db_find_key(hDB, 0, path.c_str(), &hkey);
     887            0 :       if (status != DB_SUCCESS) {
     888            0 :          dresult->AddToArray(MJsonNode::MakeNull());
     889            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
     890            0 :          continue;
     891              :       }
     892              : 
     893            0 :       char* buf = NULL;
     894            0 :       int bufsize = 0;
     895            0 :       int end = 0;
     896              : 
     897            0 :       status = db_copy_json_save(hDB, hkey, &buf, &bufsize, &end);
     898              : 
     899            0 :       if (status == DB_SUCCESS) {
     900            0 :          ss_repair_utf8(buf);
     901            0 :          dresult->AddToArray(MJsonNode::MakeJSON(buf));
     902            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
     903              :       } else {
     904            0 :          dresult->AddToArray(MJsonNode::MakeNull());
     905            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
     906              :       }
     907              : 
     908            0 :       if (buf)
     909            0 :          free(buf);
     910            0 :    }
     911              : 
     912            0 :    return mjsonrpc_make_result("data", dresult, "status", sresult);
     913              : }
     914              : 
     915            0 : static MJsonNode* js_db_paste(const MJsonNode* params)
     916              : {
     917            0 :    if (!params) {
     918            0 :       MJSO* doc = MJSO::I();
     919            0 :       doc->D("write data into ODB");
     920            0 :       doc->P("paths[]", MJSON_STRING, "array of ODB subtree paths, see note on array indices");
     921            0 :       doc->P("values[]", 0, "array of data values written to ODB via db_paste_json() for each path");
     922            0 :       doc->R("status[]", MJSON_INT, "array of return status of db_paste_json() for each path");
     923            0 :       return doc;
     924              :    }
     925              : 
     926            0 :    MJsonNode* error = NULL;
     927              : 
     928            0 :    const MJsonNodeVector* paths  = mjsonrpc_get_param_array(params, "paths",  &error); if (error) return error;
     929            0 :    const MJsonNodeVector* values = mjsonrpc_get_param_array(params, "values", &error); if (error) return error;
     930              : 
     931            0 :    if (paths->size() != values->size()) {
     932            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "paths and values should have the same length");
     933              :    }
     934              : 
     935            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
     936              : 
     937              :    HNDLE hDB;
     938            0 :    cm_get_experiment_database(&hDB, NULL);
     939              : 
     940            0 :    for (unsigned i=0; i<paths->size(); i++) {
     941            0 :       int status = 0;
     942              :       HNDLE hkey;
     943            0 :       std::string path = (*paths)[i]->GetString();
     944              : 
     945            0 :       status = db_find_key(hDB, 0, path.c_str(), &hkey);
     946            0 :       if (status != DB_SUCCESS) {
     947            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
     948            0 :          continue;
     949              :       }
     950              : 
     951            0 :       const MJsonNode* v = (*values)[i];
     952            0 :       assert(v != NULL);
     953              : 
     954            0 :       if (path.find("[*]") != std::string::npos) {
     955              : 
     956              :          KEY key;
     957            0 :          db_get_key(hDB, hkey, &key);
     958            0 :          for (int j=0 ; j<key.num_values ; j++)
     959            0 :             status = db_paste_json_node(hDB, hkey, j, v);
     960              : 
     961            0 :       } else if (path.find("[") != std::string::npos) {
     962            0 :          std::vector<unsigned> list;
     963            0 :          status = parse_array_index_list("js_db_paste", path.c_str(), &list);
     964              : 
     965            0 :          if (status != SUCCESS) {
     966            0 :             sresult->AddToArray(MJsonNode::MakeInt(status));
     967            0 :             continue;
     968              :          }
     969              : 
     970              :          // supported permutations of array indices and data values:
     971              :          // single index: intarray[1] -> data should be a single value: MJSON_ARRAY is rejected right here, MJSON_OBJECT is rejected by db_paste
     972              :          // multiple index intarray[1,2,3] -> data should be an array of equal length, or
     973              :          // multiple index intarray[1,2,3] -> if data is a single value, all array elements are set to this same value
     974              :          
     975            0 :          if (list.size() < 1) {
     976            0 :             cm_msg(MERROR, "js_db_paste", "invalid array indices for array path \"%s\"", path.c_str());
     977            0 :             sresult->AddToArray(MJsonNode::MakeInt(DB_TYPE_MISMATCH));
     978            0 :             continue;
     979            0 :          } else if (list.size() == 1) {
     980            0 :             if (v->GetType() == MJSON_ARRAY) {
     981            0 :                cm_msg(MERROR, "js_db_paste", "unexpected array of values for array path \"%s\"", path.c_str());
     982            0 :                sresult->AddToArray(MJsonNode::MakeInt(DB_TYPE_MISMATCH));
     983            0 :                continue;
     984              :             }
     985              : 
     986            0 :             status = db_paste_json_node(hDB, hkey, list[0], v);
     987            0 :             sresult->AddToArray(MJsonNode::MakeInt(status));
     988            0 :          } else if ((list.size() > 1) && (v->GetType() == MJSON_ARRAY)) {
     989            0 :             const MJsonNodeVector* vvalues = v->GetArray();
     990              : 
     991            0 :             if (list.size() != vvalues->size()) {
     992            0 :                cm_msg(MERROR, "js_db_paste", "length of values array %d should be same as number of indices %d for array path \"%s\"", (int)vvalues->size(), (int)list.size(), path.c_str());
     993            0 :                sresult->AddToArray(MJsonNode::MakeInt(DB_TYPE_MISMATCH));
     994            0 :                continue;
     995              :             }
     996              : 
     997            0 :             MJsonNode *ssresult = MJsonNode::MakeArray();
     998              : 
     999            0 :             for (unsigned j =0; j <list.size(); j++) {
    1000            0 :                const MJsonNode* vv = (*vvalues)[j];
    1001              : 
    1002            0 :                if (vv == NULL) {
    1003            0 :                   cm_msg(MERROR, "js_db_paste", "internal error: NULL array value at index %d for array path \"%s\"", j, path.c_str());
    1004            0 :                   sresult->AddToArray(MJsonNode::MakeInt(DB_TYPE_MISMATCH));
    1005            0 :                   continue;
    1006              :                }
    1007              : 
    1008            0 :                status = db_paste_json_node(hDB, hkey, list[j], vv);
    1009            0 :                ssresult->AddToArray(MJsonNode::MakeInt(status));
    1010              :             }
    1011              :             
    1012            0 :             sresult->AddToArray(ssresult);
    1013              :          } else {
    1014            0 :             MJsonNode *ssresult = MJsonNode::MakeArray();
    1015            0 :             for (unsigned j =0; j <list.size(); j++) {
    1016            0 :                status = db_paste_json_node(hDB, hkey, list[j], v);
    1017            0 :                ssresult->AddToArray(MJsonNode::MakeInt(status));
    1018              :             }
    1019            0 :             sresult->AddToArray(ssresult);
    1020              :          }
    1021            0 :       } else {
    1022            0 :          status = db_paste_json_node(hDB, hkey, 0, v);
    1023            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
    1024              :       }
    1025            0 :    }
    1026              : 
    1027            0 :    return mjsonrpc_make_result("status", sresult);
    1028              : }
    1029              : 
    1030            0 : static MJsonNode* js_db_create(const MJsonNode* params)
    1031              : {
    1032            0 :    if (!params) {
    1033            0 :       MJSO* doc = MJSO::I();
    1034            0 :       doc->D("Create new ODB entries");
    1035            0 :       MJSO* o = doc->PA("array of ODB paths to be created")->AddObject("", "arguments to db_create_key() and db_set_num_values()");
    1036            0 :       o->Add("path", MJSON_STRING, "ODB path to be created");
    1037            0 :       o->Add("type", MJSON_INT, "MIDAS TID_xxx type");
    1038            0 :       o->Add("array_length?", MJSON_INT, "optional array length, default is 1");
    1039            0 :       o->Add("string_length?", MJSON_INT, "for TID_STRING, optional string length, default is NAME_LENGTH");
    1040            0 :       doc->R("status[]", MJSON_INT, "return status of db_create_key(), db_set_num_values() and db_set_data() (for TID_STRING) for each path");
    1041            0 :       return doc;
    1042              :    }
    1043              : 
    1044            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
    1045              : 
    1046            0 :    const MJsonNodeVector* pp = params->GetArray();
    1047              : 
    1048            0 :    if (!pp) {
    1049            0 :       delete sresult;
    1050            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "parameters must be an array of objects");
    1051              :    }
    1052              : 
    1053              :    HNDLE hDB;
    1054            0 :    cm_get_experiment_database(&hDB, NULL);
    1055              : 
    1056            0 :    for (unsigned i=0; i<pp->size(); i++) {
    1057            0 :       const MJsonNode* p = (*pp)[i];
    1058            0 :       std::string path = mjsonrpc_get_param(p, "path", NULL)->GetString();
    1059            0 :       int type = mjsonrpc_get_param(p, "type", NULL)->GetInt();
    1060            0 :       int array_length = mjsonrpc_get_param(p, "array_length", NULL)->GetInt();
    1061            0 :       int string_length = mjsonrpc_get_param(p, "string_length", NULL)->GetInt();
    1062              : 
    1063              :       //printf("create odb [%s], type %d, array %d, string %d\n", path.c_str(), type, array_length, string_length);
    1064              : 
    1065            0 :       if (string_length == 0)
    1066            0 :          string_length = NAME_LENGTH;
    1067              : 
    1068            0 :       int status = db_create_key(hDB, 0, path.c_str(), type);
    1069              : 
    1070            0 :       if (status == DB_SUCCESS && string_length > 0 && type == TID_STRING) {
    1071              :          HNDLE hKey;
    1072            0 :          status = db_find_key(hDB, 0, path.c_str(), &hKey);
    1073            0 :          if (status == DB_SUCCESS) {
    1074            0 :             char* buf = (char*)calloc(1, string_length);
    1075            0 :             assert(buf != NULL);
    1076            0 :             int size = string_length;
    1077            0 :             status = db_set_data(hDB, hKey, buf, size, 1, TID_STRING);
    1078            0 :             free(buf);
    1079              :          }
    1080              :       }
    1081              : 
    1082            0 :       if (status == DB_SUCCESS && array_length > 1) {
    1083              :          HNDLE hKey;
    1084            0 :          status = db_find_key(hDB, 0, path.c_str(), &hKey);
    1085            0 :          if (status == DB_SUCCESS)
    1086            0 :             status = db_set_num_values(hDB, hKey, array_length);
    1087              :       }
    1088              : 
    1089            0 :       sresult->AddToArray(MJsonNode::MakeInt(status));
    1090            0 :    }
    1091              : 
    1092            0 :    return mjsonrpc_make_result("status", sresult);
    1093              : }
    1094              : 
    1095            0 : static MJsonNode* js_db_delete(const MJsonNode* params)
    1096              : {
    1097            0 :    if (!params) {
    1098            0 :       MJSO* doc = MJSO::I();
    1099            0 :       doc->D("delete ODB keys");
    1100            0 :       doc->P("paths[]", MJSON_STRING, "array of ODB paths to delete");
    1101            0 :       doc->R("status[]", MJSON_INT, "return status of db_delete_key() for each path");
    1102            0 :       return doc;
    1103              :    }
    1104              : 
    1105            0 :    MJsonNode* error = NULL;
    1106              : 
    1107            0 :    const MJsonNodeVector* paths  = mjsonrpc_get_param_array(params, "paths",  &error); if (error) return error;
    1108              : 
    1109            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
    1110              : 
    1111              :    HNDLE hDB;
    1112            0 :    cm_get_experiment_database(&hDB, NULL);
    1113              : 
    1114            0 :    for (unsigned i=0; i<paths->size(); i++) {
    1115            0 :       int status = 0;
    1116              :       HNDLE hkey;
    1117            0 :       std::string path = (*paths)[i]->GetString();
    1118              : 
    1119            0 :       status = db_find_link(hDB, 0, path.c_str(), &hkey);
    1120            0 :       if (status != DB_SUCCESS) {
    1121            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
    1122            0 :          continue;
    1123              :       }
    1124              : 
    1125            0 :       status = db_delete_key(hDB, hkey, false);
    1126            0 :       sresult->AddToArray(MJsonNode::MakeInt(status));
    1127            0 :    }
    1128              : 
    1129            0 :    return mjsonrpc_make_result("status", sresult);
    1130              : }
    1131              : 
    1132            0 : static MJsonNode* js_db_resize(const MJsonNode* params)
    1133              : {
    1134            0 :    if (!params) {
    1135            0 :       MJSO* doc = MJSO::I();
    1136            0 :       doc->D("Change size of ODB arrays");
    1137            0 :       doc->P("paths[]", MJSON_STRING, "array of ODB paths to resize");
    1138            0 :       doc->P("new_lengths[]", MJSON_INT, "array of new lengths for each ODB path");
    1139            0 :       doc->R("status[]", MJSON_INT, "return status of db_set_num_values() for each path");
    1140            0 :       return doc;
    1141              :    }
    1142              : 
    1143            0 :    MJsonNode* error = NULL;
    1144              : 
    1145            0 :    const MJsonNodeVector* paths   = mjsonrpc_get_param_array(params, "paths",  &error); if (error) return error;
    1146            0 :    const MJsonNodeVector* lengths = mjsonrpc_get_param_array(params, "new_lengths", &error); if (error) return error;
    1147              : 
    1148            0 :    if (paths->size() != lengths->size()) {
    1149            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"new_lengths\" should have the same length");
    1150              :    }
    1151              : 
    1152            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
    1153              : 
    1154              :    HNDLE hDB;
    1155            0 :    cm_get_experiment_database(&hDB, NULL);
    1156              : 
    1157            0 :    for (unsigned i=0; i<paths->size(); i++) {
    1158            0 :       int status = 0;
    1159              :       HNDLE hkey;
    1160            0 :       std::string path = (*paths)[i]->GetString();
    1161              : 
    1162            0 :       status = db_find_key(hDB, 0, path.c_str(), &hkey);
    1163            0 :       if (status != DB_SUCCESS) {
    1164            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
    1165            0 :          continue;
    1166              :       }
    1167              : 
    1168            0 :       int length = (*lengths)[i]->GetInt();
    1169            0 :       if (length < 1) {
    1170            0 :          sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
    1171            0 :          continue;
    1172              :       }
    1173              : 
    1174            0 :       status = db_set_num_values(hDB, hkey, length);
    1175            0 :       sresult->AddToArray(MJsonNode::MakeInt(status));
    1176            0 :    }
    1177              : 
    1178            0 :    return mjsonrpc_make_result("status", sresult);
    1179              : }
    1180              : 
    1181            0 : static MJsonNode* js_db_resize_string(const MJsonNode* params)
    1182              : {
    1183            0 :    if (!params) {
    1184            0 :       MJSO* doc = MJSO::I();
    1185            0 :       doc->D("Change size of ODB string arrays");
    1186            0 :       doc->P("paths[]", MJSON_STRING, "array of ODB paths to resize");
    1187            0 :       doc->P("new_lengths[]", MJSON_INT, "array of new lengths for each ODB path");
    1188            0 :       doc->P("new_string_lengths[]", MJSON_INT, "array of new string lengths for each ODB path");
    1189            0 :       doc->R("status[]", MJSON_INT, "return status of db_resize_string() for each path");
    1190            0 :       return doc;
    1191              :    }
    1192              : 
    1193            0 :    MJsonNode* error = NULL;
    1194              : 
    1195            0 :    const MJsonNodeVector* paths   = mjsonrpc_get_param_array(params, "paths",  &error); if (error) return error;
    1196            0 :    const MJsonNodeVector* lengths = mjsonrpc_get_param_array(params, "new_lengths", &error); if (error) return error;
    1197            0 :    const MJsonNodeVector* string_lengths = mjsonrpc_get_param_array(params, "new_string_lengths", &error); if (error) return error;
    1198              : 
    1199            0 :    if (paths->size() != lengths->size()) {
    1200            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"new_lengths\" should have the same length");
    1201              :    }
    1202              : 
    1203            0 :    if (paths->size() != string_lengths->size()) {
    1204            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"new_string_lengths\" should have the same length");
    1205              :    }
    1206              : 
    1207            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
    1208              : 
    1209              :    HNDLE hDB;
    1210            0 :    cm_get_experiment_database(&hDB, NULL);
    1211              : 
    1212            0 :    for (unsigned i=0; i<paths->size(); i++) {
    1213            0 :       std::string path = (*paths)[i]->GetString();
    1214              : 
    1215            0 :       int length = (*lengths)[i]->GetInt();
    1216            0 :       if (length < 0) {
    1217            0 :          sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
    1218            0 :          continue;
    1219              :       }
    1220              : 
    1221            0 :       int string_length = (*string_lengths)[i]->GetInt();
    1222            0 :       if (length < 0) {
    1223            0 :          sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
    1224            0 :          continue;
    1225              :       }
    1226              : 
    1227            0 :       int status = db_resize_string(hDB, 0, path.c_str(), length, string_length);
    1228            0 :       sresult->AddToArray(MJsonNode::MakeInt(status));
    1229            0 :    }
    1230              : 
    1231            0 :    return mjsonrpc_make_result("status", sresult);
    1232              : }
    1233              : 
    1234            0 : static MJsonNode* js_db_key(const MJsonNode* params)
    1235              : {
    1236            0 :    if (!params) {
    1237            0 :       MJSO* doc = MJSO::I();
    1238            0 :       doc->D("get ODB keys");
    1239            0 :       doc->P("paths[]", MJSON_STRING, "array of ODB paths");
    1240            0 :       doc->R("status[]", MJSON_INT, "return status of db_key() for each path");
    1241            0 :       doc->R("keys[]", MJSON_OBJECT, "key data for each path");
    1242            0 :       doc->R("keys[].type", MJSON_INT, "key type TID_xxx");
    1243            0 :       doc->R("keys[].num_values", MJSON_INT, "array length, 1 for normal entries");
    1244            0 :       doc->R("keys[].name", MJSON_STRING, "key name");
    1245            0 :       doc->R("keys[].total_size", MJSON_INT, "data total size in bytes");
    1246            0 :       doc->R("keys[].item_size", MJSON_INT, "array element size, string length for TID_STRING");
    1247            0 :       doc->R("keys[].access_mode", MJSON_INT, "access mode bitmap of MODE_xxx");
    1248            0 :       doc->R("keys[].notify_count", MJSON_INT, "number of hotlinks attached to this key");
    1249            0 :       doc->R("keys[].last_written", MJSON_INT, "timestamp when data was last updated");
    1250            0 :       return doc;
    1251              :    }
    1252              : 
    1253            0 :    MJsonNode* error = NULL;
    1254              : 
    1255            0 :    const MJsonNodeVector* paths  = mjsonrpc_get_param_array(params, "paths",  &error); if (error) return error;
    1256              : 
    1257            0 :    MJsonNode* kresult = MJsonNode::MakeArray();
    1258            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
    1259              : 
    1260              :    HNDLE hDB;
    1261            0 :    cm_get_experiment_database(&hDB, NULL);
    1262              : 
    1263            0 :    for (unsigned i=0; i<paths->size(); i++) {
    1264            0 :       int status = 0;
    1265              :       HNDLE hkey;
    1266              :       KEY key;
    1267            0 :       std::string path = (*paths)[i]->GetString();
    1268              : 
    1269            0 :       status = db_find_key(hDB, 0, path.c_str(), &hkey);
    1270            0 :       if (status != DB_SUCCESS) {
    1271            0 :          kresult->AddToArray(MJsonNode::MakeNull());
    1272            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
    1273            0 :          continue;
    1274              :       }
    1275              : 
    1276            0 :       status = db_get_key(hDB, hkey, &key);
    1277            0 :       if (status != DB_SUCCESS) {
    1278            0 :          kresult->AddToArray(MJsonNode::MakeNull());
    1279            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
    1280            0 :          continue;
    1281              :       }
    1282              : 
    1283            0 :       MJsonNode* jkey = MJsonNode::MakeObject();
    1284              : 
    1285            0 :       jkey->AddToObject("type", MJsonNode::MakeInt(key.type));
    1286            0 :       jkey->AddToObject("num_values", MJsonNode::MakeInt(key.num_values));
    1287            0 :       ss_repair_utf8(key.name);
    1288            0 :       jkey->AddToObject("name", MJsonNode::MakeString(key.name));
    1289            0 :       jkey->AddToObject("total_size", MJsonNode::MakeInt(key.total_size));
    1290            0 :       jkey->AddToObject("item_size", MJsonNode::MakeInt(key.item_size));
    1291            0 :       jkey->AddToObject("access_mode", MJsonNode::MakeInt(key.access_mode));
    1292            0 :       jkey->AddToObject("notify_count", MJsonNode::MakeInt(key.notify_count));
    1293            0 :       jkey->AddToObject("last_written", MJsonNode::MakeInt(key.last_written));
    1294              : 
    1295            0 :       kresult->AddToArray(jkey);
    1296            0 :       sresult->AddToArray(MJsonNode::MakeInt(status));
    1297            0 :    }
    1298              : 
    1299            0 :    return mjsonrpc_make_result("keys", kresult, "status", sresult);
    1300              : }
    1301              : 
    1302            0 : static MJsonNode* js_db_rename(const MJsonNode* params)
    1303              : {
    1304            0 :    if (!params) {
    1305            0 :       MJSO* doc = MJSO::I();
    1306            0 :       doc->D("Change size of ODB arrays");
    1307            0 :       doc->P("paths[]", MJSON_STRING, "array of ODB paths to rename");
    1308            0 :       doc->P("new_names[]", MJSON_STRING, "array of new names for each ODB path");
    1309            0 :       doc->R("status[]", MJSON_INT, "return status of db_rename_key() for each path");
    1310            0 :       return doc;
    1311              :    }
    1312              : 
    1313            0 :    MJsonNode* error = NULL;
    1314              : 
    1315            0 :    const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths",  &error); if (error) return error;
    1316            0 :    const MJsonNodeVector* names = mjsonrpc_get_param_array(params, "new_names", &error); if (error) return error;
    1317              : 
    1318            0 :    if (paths->size() != names->size()) {
    1319            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"new_names\" should have the same length");
    1320              :    }
    1321              : 
    1322            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
    1323              : 
    1324              :    HNDLE hDB;
    1325            0 :    cm_get_experiment_database(&hDB, NULL);
    1326              : 
    1327            0 :    for (unsigned i=0; i<paths->size(); i++) {
    1328            0 :       int status = 0;
    1329              :       HNDLE hkey;
    1330            0 :       std::string path = (*paths)[i]->GetString();
    1331              : 
    1332            0 :       status = db_find_link(hDB, 0, path.c_str(), &hkey);
    1333            0 :       if (status != DB_SUCCESS) {
    1334            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
    1335            0 :          continue;
    1336              :       }
    1337              : 
    1338            0 :       std::string new_name = (*names)[i]->GetString();
    1339            0 :       if (new_name.length() < 1) {
    1340            0 :          sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
    1341            0 :          continue;
    1342              :       }
    1343              : 
    1344            0 :       status = db_rename_key(hDB, hkey, new_name.c_str());
    1345              : 
    1346            0 :       sresult->AddToArray(MJsonNode::MakeInt(status));
    1347            0 :    }
    1348              : 
    1349            0 :    return mjsonrpc_make_result("status", sresult);
    1350              : }
    1351              : 
    1352            0 : static MJsonNode* js_db_link(const MJsonNode* params)
    1353              : {
    1354            0 :    if (!params) {
    1355            0 :       MJSO* doc = MJSO::I();
    1356            0 :       doc->D("Create ODB symlinks");
    1357            0 :       doc->P("new_links[]", MJSON_STRING, "array of new symlinks to be created");
    1358            0 :       doc->P("target_paths[]", MJSON_STRING, "array of existing ODB paths for each link");
    1359            0 :       doc->R("status[]", MJSON_INT, "return status of db_create_link() for each path");
    1360            0 :       return doc;
    1361              :    }
    1362              : 
    1363            0 :    MJsonNode* error = NULL;
    1364              : 
    1365            0 :    const MJsonNodeVector* target_paths = mjsonrpc_get_param_array(params, "target_paths",  &error); if (error) return error;
    1366            0 :    const MJsonNodeVector* new_links = mjsonrpc_get_param_array(params, "new_links", &error); if (error) return error;
    1367              : 
    1368            0 :    if (target_paths->size() != new_links->size()) {
    1369            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"target_paths\" and \"new_links\" should have the same length");
    1370              :    }
    1371              : 
    1372            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
    1373              : 
    1374              :    HNDLE hDB;
    1375            0 :    cm_get_experiment_database(&hDB, NULL);
    1376              : 
    1377            0 :    for (unsigned i=0; i<new_links->size(); i++) {
    1378            0 :       int status = 0;
    1379            0 :       std::string target_path = (*target_paths)[i]->GetString();
    1380            0 :       std::string new_link = (*new_links)[i]->GetString();
    1381            0 :       if (new_link.length() < 1) {
    1382            0 :          sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
    1383            0 :          continue;
    1384              :       }
    1385              : 
    1386            0 :       status = db_create_link(hDB, 0, new_link.c_str(), target_path.c_str());
    1387              : 
    1388            0 :       sresult->AddToArray(MJsonNode::MakeInt(status));
    1389            0 :    }
    1390              : 
    1391            0 :    return mjsonrpc_make_result("status", sresult);
    1392              : }
    1393              : 
    1394            0 : static MJsonNode* js_db_reorder(const MJsonNode* params)
    1395              : {
    1396            0 :    if (!params) {
    1397            0 :       MJSO* doc = MJSO::I();
    1398            0 :       doc->D("Change order of ODB keys in a subdirectory");
    1399            0 :       doc->P("paths[]", MJSON_STRING, "array of new symlinks to be created");
    1400            0 :       doc->P("indices[]", MJSON_INT, "array of existing ODB paths for each link");
    1401            0 :       doc->R("status[]", MJSON_INT, "return status of db_reorder_key() for each path");
    1402            0 :       return doc;
    1403              :    }
    1404              : 
    1405            0 :    MJsonNode* error = NULL;
    1406              : 
    1407            0 :    const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths",  &error); if (error) return error;
    1408            0 :    const MJsonNodeVector* indices = mjsonrpc_get_param_array(params, "indices", &error); if (error) return error;
    1409              : 
    1410            0 :    if (paths->size() != indices->size()) {
    1411            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"indices\" should have the same length");
    1412              :    }
    1413              : 
    1414            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
    1415              : 
    1416              :    HNDLE hDB;
    1417            0 :    cm_get_experiment_database(&hDB, NULL);
    1418              : 
    1419            0 :    for (unsigned i=0; i<paths->size(); i++) {
    1420            0 :       int status = 0;
    1421              :       HNDLE hkey;
    1422            0 :       std::string path = (*paths)[i]->GetString();
    1423            0 :       int index = (*indices)[i]->GetInt();
    1424              : 
    1425            0 :       status = db_find_key(hDB, 0, path.c_str(), &hkey);
    1426            0 :       if (status != DB_SUCCESS) {
    1427            0 :          sresult->AddToArray(MJsonNode::MakeInt(status));
    1428            0 :          continue;
    1429              :       }
    1430              : 
    1431            0 :       status = db_reorder_key(hDB, hkey, index);
    1432              : 
    1433            0 :       sresult->AddToArray(MJsonNode::MakeInt(status));
    1434            0 :    }
    1435              : 
    1436            0 :    return mjsonrpc_make_result("status", sresult);
    1437              : }
    1438              : 
    1439            0 : static MJsonNode* js_db_sor(const MJsonNode* params)
    1440              : {
    1441            0 :    if (!params) {
    1442            0 :       MJSO* doc = MJSO::I();
    1443            0 :       doc->D("Show ODB open records starting from given ODB path");
    1444            0 :       doc->P("path?", MJSON_STRING, "ODB path");
    1445            0 :       doc->R("sor", MJSON_JSON, "return value of db_sor()");
    1446            0 :       return doc;
    1447              :    }
    1448              : 
    1449            0 :    MJsonNode* error = NULL;
    1450              : 
    1451            0 :    std::string path = mjsonrpc_get_param(params, "path", NULL)->GetString(); if (error) return error;
    1452              : 
    1453              :    HNDLE hDB;
    1454            0 :    cm_get_experiment_database(&hDB, NULL);
    1455              : 
    1456            0 :    MJsonNode* sor = db_sor(hDB, path.c_str());
    1457              : 
    1458            0 :    return mjsonrpc_make_result("sor", sor);
    1459            0 : }
    1460              : 
    1461            0 : static MJsonNode* js_db_scl(const MJsonNode* params)
    1462              : {
    1463            0 :    if (!params) {
    1464            0 :       MJSO* doc = MJSO::I();
    1465            0 :       doc->D("Show ODB clients");
    1466            0 :       doc->R("scl", MJSON_JSON, "return value of db_scl()");
    1467            0 :       return doc;
    1468              :    }
    1469              : 
    1470              :    HNDLE hDB;
    1471            0 :    cm_get_experiment_database(&hDB, NULL);
    1472              : 
    1473            0 :    MJsonNode* scl = db_scl(hDB);
    1474              : 
    1475            0 :    return mjsonrpc_make_result("scl", scl);
    1476              : }
    1477              : 
    1478              : /////////////////////////////////////////////////////////////////////////////////
    1479              : //
    1480              : // cm_msg code goes here
    1481              : //
    1482              : /////////////////////////////////////////////////////////////////////////////////
    1483              : 
    1484            0 : static MJsonNode* js_cm_msg_facilities(const MJsonNode* params)
    1485              : {
    1486            0 :    if (!params) {
    1487            0 :       MJSO* doc = MJSO::I();
    1488            0 :       doc->D("get message facilities using cm_msg_facilities()");
    1489            0 :       doc->R("status", MJSON_INT, "return status of cm_msg_facilities()");
    1490            0 :       doc->R("facilities[]", MJSON_STRING, "array of facility names");
    1491            0 :       return doc;
    1492              :    }
    1493              : 
    1494            0 :    STRING_LIST list;
    1495              :    
    1496            0 :    int status = cm_msg_facilities(&list);
    1497              : 
    1498            0 :    MJsonNode* facilities = MJsonNode::MakeArray();
    1499              : 
    1500            0 :    for (unsigned i=0; i<list.size(); i++) {
    1501            0 :       ss_repair_utf8(list[i]);
    1502            0 :       facilities->AddToArray(MJsonNode::MakeString(list[i].c_str()));
    1503              :    }
    1504              : 
    1505            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status),
    1506            0 :                                "facilities", facilities);
    1507            0 : }
    1508              : 
    1509            0 : static MJsonNode* js_cm_msg1(const MJsonNode* params)
    1510              : {
    1511            0 :    if (!params) {
    1512            0 :       MJSO *doc = MJSO::I();
    1513            0 :       doc->D("Generate a midas message using cm_msg1()");
    1514            0 :       doc->P("facility?", MJSON_STRING, "message facility, default is \"midas\"");
    1515            0 :       doc->P("user?", MJSON_STRING, "message user, default is \"javascript_commands\"");
    1516            0 :       doc->P("type?", MJSON_INT, "message type, MT_xxx from midas.h, default is MT_INFO");
    1517            0 :       doc->P("message", MJSON_STRING, "message text");
    1518            0 :       doc->R("status", MJSON_INT, "return status of cm_msg1()");
    1519            0 :       return doc;
    1520              :    }
    1521              : 
    1522            0 :    MJsonNode* error = NULL;
    1523              : 
    1524            0 :    std::string facility = mjsonrpc_get_param(params, "facility", NULL)->GetString();
    1525            0 :    std::string user = mjsonrpc_get_param(params, "user", NULL)->GetString();
    1526            0 :    int type = mjsonrpc_get_param(params, "type", NULL)->GetInt();
    1527            0 :    std::string message = mjsonrpc_get_param(params, "message", &error)->GetString(); if (error) return error;
    1528              : 
    1529            0 :    if (facility.size() <1)
    1530            0 :       facility = "midas";
    1531            0 :    if (user.size()<1)
    1532            0 :       user = "javascript_commands";
    1533            0 :    if (type == 0)
    1534            0 :       type = MT_INFO;
    1535              : 
    1536            0 :    int status = cm_msg1(type, __FILE__, __LINE__, facility.c_str(), user.c_str(), "%s", message.c_str());
    1537              : 
    1538            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    1539            0 : }
    1540              : 
    1541            0 : static MJsonNode* js_cm_msg_retrieve(const MJsonNode* params)
    1542              : {
    1543            0 :    if (!params) {
    1544            0 :       MJSO *doc = MJSO::I();
    1545            0 :       doc->D("Retrieve midas messages using cm_msg_retrieve2()");
    1546            0 :       doc->P("facility?", MJSON_STRING, "message facility, default is \"midas\"");
    1547            0 :       doc->P("min_messages?", MJSON_INT, "get at least this many messages, default is 1");
    1548            0 :       doc->P("time?", MJSON_NUMBER, "start from given timestamp, value 0 means give me newest messages, default is 0");
    1549            0 :       doc->R("num_messages", MJSON_INT, "number of messages returned");
    1550            0 :       doc->R("messages", MJSON_STRING, "messages separated by \\n");
    1551            0 :       doc->R("status", MJSON_INT, "return status of cm_msg_retrieve2()");
    1552            0 :       return doc;
    1553              :    }
    1554              : 
    1555            0 :    std::string facility = mjsonrpc_get_param(params, "facility", NULL)->GetString();
    1556            0 :    int min_messages = mjsonrpc_get_param(params, "min_messages", NULL)->GetInt();
    1557            0 :    double time = mjsonrpc_get_param(params, "time", NULL)->GetDouble();
    1558              : 
    1559            0 :    if (facility.size() < 1)
    1560            0 :       facility = "midas";
    1561              : 
    1562            0 :    int num_messages = 0;
    1563            0 :    char* messages = NULL;
    1564              : 
    1565            0 :    int status = cm_msg_retrieve2(facility.c_str(), (time_t)time, min_messages, &messages, &num_messages);
    1566              : 
    1567            0 :    MJsonNode* result = MJsonNode::MakeObject();
    1568              : 
    1569            0 :    result->AddToObject("status", MJsonNode::MakeInt(status));
    1570            0 :    result->AddToObject("num_messages", MJsonNode::MakeInt(num_messages));
    1571              : 
    1572            0 :    if (messages) {
    1573            0 :       ss_repair_utf8(messages);
    1574            0 :       result->AddToObject("messages", MJsonNode::MakeString(messages));
    1575            0 :       free(messages);
    1576            0 :       messages = NULL;
    1577              :    }
    1578              : 
    1579            0 :    return mjsonrpc_make_result(result);
    1580            0 : }
    1581              : 
    1582              : /////////////////////////////////////////////////////////////////////////////////
    1583              : //
    1584              : // Alarm code goes here
    1585              : //
    1586              : /////////////////////////////////////////////////////////////////////////////////
    1587              : 
    1588            0 : static MJsonNode* js_al_reset_alarm(const MJsonNode* params)
    1589              : {
    1590            0 :    if (!params) {
    1591            0 :       MJSO* doc = MJSO::I();
    1592            0 :       doc->D("reset alarms");
    1593            0 :       doc->P("alarms[]", MJSON_STRING, "array of alarm names");
    1594            0 :       doc->R("status[]", MJSON_INT, "return status of al_reset_alarm() for each alarm");
    1595            0 :       return doc;
    1596              :    }
    1597              : 
    1598            0 :    MJsonNode* error = NULL;
    1599              : 
    1600            0 :    const MJsonNodeVector* alarms  = mjsonrpc_get_param_array(params, "alarms",  &error); if (error) return error;
    1601              : 
    1602            0 :    MJsonNode* sresult = MJsonNode::MakeArray();
    1603              : 
    1604            0 :    for (unsigned i=0; i<alarms->size(); i++) {
    1605            0 :       int status = al_reset_alarm((*alarms)[i]->GetString().c_str());
    1606            0 :       sresult->AddToArray(MJsonNode::MakeInt(status));
    1607              :    }
    1608              : 
    1609            0 :    return mjsonrpc_make_result("status", sresult);
    1610              : }
    1611              : 
    1612            0 : static MJsonNode* js_al_trigger_alarm(const MJsonNode* params)
    1613              : {
    1614            0 :    if (!params) {
    1615            0 :       MJSO* doc = MJSO::I();
    1616            0 :       doc->D("trigger an alarm");
    1617            0 :       doc->P("name", MJSON_STRING, "alarm name");
    1618            0 :       doc->P("message", MJSON_STRING, "alarm message");
    1619            0 :       doc->P("class", MJSON_STRING, "alarm class");
    1620            0 :       doc->P("condition", MJSON_STRING, "alarm condition");
    1621            0 :       doc->P("type", MJSON_INT, "alarm type (AT_xxx)");
    1622            0 :       doc->R("status", MJSON_INT, "return status of al_trigger_alarm()");
    1623            0 :       return doc;
    1624              :    }
    1625              : 
    1626            0 :    MJsonNode* error = NULL;
    1627              : 
    1628            0 :    std::string name = mjsonrpc_get_param(params, "name", &error)->GetString(); if (error) return error;
    1629            0 :    std::string message = mjsonrpc_get_param(params, "message", &error)->GetString(); if (error) return error;
    1630            0 :    std::string xclass = mjsonrpc_get_param(params, "class", &error)->GetString(); if (error) return error;
    1631            0 :    std::string condition = mjsonrpc_get_param(params, "condition", &error)->GetString(); if (error) return error;
    1632            0 :    int type = mjsonrpc_get_param(params, "type", &error)->GetInt(); if (error) return error;
    1633              : 
    1634            0 :    int status = al_trigger_alarm(name.c_str(), message.c_str(), xclass.c_str(), condition.c_str(), type);
    1635              :    
    1636            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    1637            0 : }
    1638              : 
    1639            0 : static MJsonNode* js_al_trigger_class(const MJsonNode* params)
    1640              : {
    1641            0 :    if (!params) {
    1642            0 :       MJSO* doc = MJSO::I();
    1643            0 :       doc->D("trigger an alarm");
    1644            0 :       doc->P("class", MJSON_STRING, "alarm class");
    1645            0 :       doc->P("message", MJSON_STRING, "alarm message");
    1646            0 :       doc->P("first?", MJSON_BOOL, "see al_trigger_class() in midas.c");
    1647            0 :       doc->R("status", MJSON_INT, "return status of al_trigger_class()");
    1648            0 :       return doc;
    1649              :    }
    1650              : 
    1651            0 :    MJsonNode* error = NULL;
    1652              : 
    1653            0 :    std::string xclass = mjsonrpc_get_param(params, "class", &error)->GetString(); if (error) return error;
    1654            0 :    std::string message = mjsonrpc_get_param(params, "message", &error)->GetString(); if (error) return error;
    1655            0 :    bool first = mjsonrpc_get_param(params, "first", NULL)->GetBool();
    1656              : 
    1657            0 :    int status = al_trigger_class(xclass.c_str(), message.c_str(), first);
    1658              : 
    1659            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    1660            0 : }
    1661              : 
    1662              : /////////////////////////////////////////////////////////////////////////////////
    1663              : //
    1664              : // History code goes here
    1665              : //
    1666              : /////////////////////////////////////////////////////////////////////////////////
    1667              : 
    1668              : #include "history.h"
    1669              : 
    1670            0 : static MJsonNode* js_hs_get_active_events(const MJsonNode* params)
    1671              : {
    1672            0 :    if (!params) {
    1673            0 :       MJSO* doc = MJSO::I();
    1674            0 :       doc->D("get list of active history events using hs_read_event_list()");
    1675            0 :       doc->R("status", MJSON_INT, "return status of hs_read_event_list()");
    1676            0 :       doc->R("events[]", MJSON_STRING, "array of history event names");
    1677            0 :       return doc;
    1678              :    }
    1679              : 
    1680            0 :    STRING_LIST list;
    1681              :    
    1682            0 :    int status = hs_read_event_list(&list);
    1683              : 
    1684            0 :    MJsonNode* events = MJsonNode::MakeArray();
    1685              : 
    1686            0 :    for (unsigned i=0; i<list.size(); i++) {
    1687            0 :       ss_repair_utf8(list[i]);
    1688            0 :       events->AddToArray(MJsonNode::MakeString(list[i].c_str()));
    1689              :    }
    1690              : 
    1691            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "events", events);
    1692            0 : }
    1693              : 
    1694              : typedef std::map<std::string,MidasHistoryInterface*> MhiMap;
    1695              : 
    1696              : static MhiMap gHistoryChannels;
    1697              : 
    1698            0 : static MidasHistoryInterface* GetHistory(const char* name)
    1699              : {
    1700              :    // empty name means use the default reader channel
    1701              : 
    1702            0 :    MhiMap::iterator ci = gHistoryChannels.find(name);
    1703            0 :    if (ci != gHistoryChannels.end()) {
    1704            0 :       return ci->second;
    1705              :    };
    1706              : 
    1707            0 :    int verbose = 0;
    1708              : 
    1709              :    HNDLE hDB;
    1710            0 :    cm_get_experiment_database(&hDB, NULL);
    1711              : 
    1712            0 :    HNDLE hKey = 0;
    1713              : 
    1714            0 :    if (strlen(name) < 1) {
    1715            0 :       int status = hs_find_reader_channel(hDB, &hKey, verbose);
    1716            0 :       if (status != HS_SUCCESS) {
    1717            0 :          return NULL;
    1718              :       }
    1719              :    } else {
    1720              :       HNDLE hKeyChan;
    1721            0 :       int status = db_find_key(hDB, 0, "/Logger/History", &hKeyChan);
    1722            0 :       if (status != DB_SUCCESS) {
    1723            0 :          return NULL;
    1724              :       }
    1725            0 :       status = db_find_key(hDB, hKeyChan, name, &hKey);
    1726            0 :       if (status != DB_SUCCESS) {
    1727            0 :          return NULL;
    1728              :       }
    1729              :    }
    1730              : 
    1731            0 :    MidasHistoryInterface* mh = NULL;
    1732              : 
    1733            0 :    int status = hs_get_history(hDB, hKey, HS_GET_READER|HS_GET_INACTIVE, verbose, &mh);
    1734            0 :    if (status != HS_SUCCESS || mh==NULL) {
    1735            0 :       cm_msg(MERROR, "GetHistory", "Cannot configure history, hs_get_history() status %d", status);
    1736            0 :       return NULL;
    1737              :    }
    1738              : 
    1739              :    //printf("hs_get_history: \"%s\" -> mh %p\n", name, mh);
    1740              : 
    1741            0 :    gHistoryChannels[name] = mh;
    1742              :    
    1743              :    // cm_msg(MINFO, "GetHistory", "Reading history channel \"%s\" from channel \'%s\' type \'%s\'", name, mh->name, mh->type);
    1744              : 
    1745            0 :    return mh;
    1746              : }
    1747              : 
    1748            0 : static void js_hs_exit()
    1749              : {
    1750            0 :    for (auto& e : gHistoryChannels) {
    1751              :       //printf("history channel \"%s\" mh %p\n", e.first.c_str(), e.second);
    1752            0 :       delete e.second;
    1753              :    }
    1754            0 :    gHistoryChannels.clear();
    1755            0 : }
    1756              : 
    1757            0 : static MJsonNode* js_hs_get_channels(const MJsonNode* params)
    1758              : {
    1759            0 :    if (!params) {
    1760            0 :       MJSO* doc = MJSO::I();
    1761            0 :       doc->D("get list of history channels in /Logger/History");
    1762            0 :       doc->R("status", MJSON_INT, "return success or failure status");
    1763            0 :       doc->R("default_channel", MJSON_STRING, "name of the default logger history channel");
    1764            0 :       doc->R("channels[]", MJSON_STRING, "all logger history channel names");
    1765            0 :       doc->R("active_channels[]", MJSON_STRING, "active logger history channel names");
    1766            0 :       return doc;
    1767              :    }
    1768              : 
    1769            0 :    MJsonNode* channels = MJsonNode::MakeArray();
    1770            0 :    MJsonNode* active_channels = MJsonNode::MakeArray();
    1771              : 
    1772              :    HNDLE hDB;
    1773            0 :    cm_get_experiment_database(&hDB, NULL);
    1774              :    
    1775              :    // get history channel name selected by user in ODB
    1776              : 
    1777              :    //std::string selected_channel;
    1778              :    //db_get_value_string(hDB, 0, "/History/LoggerHistoryChannel", 0, &selected_channel, TRUE);
    1779              :    
    1780              :    int status;
    1781              :    HNDLE hKeyChan;
    1782              : 
    1783            0 :    status = db_find_key(hDB, 0, "/Logger/History", &hKeyChan);
    1784            0 :    if (status == DB_SUCCESS) {
    1785            0 :       for (int ichan=0; ; ichan++) {
    1786              :          HNDLE hKey;
    1787            0 :          status = db_enum_key(hDB, hKeyChan, ichan, &hKey);
    1788            0 :          if (status == DB_NO_MORE_SUBKEYS) {
    1789            0 :             status = DB_SUCCESS;
    1790            0 :             break;
    1791              :          }
    1792            0 :          if (status != DB_SUCCESS)
    1793            0 :             break;
    1794              : 
    1795              :          KEY key;
    1796              : 
    1797            0 :          status = db_get_key(hDB, hKey, &key);
    1798              : 
    1799            0 :          if (status == DB_SUCCESS) {
    1800            0 :             ss_repair_utf8(key.name);
    1801            0 :             channels->AddToArray(MJsonNode::MakeString(key.name));
    1802              : 
    1803            0 :             INT active = 0;
    1804            0 :             INT size = sizeof(active);
    1805            0 :             status = db_get_value(hDB, hKey, "Active", &active, &size, TID_BOOL, FALSE);
    1806            0 :             if (status == DB_SUCCESS) {
    1807            0 :                if (active) {
    1808            0 :                   active_channels->AddToArray(MJsonNode::MakeString(key.name));
    1809              :                }
    1810              :             }
    1811              :          }
    1812            0 :       }
    1813              :    }
    1814              : 
    1815            0 :    std::string default_channel;
    1816              : 
    1817              :    HNDLE hKey;
    1818            0 :    status = hs_find_reader_channel(hDB, &hKey, 0);
    1819            0 :    if (status == DB_SUCCESS) {
    1820              :       KEY key;
    1821            0 :       status = db_get_key(hDB, hKey, &key);
    1822            0 :       if (status == DB_SUCCESS) {
    1823            0 :          default_channel = key.name;
    1824              :       }
    1825              :    }
    1826              : 
    1827            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(1),
    1828              :                                //"selected_channel", MJsonNode::MakeString(selected_channel.c_str()),
    1829              :                                "default_channel", MJsonNode::MakeString(default_channel.c_str()),
    1830              :                                "active_channels", active_channels,
    1831            0 :                                "channels", channels);
    1832            0 : }
    1833              : 
    1834            0 : static MJsonNode* js_hs_get_events(const MJsonNode* params)
    1835              : {
    1836            0 :    if (!params) {
    1837            0 :       MJSO* doc = MJSO::I();
    1838            0 :       doc->D("get list of history events that existed at give time using hs_get_events()");
    1839            0 :       doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
    1840            0 :       doc->P("time?", MJSON_NUMBER, "timestamp, value 0 means current time, default is 0");
    1841            0 :       doc->R("status", MJSON_INT, "return status of hs_get_events()");
    1842            0 :       doc->R("channel", MJSON_STRING, "logger history channel name");
    1843            0 :       doc->R("events[]", MJSON_STRING, "array of history event names");
    1844            0 :       return doc;
    1845              :    }
    1846              : 
    1847            0 :    std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
    1848            0 :    double time = mjsonrpc_get_param(params, "time", NULL)->GetDouble();
    1849              : 
    1850            0 :    MidasHistoryInterface* mh = GetHistory(channel.c_str());
    1851              : 
    1852            0 :    MJsonNode* events = MJsonNode::MakeArray();
    1853              : 
    1854            0 :    if (!mh) {
    1855            0 :       int status = HS_FILE_ERROR;
    1856            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "events", events);
    1857              :    }
    1858              : 
    1859            0 :    if (time == 0) {
    1860            0 :       time = ::time(NULL);
    1861              :    }
    1862              : 
    1863            0 :    STRING_LIST list;
    1864              :    
    1865            0 :    int status = mh->hs_get_events(time, &list);
    1866              : 
    1867            0 :    for (unsigned i=0; i<list.size(); i++) {
    1868            0 :       ss_repair_utf8(list[i]);
    1869            0 :       events->AddToArray(MJsonNode::MakeString(list[i].c_str()));
    1870              :    }
    1871              : 
    1872            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "events", events);
    1873            0 : }
    1874              : 
    1875            0 : static MJsonNode* js_hs_reopen(const MJsonNode* params)
    1876              : {
    1877            0 :    if (!params) {
    1878            0 :       MJSO* doc = MJSO::I();
    1879            0 :       doc->D("reopen the history channel to make sure we see the latest list of events using hs_clear_cache()");
    1880            0 :       doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
    1881            0 :       doc->R("status", MJSON_INT, "return status of hs_get_events()");
    1882            0 :       doc->R("channel", MJSON_STRING, "logger history channel name");
    1883            0 :       return doc;
    1884              :    }
    1885              : 
    1886            0 :    std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
    1887              : 
    1888            0 :    MidasHistoryInterface* mh = GetHistory(channel.c_str());
    1889              : 
    1890            0 :    if (!mh) {
    1891            0 :       int status = HS_FILE_ERROR;
    1892            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    1893              :    }
    1894              : 
    1895            0 :    int status = mh->hs_clear_cache();
    1896              : 
    1897            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name));
    1898            0 : }
    1899              : 
    1900            0 : static MJsonNode* js_hs_get_tags(const MJsonNode* params)
    1901              : {
    1902            0 :    if (!params) {
    1903            0 :       MJSO* doc = MJSO::I();
    1904            0 :       doc->D("get list of history tags for given history events that existed at give time using hs_get_tags()");
    1905            0 :       doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
    1906            0 :       doc->P("time?", MJSON_NUMBER, "timestamp, value 0 means current time, default is 0");
    1907            0 :       doc->P("events[]?", MJSON_STRING, "array of history event names, default is get all events using hs_get_events()");
    1908            0 :       doc->R("status", MJSON_INT, "return status");
    1909            0 :       doc->R("channel", MJSON_STRING, "logger history channel name");
    1910            0 :       doc->R("events[].name", MJSON_STRING, "array of history event names for each history event");
    1911            0 :       doc->R("events[].status", MJSON_INT, "array of status ohistory tags for each history event");
    1912            0 :       doc->R("events[].tags[]", MJSON_STRING, "array of history tags for each history event");
    1913            0 :       doc->R("events[].tags[].name", MJSON_STRING, "history tag name");
    1914            0 :       doc->R("events[].tags[].type", MJSON_INT, "history tag midas data type");
    1915            0 :       doc->R("events[].tags[].n_data?", MJSON_INT, "history tag number of array elements, omitted if 1");
    1916            0 :       return doc;
    1917              :    }
    1918              : 
    1919            0 :    std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
    1920            0 :    double time = mjsonrpc_get_param(params, "time", NULL)->GetDouble();
    1921            0 :    const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
    1922              : 
    1923            0 :    if (time == 0) {
    1924            0 :       time = ::time(NULL);
    1925              :    }
    1926              : 
    1927            0 :    MidasHistoryInterface* mh = GetHistory(channel.c_str());
    1928              : 
    1929            0 :    MJsonNode* events = MJsonNode::MakeArray();
    1930              : 
    1931            0 :    if (!mh) {
    1932            0 :       int status = HS_FILE_ERROR;
    1933            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "events", events);
    1934              :    }
    1935              : 
    1936            0 :    std::vector<std::string> event_names;
    1937              : 
    1938            0 :    if (events_array && events_array->size() > 0) {
    1939            0 :       for (unsigned i=0; i<events_array->size(); i++) {
    1940            0 :          event_names.push_back((*events_array)[i]->GetString());
    1941              :       }
    1942              :    }
    1943              : 
    1944            0 :    if (event_names.size() < 1) {
    1945            0 :       int status = mh->hs_get_events(time, &event_names);
    1946            0 :       if (status != HS_SUCCESS) {
    1947            0 :          return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "events", events);
    1948              :       }
    1949              :    }
    1950              : 
    1951            0 :    for (unsigned i=0; i<event_names.size(); i++) {
    1952            0 :       MJsonNode* o = MJsonNode::MakeObject();
    1953            0 :       const char* event_name = event_names[i].c_str();
    1954            0 :       std::vector<TAG> tags;
    1955            0 :       int status = mh->hs_get_tags(event_name, time, &tags);
    1956              :       //ss_repair_utf8(event_name); redundant!
    1957            0 :       o->AddToObject("name", MJsonNode::MakeString(event_name));
    1958            0 :       o->AddToObject("status", MJsonNode::MakeInt(status));
    1959            0 :       MJsonNode *ta = MJsonNode::MakeArray();
    1960            0 :       for (unsigned j=0; j<tags.size(); j++) {
    1961            0 :          MJsonNode* to = MJsonNode::MakeObject();
    1962            0 :          ss_repair_utf8(tags[j].name);
    1963            0 :          to->AddToObject("name", MJsonNode::MakeString(tags[j].name));
    1964            0 :          to->AddToObject("type", MJsonNode::MakeInt(tags[j].type));
    1965            0 :          if (tags[j].n_data != 1) {
    1966            0 :             to->AddToObject("n_data", MJsonNode::MakeInt(tags[j].n_data));
    1967              :          }
    1968            0 :          ta->AddToArray(to);
    1969              :       }
    1970            0 :       o->AddToObject("tags", ta);
    1971            0 :       events->AddToArray(o);
    1972            0 :    }
    1973              : 
    1974            0 :    int status = HS_SUCCESS;
    1975            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "events", events);
    1976            0 : }
    1977              : 
    1978            0 : static MJsonNode* js_hs_get_last_written(const MJsonNode* params)
    1979              : {
    1980            0 :    if (!params) {
    1981            0 :       MJSO* doc = MJSO::I();
    1982            0 :       doc->D("get list of history tags for given history events that existed at give time using hs_get_last_written()");
    1983            0 :       doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
    1984            0 :       doc->P("time?", MJSON_NUMBER, "timestamp, value 0 means current time, default is 0");
    1985            0 :       doc->P("events[]", MJSON_STRING, "array of history event names");
    1986            0 :       doc->P("tags[]", MJSON_STRING, "array of history event tag names");
    1987            0 :       doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
    1988            0 :       doc->R("status", MJSON_INT, "return status");
    1989            0 :       doc->R("channel", MJSON_STRING, "logger history channel name");
    1990            0 :       doc->R("last_written[]", MJSON_NUMBER, "array of last-written times for each history event");
    1991            0 :       return doc;
    1992              :    }
    1993              : 
    1994            0 :    std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
    1995            0 :    double time = mjsonrpc_get_param(params, "time", NULL)->GetDouble();
    1996              : 
    1997            0 :    const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
    1998            0 :    const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
    1999            0 :    const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
    2000              : 
    2001            0 :    MidasHistoryInterface* mh = GetHistory(channel.c_str());
    2002              : 
    2003            0 :    MJsonNode* lw = MJsonNode::MakeArray();
    2004              : 
    2005            0 :    if (!mh) {
    2006            0 :       int status = HS_FILE_ERROR;
    2007            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "last_written", lw);
    2008              :    }
    2009              : 
    2010            0 :    unsigned num_var = events_array->size();
    2011              : 
    2012            0 :    if (tags_array->size() != num_var) {
    2013            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
    2014              :    }
    2015              : 
    2016            0 :    if (index_array->size() != num_var) {
    2017            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
    2018              :    }
    2019              : 
    2020            0 :    std::vector<std::string> event_names(num_var);
    2021            0 :    std::vector<std::string> tag_names(num_var);
    2022              :    // const char** event_name = new const char*[num_var];
    2023              :    // const char** tag_name = new const char*[num_var];
    2024            0 :    int* var_index = new int[num_var];
    2025            0 :    time_t* last_written = new time_t[num_var];
    2026              : 
    2027            0 :    for (unsigned i=0; i<num_var; i++) {
    2028              :       //event_name[i] = (*events_array)[i]->GetString().c_str();
    2029              :       //tag_name[i] = (*tags_array)[i]->GetString().c_str();
    2030            0 :       event_names[i] = (*events_array)[i]->GetString();
    2031            0 :       tag_names[i] = (*tags_array)[i]->GetString();
    2032            0 :       var_index[i] = (*index_array)[i]->GetInt();
    2033              :    }
    2034              : 
    2035              :    if (/* DISABLES CODE */ (0)) {
    2036              :       printf("time %f, num_vars %d:\n", time, num_var);
    2037              :       for (unsigned i=0; i<num_var; i++) {
    2038              :          printf("%d: [%s] [%s] [%d]\n", i, event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
    2039              :       }
    2040              :    }
    2041              : 
    2042            0 :    if (time == 0) {
    2043            0 :       time = ::time(NULL);
    2044              :    }
    2045              : 
    2046              :    
    2047            0 :    const char** event_name = new const char*[num_var];
    2048            0 :    const char** tag_name = new const char*[num_var];
    2049            0 :    for (unsigned i=0; i<num_var; i++) {
    2050            0 :       event_name[i] = event_names[i].c_str();
    2051            0 :       tag_name[i] = tag_names[i].c_str();
    2052              :    }
    2053            0 :    int status = mh->hs_get_last_written(time, num_var, event_name, tag_name, var_index, last_written);
    2054              : 
    2055            0 :    for (unsigned i=0; i<num_var; i++) {
    2056              :       if (/* DISABLES CODE */ (0)) {
    2057              :          printf("%d: last_written %d\n", i, (int)last_written[i]);
    2058              :       }
    2059            0 :       lw->AddToArray(MJsonNode::MakeNumber(last_written[i]));
    2060              :    }
    2061              : 
    2062            0 :    delete[] event_name;
    2063            0 :    delete[] tag_name;
    2064            0 :    delete[] var_index;
    2065            0 :    delete[] last_written;
    2066              : 
    2067            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "last_written", lw);
    2068            0 : }
    2069              : 
    2070              : class JsonHistoryBuffer: public MidasHistoryBufferInterface
    2071              : {
    2072              : public:
    2073              :    int fCount;
    2074              :    std::string fTimeJson;
    2075              :    std::string fValueJson;
    2076              : 
    2077              : public:
    2078            0 :    JsonHistoryBuffer() // ctor
    2079            0 :    {
    2080            0 :       fCount = 0;
    2081              :       
    2082            0 :       fTimeJson = "[";
    2083            0 :       fValueJson = "[";
    2084            0 :    }
    2085              : 
    2086            0 :    void Add(time_t t, double v)
    2087              :    {
    2088              :       //printf("add time %d, value %f\n", (int)t, v);
    2089              : 
    2090            0 :       if (fCount>0) {
    2091            0 :          fTimeJson += ",";
    2092            0 :          fValueJson += ",";
    2093              :       }
    2094            0 :       fCount++;
    2095              : 
    2096            0 :       fTimeJson += MJsonNode::EncodeDouble(t);
    2097            0 :       fValueJson += MJsonNode::EncodeDouble(v);
    2098            0 :    }
    2099              : 
    2100            0 :    void Finish()
    2101              :    {
    2102            0 :       fTimeJson += "]";
    2103            0 :       fValueJson += "]";
    2104            0 :    }
    2105              : };
    2106              : 
    2107            0 : static MJsonNode* js_hs_read(const MJsonNode* params)
    2108              : {
    2109            0 :    if (!params) {
    2110            0 :       MJSO* doc = MJSO::I();
    2111            0 :       doc->D("get history data for given history events that existed at give time using hs_read_buffer()");
    2112            0 :       doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
    2113            0 :       doc->P("start_time", MJSON_NUMBER, "start time of the data");
    2114            0 :       doc->P("end_time", MJSON_NUMBER, "end time of the data");
    2115            0 :       doc->P("events[]", MJSON_STRING, "array of history event names");
    2116            0 :       doc->P("tags[]", MJSON_STRING, "array of history event tag names");
    2117            0 :       doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
    2118            0 :       doc->R("status", MJSON_INT, "return status");
    2119            0 :       doc->R("channel", MJSON_STRING, "logger history channel name");
    2120            0 :       doc->R("data[]", MJSON_ARRAY, "array of history data");
    2121            0 :       doc->R("data[].status", MJSON_INT, "status for each event");
    2122            0 :       doc->R("data[].count", MJSON_INT, "number of data for each event");
    2123            0 :       doc->R("data[].time[]", MJSON_NUMBER, "time data");
    2124            0 :       doc->R("data[].value[]", MJSON_NUMBER, "value data");
    2125            0 :       return doc;
    2126              :    }
    2127              : 
    2128            0 :    MJsonNode* error = NULL;
    2129              : 
    2130            0 :    std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
    2131            0 :    double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
    2132            0 :    double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
    2133              : 
    2134            0 :    const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
    2135            0 :    const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
    2136            0 :    const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
    2137              : 
    2138            0 :    MidasHistoryInterface* mh = GetHistory(channel.c_str());
    2139              : 
    2140            0 :    MJsonNode* data = MJsonNode::MakeArray();
    2141              : 
    2142            0 :    if (!mh) {
    2143            0 :       int status = HS_FILE_ERROR;
    2144            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "data", data);
    2145              :    }
    2146              : 
    2147            0 :    unsigned num_var = events_array->size();
    2148              : 
    2149            0 :    if (tags_array->size() != num_var) {
    2150            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
    2151              :    }
    2152              : 
    2153            0 :    if (index_array->size() != num_var) {
    2154            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
    2155              :    }
    2156              : 
    2157            0 :    std::vector<std::string> event_names(num_var);
    2158            0 :    std::vector<std::string> tag_names(num_var);
    2159            0 :    int* var_index = new int[num_var];
    2160            0 :    JsonHistoryBuffer** jbuf = new JsonHistoryBuffer*[num_var];
    2161            0 :    MidasHistoryBufferInterface** buf = new MidasHistoryBufferInterface*[num_var];
    2162            0 :    int* hs_status = new int[num_var];
    2163              : 
    2164            0 :    for (unsigned i=0; i<num_var; i++) {
    2165              :       //event_name[i] = (*events_array)[i]->GetString().c_str();
    2166              :       //tag_name[i] = (*tags_array)[i]->GetString().c_str();
    2167            0 :       event_names[i] = (*events_array)[i]->GetString();
    2168            0 :       tag_names[i] = (*tags_array)[i]->GetString();
    2169            0 :       var_index[i] = (*index_array)[i]->GetInt();
    2170            0 :       jbuf[i] = new JsonHistoryBuffer();
    2171            0 :       buf[i] = jbuf[i];
    2172            0 :       hs_status[i] = 0;
    2173              :    }
    2174              : 
    2175              :    if (/* DISABLES CODE */ (0)) {
    2176              :       printf("time %f %f, num_vars %d:\n", start_time, end_time, num_var);
    2177              :       for (unsigned i=0; i<num_var; i++) {
    2178              :          printf("%d: [%s] [%s] [%d]\n", i, event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
    2179              :       }
    2180              :    }
    2181              : 
    2182            0 :    const char** event_name = new const char*[num_var];
    2183            0 :    const char** tag_name = new const char*[num_var];
    2184            0 :    for (unsigned i=0; i<num_var; i++) {
    2185            0 :       event_name[i] = event_names[i].c_str();
    2186            0 :       tag_name[i] = tag_names[i].c_str();
    2187              :    }
    2188              : 
    2189            0 :    int status = mh->hs_read_buffer(start_time, end_time, num_var, event_name, tag_name, var_index, buf, hs_status);
    2190              : 
    2191            0 :    for (unsigned i=0; i<num_var; i++) {
    2192            0 :       jbuf[i]->Finish();
    2193              : 
    2194            0 :       MJsonNode* obj = MJsonNode::MakeObject();
    2195            0 :       obj->AddToObject("status", MJsonNode::MakeInt(hs_status[i]));
    2196            0 :       obj->AddToObject("count", MJsonNode::MakeInt(jbuf[i]->fCount));
    2197            0 :       obj->AddToObject("time", MJsonNode::MakeJSON(jbuf[i]->fTimeJson.c_str()));
    2198            0 :       obj->AddToObject("value", MJsonNode::MakeJSON(jbuf[i]->fValueJson.c_str()));
    2199            0 :       data->AddToArray(obj);
    2200              : 
    2201            0 :       delete jbuf[i];
    2202            0 :       jbuf[i] = NULL;
    2203            0 :       buf[i] = NULL;
    2204              :    }
    2205              : 
    2206            0 :    delete[] event_name;
    2207            0 :    delete[] tag_name;
    2208            0 :    delete[] var_index;
    2209            0 :    delete[] buf;
    2210            0 :    delete[] jbuf;
    2211            0 :    delete[] hs_status;
    2212              : 
    2213            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "data", data);
    2214            0 : }
    2215              : 
    2216            0 : static MJsonNode* js_hs_read_binned(const MJsonNode* params)
    2217              : {
    2218            0 :    if (!params) {
    2219            0 :       MJSO* doc = MJSO::I();
    2220            0 :       doc->D("get history data for given history events that existed at give time using hs_read_buffer()");
    2221            0 :       doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
    2222            0 :       doc->P("start_time", MJSON_NUMBER, "start time of the data");
    2223            0 :       doc->P("end_time", MJSON_NUMBER, "end time of the data");
    2224            0 :       doc->P("num_bins", MJSON_INT, "number of time bins");
    2225            0 :       doc->P("events[]", MJSON_STRING, "array of history event names");
    2226            0 :       doc->P("tags[]", MJSON_STRING, "array of history event tag names");
    2227            0 :       doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
    2228            0 :       doc->R("status", MJSON_INT, "return status");
    2229            0 :       doc->R("channel", MJSON_STRING, "logger history channel name");
    2230            0 :       doc->R("data[]", MJSON_ARRAY, "array of history data");
    2231            0 :       doc->R("data[].status", MJSON_INT, "status for each event");
    2232            0 :       doc->R("data[].num_entries", MJSON_INT, "number of data points for each event");
    2233            0 :       doc->R("data[].count[]", MJSON_INT, "number of data points for each bin");
    2234            0 :       doc->R("data[].mean[]", MJSON_NUMBER, "mean for each bin");
    2235            0 :       doc->R("data[].rms[]", MJSON_NUMBER, "rms for each bin");
    2236            0 :       doc->R("data[].min[]", MJSON_NUMBER, "minimum value for each bin");
    2237            0 :       doc->R("data[].max[]", MJSON_NUMBER, "maximum value for each bin");
    2238            0 :       doc->R("data[].bins_first_time[]", MJSON_NUMBER, "first data point in each bin");
    2239            0 :       doc->R("data[].bins_first_value[]", MJSON_NUMBER, "first data point in each bin");
    2240            0 :       doc->R("data[].bins_last_time[]", MJSON_NUMBER, "last data point in each bin");
    2241            0 :       doc->R("data[].bins_last_value[]", MJSON_NUMBER, "last data point in each bin");
    2242            0 :       doc->R("data[].last_time", MJSON_NUMBER, "time of last data entry");
    2243            0 :       doc->R("data[].last_value", MJSON_NUMBER, "value of last data entry");
    2244            0 :       return doc;
    2245              :    }
    2246              : 
    2247            0 :    MJsonNode* error = NULL;
    2248              : 
    2249            0 :    std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
    2250            0 :    double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
    2251            0 :    double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
    2252            0 :    int num_bins = mjsonrpc_get_param(params, "num_bins", &error)->GetInt(); if (error) return error;
    2253              : 
    2254            0 :    if (num_bins < 1) {
    2255            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Value of num_bins should be 1 or more");
    2256              :    }
    2257              : 
    2258            0 :    const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
    2259            0 :    const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
    2260            0 :    const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
    2261              : 
    2262            0 :    MidasHistoryInterface* mh = GetHistory(channel.c_str());
    2263              : 
    2264            0 :    MJsonNode* data = MJsonNode::MakeArray();
    2265              : 
    2266            0 :    if (!mh) {
    2267            0 :       int status = HS_FILE_ERROR;
    2268            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "data", data);
    2269              :    }
    2270              : 
    2271            0 :    unsigned num_var = events_array->size();
    2272              : 
    2273            0 :    if (num_var < 1) {
    2274            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Array of events should have 1 or more elements");
    2275              :    }
    2276              : 
    2277            0 :    if (tags_array->size() != num_var) {
    2278            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
    2279              :    }
    2280              : 
    2281            0 :    if (index_array->size() != num_var) {
    2282            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
    2283              :    }
    2284              :    
    2285            0 :    std::vector<std::string> event_names(num_var);
    2286            0 :    std::vector<std::string> tag_names(num_var);
    2287              :    //const char** event_name = new const char*[num_var];
    2288              :    //const char** tag_name = new const char*[num_var];
    2289            0 :    int* var_index = new int[num_var];
    2290              : 
    2291            0 :    int* num_entries = new int[num_var];
    2292            0 :    time_t* last_time = new time_t[num_var];
    2293            0 :    double* last_value = new double[num_var];
    2294            0 :    int* hs_status = new int[num_var];
    2295              : 
    2296            0 :    int** count_bins = new int*[num_var];
    2297            0 :    double** mean_bins = new double*[num_var];
    2298            0 :    double** rms_bins = new double*[num_var];
    2299            0 :    double** min_bins = new double*[num_var];
    2300            0 :    double** max_bins = new double*[num_var];
    2301              : 
    2302            0 :    time_t** bins_first_time = new time_t*[num_var];
    2303            0 :    time_t** bins_last_time  = new time_t*[num_var];
    2304              : 
    2305            0 :    double** bins_first_value = new double*[num_var];
    2306            0 :    double** bins_last_value  = new double*[num_var];
    2307              : 
    2308            0 :    for (unsigned i=0; i<num_var; i++) {
    2309              :       //event_name[i] = (*events_array)[i]->GetString().c_str();
    2310              :       //tag_name[i] = (*tags_array)[i]->GetString().c_str();
    2311            0 :       event_names[i] = (*events_array)[i]->GetString();
    2312            0 :       tag_names[i] = (*tags_array)[i]->GetString();
    2313            0 :       var_index[i] = (*index_array)[i]->GetInt();
    2314            0 :       num_entries[i] = 0;
    2315            0 :       last_time[i] = 0;
    2316            0 :       last_value[i] = 0;
    2317            0 :       hs_status[i] = 0;
    2318            0 :       count_bins[i] = new int[num_bins];
    2319            0 :       mean_bins[i] = new double[num_bins];
    2320            0 :       rms_bins[i] = new double[num_bins];
    2321            0 :       min_bins[i] = new double[num_bins];
    2322            0 :       max_bins[i] = new double[num_bins];
    2323              : 
    2324            0 :       bins_first_time[i] = new time_t[num_bins];
    2325            0 :       bins_last_time[i] = new time_t[num_bins];
    2326              : 
    2327            0 :       bins_first_value[i] = new double[num_bins];
    2328            0 :       bins_last_value[i] = new double[num_bins];
    2329              :    }
    2330              : 
    2331              :    if (/* DISABLES CODE */ (0)) {
    2332              :       printf("time %f %f, num_vars %d:\n", start_time, end_time, num_var);
    2333              :       for (unsigned i=0; i<num_var; i++) {
    2334              :          printf("%d: [%s] [%s] [%d]\n", i, event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
    2335              :       }
    2336              :    }
    2337              : 
    2338            0 :    const char** event_name = new const char*[num_var];
    2339            0 :    const char** tag_name = new const char*[num_var];
    2340            0 :    for (unsigned i=0; i<num_var; i++) {
    2341            0 :       event_name[i] = event_names[i].c_str();
    2342            0 :       tag_name[i] = tag_names[i].c_str();
    2343              :    }
    2344              : 
    2345            0 :    int status = mh->hs_read_binned(start_time, end_time, num_bins, num_var, event_name, tag_name, var_index, num_entries, count_bins, mean_bins, rms_bins, min_bins, max_bins, bins_first_time, bins_first_value, bins_last_time, bins_last_value, last_time, last_value, hs_status);
    2346              : 
    2347            0 :    for (unsigned i=0; i<num_var; i++) {
    2348            0 :       MJsonNode* obj = MJsonNode::MakeObject();
    2349            0 :       obj->AddToObject("status", MJsonNode::MakeInt(hs_status[i]));
    2350            0 :       obj->AddToObject("num_entries", MJsonNode::MakeInt(num_entries[i]));
    2351              :       
    2352            0 :       MJsonNode* a1 = MJsonNode::MakeArray();
    2353            0 :       MJsonNode* a2 = MJsonNode::MakeArray();
    2354            0 :       MJsonNode* a3 = MJsonNode::MakeArray();
    2355            0 :       MJsonNode* a4 = MJsonNode::MakeArray();
    2356            0 :       MJsonNode* a5 = MJsonNode::MakeArray();
    2357              : 
    2358            0 :       MJsonNode* b1 = MJsonNode::MakeArray();
    2359            0 :       MJsonNode* b2 = MJsonNode::MakeArray();
    2360            0 :       MJsonNode* b3 = MJsonNode::MakeArray();
    2361            0 :       MJsonNode* b4 = MJsonNode::MakeArray();
    2362              : 
    2363            0 :       for (int j=0; j<num_bins; j++) {
    2364            0 :          a1->AddToArray(MJsonNode::MakeInt(count_bins[i][j]));
    2365            0 :          a2->AddToArray(MJsonNode::MakeNumber(mean_bins[i][j]));
    2366            0 :          a3->AddToArray(MJsonNode::MakeNumber(rms_bins[i][j]));
    2367            0 :          a4->AddToArray(MJsonNode::MakeNumber(min_bins[i][j]));
    2368            0 :          a5->AddToArray(MJsonNode::MakeNumber(max_bins[i][j]));
    2369              : 
    2370            0 :          b1->AddToArray(MJsonNode::MakeNumber(bins_first_time[i][j]));
    2371            0 :          b2->AddToArray(MJsonNode::MakeNumber(bins_first_value[i][j]));
    2372            0 :          b3->AddToArray(MJsonNode::MakeNumber(bins_last_time[i][j]));
    2373            0 :          b4->AddToArray(MJsonNode::MakeNumber(bins_last_value[i][j]));
    2374              :       }
    2375              : 
    2376            0 :       obj->AddToObject("count", a1);
    2377            0 :       obj->AddToObject("mean", a2);
    2378            0 :       obj->AddToObject("rms", a3);
    2379            0 :       obj->AddToObject("min", a4);
    2380            0 :       obj->AddToObject("max", a5);
    2381            0 :       obj->AddToObject("bins_first_time", b1);
    2382            0 :       obj->AddToObject("bins_first_value", b2);
    2383            0 :       obj->AddToObject("bins_last_time", b3);
    2384            0 :       obj->AddToObject("bins_last_value", b4);
    2385            0 :       obj->AddToObject("last_time", MJsonNode::MakeNumber(last_time[i]));
    2386            0 :       obj->AddToObject("last_value", MJsonNode::MakeNumber(last_value[i]));
    2387            0 :       data->AddToArray(obj);
    2388              : 
    2389            0 :       delete count_bins[i];
    2390            0 :       delete mean_bins[i];
    2391            0 :       delete rms_bins[i];
    2392            0 :       delete min_bins[i];
    2393            0 :       delete max_bins[i];
    2394              : 
    2395            0 :       delete bins_first_time[i];
    2396            0 :       delete bins_first_value[i];
    2397            0 :       delete bins_last_time[i];
    2398            0 :       delete bins_last_value[i];
    2399              :    }
    2400              : 
    2401            0 :    delete[] count_bins;
    2402            0 :    delete[] mean_bins;
    2403            0 :    delete[] rms_bins;
    2404            0 :    delete[] min_bins;
    2405            0 :    delete[] max_bins;
    2406              : 
    2407            0 :    delete[] bins_first_time;
    2408            0 :    delete[] bins_first_value;
    2409            0 :    delete[] bins_last_time;
    2410            0 :    delete[] bins_last_value;
    2411              : 
    2412            0 :    delete[] event_name;
    2413            0 :    delete[] tag_name;
    2414            0 :    delete[] var_index;
    2415              : 
    2416            0 :    delete[] num_entries;
    2417            0 :    delete[] last_time;
    2418            0 :    delete[] last_value;
    2419            0 :    delete[] hs_status;
    2420              : 
    2421            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "data", data);
    2422            0 : }
    2423              : 
    2424              : class BinaryHistoryBuffer: public MidasHistoryBufferInterface
    2425              : {
    2426              : public:
    2427              :    std::vector<double> fTimes;
    2428              :    std::vector<double> fValues;
    2429              : 
    2430              : public:
    2431            0 :    BinaryHistoryBuffer() // ctor
    2432            0 :    {
    2433              :       // empty
    2434            0 :    }
    2435              : 
    2436            0 :    void Add(time_t t, double v)
    2437              :    {
    2438              :       //printf("add time %d, value %f\n", (int)t, v);
    2439              : 
    2440            0 :       fTimes.push_back(t);
    2441            0 :       fValues.push_back(v);
    2442            0 :    }
    2443              : 
    2444            0 :    void Finish()
    2445              :    {
    2446            0 :       assert(fTimes.size() == fValues.size());
    2447            0 :    }
    2448              : };
    2449              : 
    2450            0 : static MJsonNode* js_hs_read_arraybuffer(const MJsonNode* params)
    2451              : {
    2452            0 :    if (!params) {
    2453            0 :       MJSO* doc = MJSO::I();
    2454            0 :       doc->D("get history data for given history events that existed at give time using hs_read_buffer()");
    2455            0 :       doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
    2456            0 :       doc->P("start_time", MJSON_NUMBER, "start time of the data");
    2457            0 :       doc->P("end_time", MJSON_NUMBER, "end time of the data");
    2458            0 :       doc->P("events[]", MJSON_STRING, "array of history event names");
    2459            0 :       doc->P("tags[]", MJSON_STRING, "array of history event tag names");
    2460            0 :       doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
    2461            0 :       doc->R("binary data", MJSON_ARRAYBUFFER, "binary data, see documentation");
    2462            0 :       return doc;
    2463              :    }
    2464              : 
    2465            0 :    MJsonNode* error = NULL;
    2466              : 
    2467            0 :    std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
    2468            0 :    double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
    2469            0 :    double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
    2470              : 
    2471            0 :    const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
    2472            0 :    const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
    2473            0 :    const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
    2474              : 
    2475            0 :    MidasHistoryInterface* mh = GetHistory(channel.c_str());
    2476              : 
    2477            0 :    if (!mh) {
    2478            0 :       int status = HS_FILE_ERROR;
    2479            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    2480              :    }
    2481              : 
    2482            0 :    size_t num_var = events_array->size();
    2483              : 
    2484            0 :    if (tags_array->size() != num_var) {
    2485            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
    2486              :    }
    2487              : 
    2488            0 :    if (index_array->size() != num_var) {
    2489            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
    2490              :    }
    2491              : 
    2492            0 :    std::vector<std::string> event_names(num_var);
    2493            0 :    std::vector<std::string> tag_names(num_var);
    2494            0 :    int* var_index = new int[num_var];
    2495            0 :    BinaryHistoryBuffer** jbuf = new BinaryHistoryBuffer*[num_var];
    2496            0 :    MidasHistoryBufferInterface** buf = new MidasHistoryBufferInterface*[num_var];
    2497            0 :    int* hs_status = new int[num_var];
    2498              : 
    2499            0 :    for (size_t i=0; i<num_var; i++) {
    2500              :       //event_name[i] = (*events_array)[i]->GetString().c_str();
    2501              :       //tag_name[i] = (*tags_array)[i]->GetString().c_str();
    2502            0 :       event_names[i] = (*events_array)[i]->GetString();
    2503            0 :       tag_names[i] = (*tags_array)[i]->GetString();
    2504            0 :       var_index[i] = (*index_array)[i]->GetInt();
    2505            0 :       jbuf[i] = new BinaryHistoryBuffer();
    2506            0 :       buf[i] = jbuf[i];
    2507            0 :       hs_status[i] = 0;
    2508              :    }
    2509              : 
    2510              :    if (/* DISABLES CODE */ (0)) {
    2511              :       printf("time %f %f, num_vars %d:\n", start_time, end_time, int(num_var));
    2512              :       for (size_t i=0; i<num_var; i++) {
    2513              :          printf("%d: [%s] [%s] [%d]\n", int(i), event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
    2514              :       }
    2515              :    }
    2516              : 
    2517            0 :    const char** event_name = new const char*[num_var];
    2518            0 :    const char** tag_name = new const char*[num_var];
    2519            0 :    for (unsigned i=0; i<num_var; i++) {
    2520            0 :       event_name[i] = event_names[i].c_str();
    2521            0 :       tag_name[i] = tag_names[i].c_str();
    2522              :    }
    2523              : 
    2524            0 :    int status = mh->hs_read_buffer(start_time, end_time, num_var, event_name, tag_name, var_index, buf, hs_status);
    2525              : 
    2526            0 :    size_t num_values = 0;
    2527              : 
    2528            0 :    for (unsigned i=0; i<num_var; i++) {
    2529            0 :       jbuf[i]->Finish();
    2530            0 :       num_values += jbuf[i]->fValues.size();
    2531              :    }
    2532              : 
    2533              :    // NB: beware of 32-bit integer overflow. all values are now 64-bit size_t, overflow should not happen.
    2534            0 :    size_t p0_size = sizeof(double)*(2+2*num_var+2*num_values);
    2535              : 
    2536            0 :    size_t size_limit = 1000*1024*1024;
    2537              : 
    2538            0 :    if (p0_size > size_limit) {
    2539            0 :       cm_msg(MERROR, "js_hs_read_binned_arraybuffer", "Refusing to return %zu bytes of history data, limit is %zu bytes\n", p0_size, size_limit);
    2540              : 
    2541            0 :       for (size_t i=0; i<num_var; i++) {
    2542            0 :          delete jbuf[i];
    2543            0 :          jbuf[i] = NULL;
    2544            0 :          buf[i] = NULL;
    2545              :       }
    2546              :       
    2547            0 :       delete[] event_name;
    2548            0 :       delete[] tag_name;
    2549            0 :       delete[] var_index;
    2550            0 :       delete[] buf;
    2551            0 :       delete[] jbuf;
    2552            0 :       delete[] hs_status;
    2553              :       
    2554            0 :       return mjsonrpc_make_error(-32603, "Internal error", "Too much history data");
    2555              :    }
    2556              : 
    2557            0 :    double* p0 = (double*)malloc(p0_size);
    2558              : 
    2559            0 :    if (p0 == NULL) {
    2560            0 :       cm_msg(MERROR, "js_hs_read_binned_arraybuffer", "Cannot allocate return buffer %d bytes\n", int(p0_size));
    2561              :       
    2562            0 :       for (size_t i=0; i<num_var; i++) {
    2563            0 :          delete jbuf[i];
    2564            0 :          jbuf[i] = NULL;
    2565            0 :          buf[i] = NULL;
    2566              :       }
    2567              :       
    2568            0 :       delete[] event_name;
    2569            0 :       delete[] tag_name;
    2570            0 :       delete[] var_index;
    2571            0 :       delete[] buf;
    2572            0 :       delete[] jbuf;
    2573            0 :       delete[] hs_status;
    2574              :       
    2575            0 :       return mjsonrpc_make_error(-32603, "Internal error", "Cannot allocate buffer, too much data");
    2576              :    }
    2577              : 
    2578            0 :    double *pptr = p0;
    2579              :    
    2580              :    //
    2581              :    // Binary data format:
    2582              :    //
    2583              :    // - hs_read() status
    2584              :    // - num_var
    2585              :    // - hs_status[0..num_var-1]
    2586              :    // - num_values[0..num_var-1]
    2587              :    // - data for var0:
    2588              :    // - t[0][0], v[0][0] ... t[0][num_values[0]-1], v[0][num_values[0]-1]
    2589              :    // - data for var1:
    2590              :    // - t[1][0], v[1][0] ... t[1][num_values[1]-1], v[1][num_values[1]-1]
    2591              :    // ...
    2592              :    // - data for last variable:
    2593              :    // - t[num_var-1][0], v[num_var-1][0] ... t[num_var-1][num_values[num_var-1]-1], v[num_var-1][num_values[num_var-1]-1]
    2594              :    //
    2595              : 
    2596            0 :    *pptr++ = status;
    2597            0 :    *pptr++ = num_var;
    2598              : 
    2599            0 :    for (size_t i=0; i<num_var; i++) {
    2600            0 :       *pptr++ = hs_status[i];
    2601              :    }
    2602              : 
    2603            0 :    for (size_t i=0; i<num_var; i++) {
    2604            0 :       *pptr++ = jbuf[i]->fValues.size();
    2605              :    }
    2606              : 
    2607            0 :    for (size_t i=0; i<num_var; i++) {
    2608            0 :       size_t nv = jbuf[i]->fValues.size();
    2609            0 :       for (size_t j=0; j<nv; j++) {
    2610            0 :          *pptr++ = jbuf[i]->fTimes[j];
    2611            0 :          *pptr++ = jbuf[i]->fValues[j];
    2612              :       }
    2613              : 
    2614            0 :       delete jbuf[i];
    2615            0 :       jbuf[i] = NULL;
    2616            0 :       buf[i] = NULL;
    2617              :    }
    2618              : 
    2619              :    //printf("p0_size %d, %d/%d\n", (int)p0_size, (int)(pptr-p0), (int)((pptr-p0)*sizeof(double)));
    2620              : 
    2621            0 :    assert(p0_size == ((pptr-p0)*sizeof(double)));
    2622              : 
    2623            0 :    delete[] event_name;
    2624            0 :    delete[] tag_name;
    2625            0 :    delete[] var_index;
    2626            0 :    delete[] buf;
    2627            0 :    delete[] jbuf;
    2628            0 :    delete[] hs_status;
    2629              : 
    2630            0 :    MJsonNode* result = MJsonNode::MakeArrayBuffer((char*)p0, p0_size);
    2631              : 
    2632            0 :    return result;
    2633            0 : }
    2634              : 
    2635            0 : static MJsonNode* js_hs_read_binned_arraybuffer(const MJsonNode* params)
    2636              : {
    2637            0 :    if (!params) {
    2638            0 :       MJSO* doc = MJSO::I();
    2639            0 :       doc->D("get history data for given history events that existed at give time using hs_read_buffer()");
    2640            0 :       doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
    2641            0 :       doc->P("start_time", MJSON_NUMBER, "start time of the data");
    2642            0 :       doc->P("end_time", MJSON_NUMBER, "end time of the data");
    2643            0 :       doc->P("num_bins", MJSON_INT, "number of time bins");
    2644            0 :       doc->P("events[]", MJSON_STRING, "array of history event names");
    2645            0 :       doc->P("tags[]", MJSON_STRING, "array of history event tag names");
    2646            0 :       doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
    2647            0 :       doc->R("binary data", MJSON_ARRAYBUFFER, "binary data, see documentation");
    2648            0 :       return doc;
    2649              :    }
    2650              : 
    2651            0 :    MJsonNode* error = NULL;
    2652              : 
    2653            0 :    std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
    2654            0 :    double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
    2655            0 :    double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
    2656            0 :    int inum_bins = mjsonrpc_get_param(params, "num_bins", &error)->GetInt(); if (error) return error;
    2657              : 
    2658            0 :    if (inum_bins < 1) {
    2659            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Value of num_bins should be 1 or more");
    2660              :    }
    2661              : 
    2662            0 :    size_t num_bins = inum_bins;
    2663              : 
    2664            0 :    const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
    2665            0 :    const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
    2666            0 :    const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
    2667              : 
    2668            0 :    MidasHistoryInterface* mh = GetHistory(channel.c_str());
    2669              : 
    2670            0 :    if (!mh) {
    2671            0 :       int status = HS_FILE_ERROR;
    2672            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    2673              :    }
    2674              : 
    2675            0 :    size_t num_var = events_array->size();
    2676              : 
    2677            0 :    if (num_var < 1) {
    2678            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Array of events should have 1 or more elements");
    2679              :    }
    2680              : 
    2681            0 :    if (tags_array->size() != num_var) {
    2682            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
    2683              :    }
    2684              : 
    2685            0 :    if (index_array->size() != num_var) {
    2686            0 :       return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
    2687              :    }
    2688              :    
    2689            0 :    std::vector<std::string> event_names(num_var);
    2690            0 :    std::vector<std::string> tag_names(num_var);
    2691              :    //const char** event_name = new const char*[num_var];
    2692              :    //const char** tag_name = new const char*[num_var];
    2693            0 :    int* var_index = new int[num_var];
    2694              : 
    2695            0 :    int* num_entries = new int[num_var];
    2696            0 :    time_t* last_time = new time_t[num_var];
    2697            0 :    double* last_value = new double[num_var];
    2698            0 :    int* hs_status = new int[num_var];
    2699              : 
    2700            0 :    int** count_bins = new int*[num_var];
    2701            0 :    double** mean_bins = new double*[num_var];
    2702            0 :    double** rms_bins = new double*[num_var];
    2703            0 :    double** min_bins = new double*[num_var];
    2704            0 :    double** max_bins = new double*[num_var];
    2705              : 
    2706            0 :    time_t** bins_first_time = new time_t*[num_var];
    2707            0 :    time_t** bins_last_time  = new time_t*[num_var];
    2708              : 
    2709            0 :    double** bins_first_value = new double*[num_var];
    2710            0 :    double** bins_last_value  = new double*[num_var];
    2711              : 
    2712            0 :    for (unsigned i=0; i<num_var; i++) {
    2713              :       //event_name[i] = (*events_array)[i]->GetString().c_str();
    2714              :       //tag_name[i] = (*tags_array)[i]->GetString().c_str();
    2715            0 :       event_names[i] = (*events_array)[i]->GetString();
    2716            0 :       tag_names[i] = (*tags_array)[i]->GetString();
    2717            0 :       var_index[i] = (*index_array)[i]->GetInt();
    2718            0 :       num_entries[i] = 0;
    2719            0 :       last_time[i] = 0;
    2720            0 :       last_value[i] = 0;
    2721            0 :       hs_status[i] = 0;
    2722            0 :       count_bins[i] = new int[num_bins];
    2723            0 :       mean_bins[i] = new double[num_bins];
    2724            0 :       rms_bins[i] = new double[num_bins];
    2725            0 :       min_bins[i] = new double[num_bins];
    2726            0 :       max_bins[i] = new double[num_bins];
    2727            0 :       bins_first_time[i] = new time_t[num_bins];
    2728            0 :       bins_last_time[i] = new time_t[num_bins];
    2729            0 :       bins_first_value[i] = new double[num_bins];
    2730            0 :       bins_last_value[i] = new double[num_bins];
    2731              :    }
    2732              : 
    2733              :    if (/* DISABLES CODE */ (0)) {
    2734              :       printf("time %f %f, num_vars %d:\n", start_time, end_time, int(num_var));
    2735              :       for (size_t i=0; i<num_var; i++) {
    2736              :          printf("%d: [%s] [%s] [%d]\n", int(i), event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
    2737              :       }
    2738              :    }
    2739              : 
    2740            0 :    const char** event_name = new const char*[num_var];
    2741            0 :    const char** tag_name = new const char*[num_var];
    2742            0 :    for (size_t i=0; i<num_var; i++) {
    2743            0 :       event_name[i] = event_names[i].c_str();
    2744            0 :       tag_name[i] = tag_names[i].c_str();
    2745              :    }
    2746              : 
    2747            0 :    int status = mh->hs_read_binned(start_time, end_time, num_bins, num_var, event_name, tag_name, var_index, num_entries, count_bins, mean_bins, rms_bins, min_bins, max_bins, bins_first_time, bins_first_value, bins_last_time, bins_last_value, last_time, last_value, hs_status);
    2748              : 
    2749              :    // NB: beware of 32-bit integer overflow: all variables are now 64-bit size_t, overflow should not happen
    2750            0 :    size_t p0_size = sizeof(double)*(5+4*num_var+9*num_var*num_bins);
    2751              : 
    2752            0 :    size_t size_limit = 100*1024*1024;
    2753              : 
    2754            0 :    if (p0_size > size_limit) {
    2755            0 :       cm_msg(MERROR, "js_hs_read_binned_arraybuffer", "Refusing to return %d bytes. limit is %d bytes\n", int(p0_size), int(size_limit));
    2756              : 
    2757            0 :       for (size_t i=0; i<num_var; i++) {
    2758            0 :          delete count_bins[i];
    2759            0 :          delete mean_bins[i];
    2760            0 :          delete rms_bins[i];
    2761            0 :          delete min_bins[i];
    2762            0 :          delete max_bins[i];
    2763            0 :          delete bins_first_time[i];
    2764            0 :          delete bins_first_value[i];
    2765            0 :          delete bins_last_time[i];
    2766            0 :          delete bins_last_value[i];
    2767              :       }
    2768              : 
    2769            0 :       delete[] count_bins;
    2770            0 :       delete[] mean_bins;
    2771            0 :       delete[] rms_bins;
    2772            0 :       delete[] min_bins;
    2773            0 :       delete[] max_bins;
    2774              :       
    2775            0 :       delete[] bins_first_time;
    2776            0 :       delete[] bins_first_value;
    2777            0 :       delete[] bins_last_time;
    2778            0 :       delete[] bins_last_value;
    2779              :       
    2780            0 :       delete[] event_name;
    2781            0 :       delete[] tag_name;
    2782            0 :       delete[] var_index;
    2783              :       
    2784            0 :       delete[] num_entries;
    2785            0 :       delete[] last_time;
    2786            0 :       delete[] last_value;
    2787            0 :       delete[] hs_status;
    2788              : 
    2789            0 :       return mjsonrpc_make_error(-32603, "Internal error", "Refuse to return too much data");
    2790              :    }
    2791              : 
    2792            0 :    double* p0 = (double*)malloc(p0_size);
    2793              : 
    2794            0 :    if (p0 == NULL) {
    2795            0 :       cm_msg(MERROR, "js_hs_read_binned_arraybuffer", "Cannot allocate return buffer %d bytes\n", int(p0_size));
    2796              : 
    2797            0 :       for (size_t i=0; i<num_var; i++) {
    2798            0 :          delete count_bins[i];
    2799            0 :          delete mean_bins[i];
    2800            0 :          delete rms_bins[i];
    2801            0 :          delete min_bins[i];
    2802            0 :          delete max_bins[i];
    2803            0 :          delete bins_first_time[i];
    2804            0 :          delete bins_first_value[i];
    2805            0 :          delete bins_last_time[i];
    2806            0 :          delete bins_last_value[i];
    2807              :       }
    2808              : 
    2809            0 :       delete[] count_bins;
    2810            0 :       delete[] mean_bins;
    2811            0 :       delete[] rms_bins;
    2812            0 :       delete[] min_bins;
    2813            0 :       delete[] max_bins;
    2814              : 
    2815            0 :       delete[] bins_first_time;
    2816            0 :       delete[] bins_first_value;
    2817            0 :       delete[] bins_last_time;
    2818            0 :       delete[] bins_last_value;
    2819              :       
    2820            0 :       delete[] event_name;
    2821            0 :       delete[] tag_name;
    2822            0 :       delete[] var_index;
    2823              :       
    2824            0 :       delete[] num_entries;
    2825            0 :       delete[] last_time;
    2826            0 :       delete[] last_value;
    2827            0 :       delete[] hs_status;
    2828              : 
    2829            0 :       return mjsonrpc_make_error(-32603, "Internal error", "Cannot allocate buffer, too much data");
    2830              :    }
    2831              : 
    2832            0 :    double *pptr = p0;
    2833              :    
    2834              :    //
    2835              :    // Binary data format:
    2836              :    //
    2837              :    // * header
    2838              :    // -- hs_read() status
    2839              :    // -- start_time
    2840              :    // -- end_time
    2841              :    // -- num_bins
    2842              :    // -- num_var
    2843              :    // * per variable info
    2844              :    // -- hs_status[0..num_var-1]
    2845              :    // -- num_entries[0..num_var-1]
    2846              :    // -- last_time[0..num_var-1]
    2847              :    // -- last_value[0..num_var-1]
    2848              :    // * data for var0 bin0
    2849              :    // -- count - number of entries in this bin
    2850              :    // -- mean - mean value
    2851              :    // -- rms - rms value
    2852              :    // -- min - minimum value
    2853              :    // -- max - maximum value
    2854              :    // -- bins_first_time - first data point in each bin
    2855              :    // -- bins_first_value
    2856              :    // -- bins_last_time - last data point in each bin
    2857              :    // -- bins_last_value
    2858              :    // - data for var0 bin1
    2859              :    // - ... bin[num_bins-1]
    2860              :    // - data for var1 bin0
    2861              :    // - ...
    2862              :    // - data for var[num_vars-1] bin[0]
    2863              :    // - ...
    2864              :    // - data for var[num_vars-1] bin[num_bins-1]
    2865              :    //
    2866              : 
    2867            0 :    *pptr++ = status;
    2868            0 :    *pptr++ = start_time;
    2869            0 :    *pptr++ = end_time;
    2870            0 :    *pptr++ = num_bins;
    2871            0 :    *pptr++ = num_var;
    2872              : 
    2873            0 :    for (unsigned i=0; i<num_var; i++) {
    2874            0 :       *pptr++ = hs_status[i];
    2875              :    }
    2876              : 
    2877            0 :    for (unsigned i=0; i<num_var; i++) {
    2878            0 :       *pptr++ = num_entries[i];
    2879              :    }
    2880              : 
    2881            0 :    for (unsigned i=0; i<num_var; i++) {
    2882            0 :       *pptr++ = last_time[i];
    2883              :    }
    2884              : 
    2885            0 :    for (unsigned i=0; i<num_var; i++) {
    2886            0 :       *pptr++ = last_value[i];
    2887              :    }
    2888              : 
    2889            0 :    for (size_t i=0; i<num_var; i++) {
    2890            0 :       for (size_t j=0; j<num_bins; j++) {
    2891            0 :          *pptr++ = count_bins[i][j];
    2892            0 :          *pptr++ = mean_bins[i][j];
    2893            0 :          *pptr++ = rms_bins[i][j];
    2894            0 :          *pptr++ = min_bins[i][j];
    2895            0 :          *pptr++ = max_bins[i][j];
    2896            0 :          *pptr++ = bins_first_time[i][j];
    2897            0 :          *pptr++ = bins_first_value[i][j];
    2898            0 :          *pptr++ = bins_last_time[i][j];
    2899            0 :          *pptr++ = bins_last_value[i][j];
    2900              :       }
    2901              : 
    2902            0 :       delete count_bins[i];
    2903            0 :       delete mean_bins[i];
    2904            0 :       delete rms_bins[i];
    2905            0 :       delete min_bins[i];
    2906            0 :       delete max_bins[i];
    2907            0 :       delete bins_first_time[i];
    2908            0 :       delete bins_first_value[i];
    2909            0 :       delete bins_last_time[i];
    2910            0 :       delete bins_last_value[i];
    2911              :    }
    2912              : 
    2913              :    //printf("p0_size %d, %d/%d\n", (int)p0_size, (int)(pptr-p0), (int)((pptr-p0)*sizeof(double)));
    2914              : 
    2915            0 :    assert(p0_size == ((pptr-p0)*sizeof(double)));
    2916              : 
    2917            0 :    delete[] count_bins;
    2918            0 :    delete[] mean_bins;
    2919            0 :    delete[] rms_bins;
    2920            0 :    delete[] min_bins;
    2921            0 :    delete[] max_bins;
    2922              : 
    2923            0 :    delete[] bins_first_time;
    2924            0 :    delete[] bins_first_value;
    2925            0 :    delete[] bins_last_time;
    2926            0 :    delete[] bins_last_value;
    2927              : 
    2928            0 :    delete[] event_name;
    2929            0 :    delete[] tag_name;
    2930            0 :    delete[] var_index;
    2931              : 
    2932            0 :    delete[] num_entries;
    2933            0 :    delete[] last_time;
    2934            0 :    delete[] last_value;
    2935            0 :    delete[] hs_status;
    2936              : 
    2937            0 :    MJsonNode* result = MJsonNode::MakeArrayBuffer((char*)p0, p0_size);
    2938              : 
    2939            0 :    return result;
    2940            0 : }
    2941              : 
    2942              : /////////////////////////////////////////////////////////////////////////////////
    2943              : //
    2944              : // image history code goes here
    2945              : //
    2946              : /////////////////////////////////////////////////////////////////////////////////
    2947              : 
    2948            0 : static MJsonNode* js_hs_image_retrieve(const MJsonNode* params) {
    2949            0 :    if (!params) {
    2950            0 :       MJSO *doc = MJSO::I();
    2951            0 :       doc->D("Get a list of history image files");
    2952            0 :       doc->P("image?", MJSON_STRING, "image name as defined under /History/Images/<image>");
    2953            0 :       doc->P("start_time", MJSON_NUMBER, "start time of the data");
    2954            0 :       doc->P("end_time", MJSON_NUMBER, "end time of the data");
    2955            0 :       doc->R("time[]", MJSON_ARRAYBUFFER, "array of time stamps in seconds");
    2956            0 :       doc->R("filename[]", MJSON_ARRAYBUFFER, "array of file names");
    2957            0 :       return doc;
    2958              :    }
    2959              : 
    2960            0 :    MJsonNode* error = NULL;
    2961              : 
    2962            0 :    std::string image = mjsonrpc_get_param(params, "image", NULL)->GetString();
    2963            0 :    double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
    2964            0 :    double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
    2965              : 
    2966            0 :    std::vector<time_t>vtime{};
    2967            0 :    std::vector<std::string>vfilename{};
    2968              : 
    2969            0 :    int status = hs_image_retrieve(image, start_time, end_time, vtime, vfilename);
    2970            0 :    int count = 10;
    2971            0 :    MJsonNode *tj = MJsonNode::MakeArray();
    2972            0 :    MJsonNode *fj = MJsonNode::MakeArray();
    2973              : 
    2974            0 :    for (int i=0 ; i<(int)vtime.size() ; i++) {
    2975            0 :       tj->AddToArray(MJsonNode::MakeInt(vtime[i]));
    2976            0 :       ss_repair_utf8(vfilename[i]);
    2977            0 :       fj->AddToArray(MJsonNode::MakeString(vfilename[i].c_str()));
    2978              :    }
    2979            0 :    MJsonNode* data = MJsonNode::MakeObject();
    2980            0 :    data->AddToObject("count", MJsonNode::MakeInt(count));
    2981            0 :    data->AddToObject("time", tj);
    2982            0 :    data->AddToObject("filename", fj);
    2983              : 
    2984            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "data", data);
    2985            0 : }
    2986              : 
    2987              : /////////////////////////////////////////////////////////////////////////////////
    2988              : //
    2989              : // elog code goes here
    2990              : //
    2991              : /////////////////////////////////////////////////////////////////////////////////
    2992              : 
    2993            0 : static MJsonNode* js_el_retrieve(const MJsonNode* params)
    2994              : {
    2995            0 :    if (!params) {
    2996            0 :       MJSO* doc = MJSO::I();
    2997            0 :       doc->D("Get an elog message");
    2998            0 :       doc->P("tag", MJSON_STRING, "elog message tag");
    2999            0 :       doc->R("status", MJSON_INT, "return status of el_retrieve");
    3000            0 :       doc->R("msg.tag", MJSON_STRING, "message tag");
    3001            0 :       return doc;
    3002              :    }
    3003              : 
    3004            0 :    MJsonNode* error = NULL;
    3005              : 
    3006            0 :    std::string tag = mjsonrpc_get_param(params, "tag", &error)->GetString(); if (error) return error;
    3007              : 
    3008            0 :    int run = 0;
    3009              :    char date[80], author[80], type[80], system[80], subject[256], text[10000];
    3010              :    char orig_tag[80], reply_tag[80], attachment[3][256], encoding[80];
    3011              : 
    3012              :    char xtag[80];
    3013            0 :    mstrlcpy(xtag, tag.c_str(), sizeof(xtag));
    3014              : 
    3015            0 :    int size = sizeof(text);
    3016              : 
    3017            0 :    int status = el_retrieve(xtag,
    3018              :                             date, &run, author, type, system, subject,
    3019              :                             text, &size, orig_tag, reply_tag,
    3020              :                             attachment[0], attachment[1], attachment[2], encoding);
    3021              : 
    3022              :    //printf("js_el_retrieve: size %d, status %d, tag [%s]\n", size, status, xtag);
    3023              : 
    3024            0 :    MJsonNode* msg = MJsonNode::MakeObject();
    3025              : 
    3026            0 :    if (status == EL_SUCCESS) {
    3027            0 :       ss_repair_utf8(xtag);
    3028            0 :       msg->AddToObject("tag", MJsonNode::MakeString(xtag));
    3029            0 :       ss_repair_utf8(date);
    3030            0 :       msg->AddToObject("date", MJsonNode::MakeString(date));
    3031            0 :       msg->AddToObject("run", MJsonNode::MakeInt(run));
    3032            0 :       ss_repair_utf8(author);
    3033            0 :       msg->AddToObject("author", MJsonNode::MakeString(author));
    3034            0 :       ss_repair_utf8(type);
    3035            0 :       msg->AddToObject("type", MJsonNode::MakeString(type));
    3036            0 :       ss_repair_utf8(system);
    3037            0 :       msg->AddToObject("system", MJsonNode::MakeString(system));
    3038            0 :       ss_repair_utf8(subject);
    3039            0 :       msg->AddToObject("subject", MJsonNode::MakeString(subject));
    3040            0 :       ss_repair_utf8(text);
    3041            0 :       msg->AddToObject("text", MJsonNode::MakeString(text));
    3042            0 :       ss_repair_utf8(orig_tag);
    3043            0 :       msg->AddToObject("orig_tag", MJsonNode::MakeString(orig_tag));
    3044            0 :       ss_repair_utf8(reply_tag);
    3045            0 :       msg->AddToObject("reply_tag", MJsonNode::MakeString(reply_tag));
    3046            0 :       ss_repair_utf8(attachment[0]);
    3047            0 :       msg->AddToObject("attachment0", MJsonNode::MakeString(attachment[0]));
    3048            0 :       ss_repair_utf8(attachment[1]);
    3049            0 :       msg->AddToObject("attachment1", MJsonNode::MakeString(attachment[1]));
    3050            0 :       ss_repair_utf8(attachment[2]);
    3051            0 :       msg->AddToObject("attachment2", MJsonNode::MakeString(attachment[2]));
    3052            0 :       ss_repair_utf8(encoding);
    3053            0 :       msg->AddToObject("encoding", MJsonNode::MakeString(encoding));
    3054              :    }
    3055              : 
    3056            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "msg", msg);
    3057            0 : }
    3058              : 
    3059            0 : static MJsonNode* js_el_query(const MJsonNode* params)
    3060              : {
    3061            0 :    if (!params) {
    3062            0 :       MJSO* doc = MJSO::I();
    3063            0 :       doc->D("Query elog messages");
    3064            0 :       doc->P("last_n_hours?", MJSON_INT, "return messages from the last N hours");
    3065            0 :       doc->R("status", MJSON_INT, "return status of el_retrieve");
    3066            0 :       doc->R("msg[].tag", MJSON_STRING, "message tag");
    3067            0 :       return doc;
    3068              :    }
    3069              : 
    3070              :    //MJsonNode* error = NULL;
    3071              : 
    3072              :    //int last_n = mjsonrpc_get_param(params, "last_n_hours", &error)->GetInt(); if (error) return error;
    3073            0 :    int last_n = mjsonrpc_get_param(params, "last_n_hours", NULL)->GetInt();
    3074              : 
    3075            0 :    std::string pd1 = mjsonrpc_get_param(params, "d1", NULL)->GetString();
    3076            0 :    std::string pm1 = mjsonrpc_get_param(params, "m1", NULL)->GetString();
    3077            0 :    std::string py1 = mjsonrpc_get_param(params, "y1", NULL)->GetString();
    3078              : 
    3079            0 :    std::string pd2 = mjsonrpc_get_param(params, "d2", NULL)->GetString();
    3080            0 :    std::string pm2 = mjsonrpc_get_param(params, "m2", NULL)->GetString();
    3081            0 :    std::string py2 = mjsonrpc_get_param(params, "y2", NULL)->GetString();
    3082              : 
    3083            0 :    std::string pr1 = mjsonrpc_get_param(params, "r1", NULL)->GetString();
    3084            0 :    std::string pr2 = mjsonrpc_get_param(params, "r2", NULL)->GetString();
    3085              : 
    3086            0 :    std::string ptype = mjsonrpc_get_param(params, "type", NULL)->GetString();
    3087            0 :    std::string psystem = mjsonrpc_get_param(params, "system", NULL)->GetString();
    3088            0 :    std::string pauthor = mjsonrpc_get_param(params, "author", NULL)->GetString();
    3089            0 :    std::string psubject = mjsonrpc_get_param(params, "subject", NULL)->GetString();
    3090            0 :    std::string psubtext = mjsonrpc_get_param(params, "subtext", NULL)->GetString();
    3091              : 
    3092            0 :    MJsonNode* msg_array = MJsonNode::MakeArray();
    3093              : 
    3094              :    int i, size, run, status;
    3095              :    char date[80], author[80], type[80], system[80], subject[256], text[10000],
    3096              :        orig_tag[80], reply_tag[80], attachment[3][256], encoding[80];
    3097              :    char str[256], str2[10000], tag[256];
    3098              :    struct tm tms;
    3099              : 
    3100              :    // month name from midas.c
    3101              :    extern const char *mname[];
    3102              : 
    3103              :    /*---- convert end date to ltime ----*/
    3104              : 
    3105            0 :    int y1 = -1;
    3106            0 :    int m1 = -1;
    3107            0 :    int d1 = -1;
    3108              : 
    3109            0 :    int y2 = -1;
    3110            0 :    int m2 = -1;
    3111            0 :    int d2 = -1;
    3112              : 
    3113            0 :    int r1 = -1;
    3114            0 :    int r2 = -1;
    3115              : 
    3116            0 :    if (pr1.length()>0)
    3117            0 :       r1 = atoi(pr1.c_str());
    3118              : 
    3119            0 :    if (pr2.length()>0)
    3120            0 :       r2 = atoi(pr2.c_str());
    3121              :    
    3122            0 :    time_t ltime_start = 0;
    3123            0 :    time_t ltime_end = 0;
    3124              : 
    3125            0 :    if (!last_n) {
    3126              :       // decode starting date year, day and month
    3127              :       
    3128            0 :       if (py1.length() > 0)
    3129            0 :          y1 = atoi(py1.c_str());
    3130              : 
    3131            0 :       if (pd1.length() > 0)
    3132            0 :          d1 = atoi(pd1.c_str());
    3133              : 
    3134            0 :       mstrlcpy(str, pm1.c_str(), sizeof(str));
    3135            0 :       for (m1 = 0; m1 < 12; m1++)
    3136            0 :          if (equal_ustring(str, mname[m1]))
    3137            0 :             break;
    3138            0 :       if (m1 == 12)
    3139            0 :          m1 = 0;
    3140              : 
    3141            0 :       if (pd2.length() > 0) {
    3142            0 :          d2 = atoi(pd2.c_str());
    3143              :       }
    3144              :          
    3145            0 :       if (py2.length() > 0) {
    3146              :          // decode ending date year, day and month
    3147              : 
    3148            0 :          mstrlcpy(str, pm2.c_str(), sizeof(str));
    3149            0 :          for (m2 = 0; m2 < 12; m2++)
    3150            0 :             if (equal_ustring(str, mname[m2]))
    3151            0 :                break;
    3152            0 :          if (m2 == 12) {
    3153            0 :             m2 = 0;
    3154              :          }
    3155              :       }
    3156              : 
    3157            0 :       if (py2.length() > 0) {
    3158            0 :          y2 = atoi(py2.c_str());
    3159              :       }
    3160              : 
    3161            0 :       if (y2>=0 && m2>=0 && d2>=0) {
    3162            0 :          memset(&tms, 0, sizeof(struct tm));
    3163            0 :          tms.tm_year = y2 % 100;
    3164            0 :          tms.tm_mon = m2;
    3165            0 :          tms.tm_mday = d2;
    3166            0 :          tms.tm_hour = 24;
    3167              :          
    3168            0 :          if (tms.tm_year < 90)
    3169            0 :             tms.tm_year += 100;
    3170            0 :          ltime_end = ss_mktime(&tms);
    3171              :       }
    3172              :    }
    3173              : 
    3174              :    /*---- do query ----*/
    3175              : 
    3176            0 :    tag[0] = 0;
    3177              : 
    3178            0 :    if (last_n) {
    3179            0 :       ss_tzset(); // required for localtime_r()
    3180            0 :       time_t now = time(NULL);
    3181            0 :       ltime_start = now - 3600 * last_n;
    3182              :       struct tm tms;
    3183            0 :       localtime_r(&ltime_start, &tms);
    3184            0 :       sprintf(tag, "%02d%02d%02d.0", tms.tm_year % 100, tms.tm_mon + 1, tms.tm_mday);
    3185            0 :    } else if (r1 > 0) {
    3186              :       /* do run query */
    3187            0 :       el_search_run(r1, tag);
    3188            0 :    } else if (y1>=0 && m1>=0 && d1>=0) {
    3189              :       /* do date-date query */
    3190            0 :       sprintf(tag, "%02d%02d%02d.0", y1 % 100, m1 + 1, d1);
    3191              :    }
    3192              : 
    3193              : #if 0
    3194              :    printf("js_el_query: y1 %d, m1 %d, d1 %d, y2 %d, m2 %d, d2 %d, r1 %d, r2 %d, last_n_hours %d, start time %lu, end time %lu, tag [%s]\n",
    3195              :           y1, m1, d1,
    3196              :           y2, m2, d2,
    3197              :           r1, r2,
    3198              :           last_n,
    3199              :           ltime_start,
    3200              :           ltime_end,
    3201              :           tag);
    3202              : #endif
    3203              : 
    3204              :    do {
    3205            0 :       size = sizeof(text);
    3206            0 :       status = el_retrieve(tag, date, &run, author, type, system, subject,
    3207              :                            text, &size, orig_tag, reply_tag,
    3208              :                            attachment[0], attachment[1], attachment[2], encoding);
    3209              : 
    3210            0 :       std::string this_tag = tag;
    3211              : 
    3212              :       //printf("js_el_query: el_retrieve: size %d, status %d, tag [%s], run %d, tags [%s] [%s]\n", size, status, tag, run, orig_tag, reply_tag);
    3213              :       
    3214            0 :       mstrlcat(tag, "+1", sizeof(tag));
    3215              : 
    3216              :       /* check for end run */
    3217            0 :       if ((r2 > 0) && (r2 < run)) {
    3218            0 :          break;
    3219              :       }
    3220              : 
    3221              :       /* convert date to unix format */
    3222            0 :       memset(&tms, 0, sizeof(struct tm));
    3223            0 :       tms.tm_year = (tag[0] - '0') * 10 + (tag[1] - '0');
    3224            0 :       tms.tm_mon = (tag[2] - '0') * 10 + (tag[3] - '0') - 1;
    3225            0 :       tms.tm_mday = (tag[4] - '0') * 10 + (tag[5] - '0');
    3226            0 :       tms.tm_hour = (date[11] - '0') * 10 + (date[12] - '0');
    3227            0 :       tms.tm_min = (date[14] - '0') * 10 + (date[15] - '0');
    3228            0 :       tms.tm_sec = (date[17] - '0') * 10 + (date[18] - '0');
    3229              : 
    3230            0 :       if (tms.tm_year < 90)
    3231            0 :          tms.tm_year += 100;
    3232              : 
    3233            0 :       time_t ltime_current = ss_mktime(&tms);
    3234              : 
    3235              :       //printf("js_el_query: ltime: start %ld, end %ld, current %ld\n", ltime_start, ltime_end, ltime_current);
    3236              : 
    3237              :       /* check for start date */
    3238            0 :       if (ltime_start > 0)
    3239            0 :          if (ltime_current < ltime_start)
    3240            0 :             continue;
    3241              : 
    3242              :       /* check for end date */
    3243            0 :       if (ltime_end > 0) {
    3244            0 :          if (ltime_current > ltime_end)
    3245            0 :             break;
    3246              :       }
    3247              : 
    3248            0 :       if (status == EL_SUCCESS) {
    3249              :          /* do filtering */
    3250            0 :          if ((ptype.length()>0) && !equal_ustring(ptype.c_str(), type))
    3251            0 :             continue;
    3252            0 :          if ((psystem.length()>0) && !equal_ustring(psystem.c_str(), system))
    3253            0 :             continue;
    3254              : 
    3255            0 :          if (pauthor.length()>0) {
    3256            0 :             mstrlcpy(str, pauthor.c_str(), sizeof(str));
    3257            0 :             for (i = 0; i < (int) strlen(str); i++)
    3258            0 :                str[i] = toupper(str[i]);
    3259            0 :             str[i] = 0;
    3260            0 :             for (i = 0; i < (int) strlen(author) && author[i] != '@'; i++)
    3261            0 :                str2[i] = toupper(author[i]);
    3262            0 :             str2[i] = 0;
    3263              : 
    3264            0 :             if (strstr(str2, str) == NULL)
    3265            0 :                continue;
    3266              :          }
    3267              : 
    3268            0 :          if (psubject.length()>0) {
    3269            0 :             mstrlcpy(str, psubject.c_str(), sizeof(str));
    3270            0 :             for (i = 0; i < (int) strlen(str); i++)
    3271            0 :                str[i] = toupper(str[i]);
    3272            0 :             str[i] = 0;
    3273            0 :             for (i = 0; i < (int) strlen(subject); i++)
    3274            0 :                str2[i] = toupper(subject[i]);
    3275            0 :             str2[i] = 0;
    3276              : 
    3277            0 :             if (strstr(str2, str) == NULL)
    3278            0 :                continue;
    3279              :          }
    3280              : 
    3281            0 :          if (psubtext.length()>0) {
    3282            0 :             mstrlcpy(str, psubtext.c_str(), sizeof(str));
    3283            0 :             for (i = 0; i < (int) strlen(str); i++)
    3284            0 :                str[i] = toupper(str[i]);
    3285            0 :             str[i] = 0;
    3286            0 :             for (i = 0; i < (int) strlen(text); i++)
    3287            0 :                str2[i] = toupper(text[i]);
    3288            0 :             str2[i] = 0;
    3289              : 
    3290            0 :             if (strstr(str2, str) == NULL)
    3291            0 :                continue;
    3292              :          }
    3293              : 
    3294              :          /* filter passed: display line */
    3295              : 
    3296            0 :          MJsonNode* msg = MJsonNode::MakeObject();
    3297              :          
    3298            0 :          ss_repair_utf8(this_tag);
    3299            0 :          msg->AddToObject("tag", MJsonNode::MakeString(this_tag.c_str()));
    3300            0 :          ss_repair_utf8(date);
    3301            0 :          msg->AddToObject("date", MJsonNode::MakeString(date));
    3302            0 :          msg->AddToObject("run", MJsonNode::MakeInt(run));
    3303            0 :          ss_repair_utf8(author);
    3304            0 :          msg->AddToObject("author", MJsonNode::MakeString(author));
    3305            0 :          ss_repair_utf8(type);
    3306            0 :          msg->AddToObject("type", MJsonNode::MakeString(type));
    3307            0 :          ss_repair_utf8(system);
    3308            0 :          msg->AddToObject("system", MJsonNode::MakeString(system));
    3309            0 :          ss_repair_utf8(subject);
    3310            0 :          msg->AddToObject("subject", MJsonNode::MakeString(subject));
    3311            0 :          ss_repair_utf8(text);
    3312            0 :          msg->AddToObject("text", MJsonNode::MakeString(text));
    3313            0 :          ss_repair_utf8(orig_tag);
    3314            0 :          msg->AddToObject("orig_tag", MJsonNode::MakeString(orig_tag));
    3315            0 :          ss_repair_utf8(reply_tag);
    3316            0 :          msg->AddToObject("reply_tag", MJsonNode::MakeString(reply_tag));
    3317            0 :          ss_repair_utf8(attachment[0]);
    3318            0 :          msg->AddToObject("attachment0", MJsonNode::MakeString(attachment[0]));
    3319            0 :          ss_repair_utf8(attachment[1]);
    3320            0 :          msg->AddToObject("attachment1", MJsonNode::MakeString(attachment[1]));
    3321            0 :          ss_repair_utf8(attachment[2]);
    3322            0 :          msg->AddToObject("attachment2", MJsonNode::MakeString(attachment[2]));
    3323            0 :          ss_repair_utf8(encoding);
    3324            0 :          msg->AddToObject("encoding", MJsonNode::MakeString(encoding));
    3325              : 
    3326            0 :          msg_array->AddToArray(msg);
    3327              :       }
    3328              : 
    3329            0 :    } while (status == EL_SUCCESS);
    3330              : 
    3331            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "msg", msg_array);
    3332            0 : }
    3333              : 
    3334            0 : static MJsonNode* js_el_delete(const MJsonNode* params)
    3335              : {
    3336            0 :    if (!params) {
    3337            0 :       MJSO* doc = MJSO::I();
    3338            0 :       doc->D("Delete elog message");
    3339            0 :       doc->P("tag", MJSON_STRING, "tag of message to delete");
    3340            0 :       doc->R("status", MJSON_INT, "return status of el_delete");
    3341            0 :       return doc;
    3342              :    }
    3343              : 
    3344            0 :    MJsonNode* error = NULL;
    3345            0 :    std::string tag = mjsonrpc_get_param(params, "tag", &error)->GetString(); if (error) return error;
    3346            0 :    int status = el_delete_message(tag.c_str());
    3347            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    3348            0 : }
    3349              : 
    3350              :    
    3351              : /////////////////////////////////////////////////////////////////////////////////
    3352              : //
    3353              : // jrpc code goes here
    3354              : //
    3355              : /////////////////////////////////////////////////////////////////////////////////
    3356              : 
    3357            0 : static MJsonNode* jrpc(const MJsonNode* params)
    3358              : {
    3359            0 :    if (!params) {
    3360            0 :       MJSO* doc = MJSO::I();
    3361            0 :       doc->D("make RPC call into frontend program via RPC_JRPC");
    3362            0 :       doc->P("client_name", MJSON_STRING, "Connect to this MIDAS client, see cm_connect_client()");
    3363            0 :       doc->P("cmd", MJSON_STRING, "Command passed to client");
    3364            0 :       doc->P("args", MJSON_STRING, "Parameters passed to client as a string, could be JSON encoded");
    3365            0 :       doc->P("max_reply_length?", MJSON_INT, "Optional maximum length of client reply. MIDAS RPC does not support returning strings of arbitrary length, maximum length has to be known ahead of time.");
    3366            0 :       doc->R("reply", MJSON_STRING, "Reply from client as a string, could be JSON encoded");
    3367            0 :       doc->R("status", MJSON_INT, "return status of cm_connect_client() and rpc_client_call()");
    3368            0 :       return doc;
    3369              :    }
    3370              : 
    3371            0 :    MJsonNode* error = NULL;
    3372              : 
    3373            0 :    std::string name   = mjsonrpc_get_param(params, "client_name", &error)->GetString(); if (error) return error;
    3374            0 :    std::string cmd    = mjsonrpc_get_param(params, "cmd", &error)->GetString(); if (error) return error;
    3375            0 :    std::string args   = mjsonrpc_get_param(params, "args", &error)->GetString(); if (error) return error;
    3376            0 :    int max_reply_length = mjsonrpc_get_param(params, "max_reply_length", NULL)->GetInt();
    3377              : 
    3378              :    int status;
    3379              : 
    3380            0 :    int buf_length = 1024;
    3381              : 
    3382            0 :    if (max_reply_length > buf_length)
    3383            0 :       buf_length = max_reply_length;
    3384              : 
    3385            0 :    char* buf = (char*)malloc(buf_length);
    3386              : 
    3387            0 :    if (buf == NULL) {
    3388            0 :       return mjsonrpc_make_error(-32602, "Invalid params", msprintf("malloc(%d) failed, likely invalid max_reply_length value %d", buf_length, max_reply_length).c_str());
    3389              :    }
    3390              : 
    3391            0 :    buf[0] = 0;
    3392              : 
    3393              :    HNDLE hconn;
    3394              : 
    3395            0 :    status = cm_connect_client(name.c_str(), &hconn);
    3396              : 
    3397            0 :    if (status != RPC_SUCCESS) {
    3398            0 :       free(buf);
    3399            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    3400              :    }
    3401              : 
    3402            0 :    status = rpc_client_call(hconn, RPC_JRPC, cmd.c_str(), args.c_str(), buf, buf_length);
    3403              : 
    3404              :    // disconnect return status ignored on purpose.
    3405              :    // disconnect not needed, there is no limit on number
    3406              :    // of connections. dead and closed connections are reaped
    3407              :    // automatically. K.O. Feb 2021.
    3408              :    // cm_disconnect_client(hconn, FALSE);
    3409              : 
    3410            0 :    if (status != RPC_SUCCESS) {
    3411            0 :       free(buf);
    3412            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    3413              :    }
    3414              : 
    3415            0 :    ss_repair_utf8(buf);
    3416            0 :    MJsonNode* reply = MJsonNode::MakeString(buf);
    3417            0 :    free(buf);
    3418              :    
    3419            0 :    return mjsonrpc_make_result("reply", reply, "status", MJsonNode::MakeInt(SUCCESS));
    3420            0 : }
    3421              : 
    3422              : /////////////////////////////////////////////////////////////////////////////////
    3423              : //
    3424              : // brpc code goes here
    3425              : //
    3426              : /////////////////////////////////////////////////////////////////////////////////
    3427              : 
    3428            0 : static MJsonNode* brpc(const MJsonNode* params)
    3429              : {
    3430            0 :    if (!params) {
    3431            0 :       MJSO* doc = MJSO::I();
    3432            0 :       doc->D("make RPC call into frontend program via RPC_BRPC");
    3433            0 :       doc->P("client_name", MJSON_STRING, "Connect to this MIDAS client, see cm_connect_client()");
    3434            0 :       doc->P("cmd", MJSON_STRING, "Command passed to client");
    3435            0 :       doc->P("args", MJSON_STRING, "Parameters passed to client as a string, could be JSON encoded");
    3436            0 :       doc->P("max_reply_length?", MJSON_INT, "Optional maximum length of client reply. MIDAS RPC does not support returning data of arbitrary length, maximum length has to be known ahead of time.");
    3437            0 :       doc->R("reply", MJSON_STRING, "Reply from client as a string, could be JSON encoded");
    3438            0 :       doc->R("status", MJSON_INT, "return status of cm_connect_client() and rpc_client_call()");
    3439            0 :       return doc;
    3440              :    }
    3441              : 
    3442            0 :    MJsonNode* error = NULL;
    3443              : 
    3444            0 :    std::string name   = mjsonrpc_get_param(params, "client_name", &error)->GetString(); if (error) return error;
    3445            0 :    std::string cmd    = mjsonrpc_get_param(params, "cmd", &error)->GetString(); if (error) return error;
    3446            0 :    std::string args   = mjsonrpc_get_param(params, "args", &error)->GetString(); if (error) return error;
    3447            0 :    int max_reply_length = mjsonrpc_get_param(params, "max_reply_length", NULL)->GetInt();
    3448              : 
    3449              :    int status;
    3450              : 
    3451            0 :    int buf_length = 1024;
    3452              : 
    3453            0 :    if (max_reply_length > buf_length)
    3454            0 :       buf_length = max_reply_length;
    3455              : 
    3456            0 :    char* buf = (char*)malloc(buf_length);
    3457              : 
    3458            0 :    if (buf == NULL) {
    3459            0 :       return mjsonrpc_make_error(-32602, "Invalid params", msprintf("malloc(%d) failed, likely invalid max_reply_length value %d", buf_length, max_reply_length).c_str());
    3460              :    }
    3461              : 
    3462            0 :    buf[0] = 0;
    3463              : 
    3464              :    HNDLE hconn;
    3465              : 
    3466            0 :    status = cm_connect_client(name.c_str(), &hconn);
    3467              : 
    3468            0 :    if (status != RPC_SUCCESS) {
    3469            0 :       free(buf);
    3470            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    3471              :    }
    3472              : 
    3473            0 :    status = rpc_client_call(hconn, RPC_BRPC, cmd.c_str(), args.c_str(), buf, &buf_length);
    3474              : 
    3475              :    // disconnect return status ignored on purpose.
    3476              :    // disconnect not needed, there is no limit on number
    3477              :    // of connections. dead and closed connections are reaped
    3478              :    // automatically. K.O. Feb 2021.
    3479              :    // cm_disconnect_client(hconn, FALSE);
    3480              : 
    3481            0 :    if (status != RPC_SUCCESS) {
    3482            0 :       free(buf);
    3483            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    3484              :    }
    3485              : 
    3486            0 :    return MJsonNode::MakeArrayBuffer(buf, buf_length);
    3487            0 : }
    3488              : 
    3489              : /////////////////////////////////////////////////////////////////////////////////
    3490              : //
    3491              : // Run transition code goes here
    3492              : //
    3493              : /////////////////////////////////////////////////////////////////////////////////
    3494              : 
    3495            0 : static MJsonNode* js_cm_transition(const MJsonNode* params)
    3496              : {
    3497            0 :    if (!params) {
    3498            0 :       MJSO* doc = MJSO::I();
    3499            0 :       doc->D("start and stop runs");
    3500            0 :       doc->P("transition", MJSON_STRING, "requested transition: TR_START, TR_STOP, TR_PAUSE, TR_RESUME");
    3501            0 :       doc->P("run_number?", MJSON_INT, "New run number, value 0 means /runinfo/run_number + 1, default is 0");
    3502            0 :       doc->P("async_flag?", MJSON_INT, "Transition type. Default is multithreaded transition TR_MTHREAD");
    3503            0 :       doc->P("debug_flag?", MJSON_INT, "See cm_transition(), value 1: trace to stdout, value 2: trace to midas.log");
    3504            0 :       doc->R("status", MJSON_INT, "return status of cm_transition()");
    3505            0 :       doc->R("error_string?", MJSON_STRING, "return error string from cm_transition()");
    3506            0 :       return doc;
    3507              :    }
    3508              : 
    3509            0 :    MJsonNode* error = NULL;
    3510              : 
    3511            0 :    std::string xtransition = mjsonrpc_get_param(params, "transition", &error)->GetString(); if (error) return error;
    3512            0 :    int run_number = mjsonrpc_get_param(params, "run_number", NULL)->GetInt();
    3513            0 :    int async_flag = mjsonrpc_get_param(params, "async_flag", NULL)->GetInt();
    3514            0 :    int debug_flag = mjsonrpc_get_param(params, "debug_flag", NULL)->GetInt();
    3515              : 
    3516              :    int status;
    3517              : 
    3518            0 :    int transition = 0;
    3519              : 
    3520            0 :    if (xtransition == "TR_START")
    3521            0 :       transition = TR_START;
    3522            0 :    else if (xtransition == "TR_STOP")
    3523            0 :       transition = TR_STOP;
    3524            0 :    else if (xtransition == "TR_PAUSE")
    3525            0 :       transition = TR_PAUSE;
    3526            0 :    else if (xtransition == "TR_RESUME")
    3527            0 :       transition = TR_RESUME;
    3528              :    else {
    3529            0 :       return mjsonrpc_make_error(15, "invalid value of \"transition\"", xtransition.c_str());
    3530              :    }
    3531              : 
    3532            0 :    if (async_flag == 0)
    3533            0 :       async_flag = TR_MTHREAD;
    3534              : 
    3535              :    char error_str[1024];
    3536              :    
    3537            0 :    status = cm_transition(transition, run_number, error_str, sizeof(error_str), async_flag, debug_flag);
    3538              : 
    3539            0 :    MJsonNode* result = MJsonNode::MakeObject();
    3540              : 
    3541            0 :    result->AddToObject("status", MJsonNode::MakeInt(status));
    3542            0 :    if (strlen(error_str) > 0) {
    3543            0 :       ss_repair_utf8(error_str);
    3544            0 :       result->AddToObject("error_string", MJsonNode::MakeString(error_str));
    3545              :    }
    3546            0 :    return mjsonrpc_make_result(result);
    3547            0 : }
    3548              : 
    3549              : /////////////////////////////////////////////////////////////////////////////////
    3550              : //
    3551              : // Event buffer code goes here
    3552              : //
    3553              : /////////////////////////////////////////////////////////////////////////////////
    3554              : 
    3555            0 : static const EVENT_HEADER* CopyEvent(const EVENT_HEADER* pevent)
    3556              : {
    3557            0 :    size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
    3558              :    //size_t total_size = ALIGN8(event_size);
    3559            0 :    EVENT_HEADER* ptr = (EVENT_HEADER*)malloc(event_size);
    3560            0 :    assert(ptr);
    3561            0 :    memcpy(ptr, pevent, event_size);
    3562            0 :    return ptr;
    3563              : }
    3564              : 
    3565              : struct EventStashEntry
    3566              : {
    3567              :    std::string buffer_name;
    3568              :    int event_id = 0;
    3569              :    int trigger_mask = 0;
    3570              :    const EVENT_HEADER* pevent = NULL;
    3571              : 
    3572            0 :    ~EventStashEntry() // dtor
    3573              :    {
    3574              :       //Print(); printf(", dtor!\n");
    3575            0 :       buffer_name.clear();
    3576            0 :       event_id = 0;
    3577            0 :       trigger_mask = 0;
    3578            0 :       if (pevent)
    3579            0 :          free((void*)pevent);
    3580            0 :       pevent = NULL;
    3581            0 :    }
    3582              : 
    3583            0 :    void ReplaceEvent(const EVENT_HEADER* xpevent)
    3584              :    {
    3585            0 :       if (pevent) {
    3586            0 :          free((void*)pevent);
    3587            0 :          pevent = NULL;
    3588              :       }
    3589            0 :       pevent = CopyEvent(xpevent);
    3590            0 :    }
    3591              : 
    3592              :    void Print() const
    3593              :    {
    3594              :       printf("EventStashEntry: %s,%d,0x%x,%p", buffer_name.c_str(), event_id, trigger_mask, pevent);
    3595              :       if (pevent)
    3596              :          printf(", size %d, serial %d, time %d, event_id %d, trigger_mask %x", pevent->data_size, pevent->serial_number, pevent->time_stamp, pevent->event_id, pevent->trigger_mask);
    3597              :    }
    3598              : };
    3599              : 
    3600              : static std::mutex gEventStashMutex;
    3601              : static std::vector<EventStashEntry*> gEventStash;
    3602              : 
    3603            0 : static void StashEvent(const std::string buffer_name, int event_id, int trigger_mask, const EVENT_HEADER* pevent)
    3604              : {
    3605            0 :    std::lock_guard<std::mutex> guard(gEventStashMutex);
    3606            0 :    bool found = false;
    3607            0 :    for (EventStashEntry* s : gEventStash) {
    3608            0 :       if (s->buffer_name != buffer_name)
    3609            0 :          continue;
    3610            0 :       if (s->event_id == event_id && s->trigger_mask == trigger_mask) {
    3611            0 :          found = true;
    3612            0 :          s->ReplaceEvent(pevent);
    3613              :          //s->Print(); printf(", replaced\n");
    3614            0 :       } else if (bm_match_event(s->event_id, s->trigger_mask, pevent)) {
    3615            0 :          s->ReplaceEvent(pevent);
    3616              :          //s->Print(); printf(", matched\n");
    3617              :       }
    3618              :    }
    3619            0 :    if (!found) {
    3620            0 :       EventStashEntry* s = new EventStashEntry();
    3621            0 :       s->buffer_name = buffer_name;
    3622            0 :       s->event_id = event_id;
    3623            0 :       s->trigger_mask = trigger_mask;
    3624            0 :       s->pevent = CopyEvent(pevent);
    3625              :       //s->Print(); printf(", added\n");
    3626            0 :       gEventStash.push_back(s);
    3627              :    }
    3628            0 : }
    3629              : 
    3630            0 : static void MatchEvent(const std::string buffer_name, const EVENT_HEADER* pevent)
    3631              : {
    3632            0 :    std::lock_guard<std::mutex> guard(gEventStashMutex);
    3633            0 :    for (EventStashEntry* s : gEventStash) {
    3634            0 :       if (s->buffer_name != buffer_name)
    3635            0 :          continue;
    3636            0 :       if (bm_match_event(s->event_id, s->trigger_mask, pevent)) {
    3637            0 :          s->ReplaceEvent(pevent);
    3638              :          //s->Print(); printf(", matched\n");
    3639              :       }
    3640              :    }
    3641            0 : }
    3642              : 
    3643            0 : static void DeleteEventStash()
    3644              : {
    3645            0 :    std::lock_guard<std::mutex> guard(gEventStashMutex);
    3646            0 :    for (size_t i=0; i<gEventStash.size(); i++) {
    3647            0 :       if (gEventStash[i]) {
    3648            0 :          delete gEventStash[i];
    3649            0 :          gEventStash[i] = NULL;
    3650              :       }
    3651              :    }
    3652            0 : }
    3653              : 
    3654            0 : static const EVENT_HEADER* FindEvent(const std::string buffer_name, int event_id, int trigger_mask, int last_event_id, int last_trigger_mask, DWORD last_serial_number, DWORD last_time_stamp)
    3655              : {
    3656            0 :    std::lock_guard<std::mutex> guard(gEventStashMutex);
    3657            0 :    for (EventStashEntry* s : gEventStash) {
    3658            0 :       if (s->buffer_name != buffer_name)
    3659            0 :          continue;
    3660            0 :       if (bm_match_event(event_id, trigger_mask, s->pevent)) {
    3661            0 :          if (s->pevent->event_id == last_event_id
    3662            0 :              && s->pevent->trigger_mask == last_trigger_mask
    3663            0 :              && s->pevent->serial_number == last_serial_number
    3664            0 :              && s->pevent->time_stamp == last_time_stamp) {
    3665              :             //s->Print(); printf(", already sent for %d,0x%x\n", event_id, trigger_mask);
    3666            0 :             return NULL;
    3667              :          } else {
    3668              :             //s->Print(); printf(", serving for %d,0x%x\n", event_id, trigger_mask);
    3669            0 :             return CopyEvent(s->pevent);
    3670              :          }
    3671              :       }
    3672              :    }
    3673            0 :    return NULL;
    3674            0 : }
    3675              : 
    3676            0 : static MJsonNode* js_bm_receive_event(const MJsonNode* params)
    3677              : {
    3678            0 :    if (!params) {
    3679            0 :       MJSO* doc = MJSO::I();
    3680            0 :       doc->D("read event buffers");
    3681            0 :       doc->P("buffer_name", MJSON_STRING, "name of event buffer");
    3682            0 :       doc->P("event_id?", MJSON_INT, "requested event id, -1 means any event id");
    3683            0 :       doc->P("trigger_mask?", MJSON_INT, "requested trigger mask, -1 means any trigger mask");
    3684            0 :       doc->P("get_recent?", MJSON_BOOL, "get last available event that matches this event request");
    3685            0 :       doc->P("last_event_header[]?", MJSON_INT, "do not resend an event we already received: event header of last received event [event_id,trigger_mask,serial_number,time_stamp]");
    3686            0 :       doc->P("timeout_millisec?", MJSON_NUMBER, "how long to wait for an event");
    3687            0 :       doc->R("binary data", MJSON_ARRAYBUFFER, "binary event data");
    3688            0 :       doc->R("status", MJSON_INT, "return status of bm_open_buffer(), bm_request_event(), bm_set_cache_size(), bm_receive_alloc()");
    3689            0 :       return doc;
    3690              :    }
    3691              : 
    3692            0 :    MJsonNode* error = NULL;
    3693              : 
    3694            0 :    std::string buffer_name  = mjsonrpc_get_param(params, "buffer_name", &error)->GetString(); if (error) return error;
    3695            0 :    int event_id = mjsonrpc_get_param(params, "event_id", NULL)->GetInt();
    3696            0 :    int trigger_mask = mjsonrpc_get_param(params, "trigger_mask", NULL)->GetInt();
    3697            0 :    bool get_recent  = mjsonrpc_get_param(params, "get_recent", NULL)->GetBool();
    3698            0 :    const MJsonNodeVector* last_event_header = mjsonrpc_get_param(params, "last_event_header", NULL)->GetArray();
    3699            0 :    int timeout_millisec = mjsonrpc_get_param(params, "timeout_millisec", NULL)->GetInt();
    3700              : 
    3701            0 :    int last_event_id      = 0;
    3702            0 :    int last_trigger_mask  = 0;
    3703            0 :    int last_serial_number = 0;
    3704            0 :    int last_time_stamp    = 0;
    3705              : 
    3706            0 :    if (last_event_header && last_event_header->size() > 0) {
    3707            0 :       if (last_event_header->size() != 4) {
    3708            0 :          return mjsonrpc_make_error(-32602, "Invalid params", "last_event_header should be an array with 4 elements");
    3709              :       }
    3710              :       
    3711            0 :       last_event_id      = (*last_event_header)[0]->GetInt();
    3712            0 :       last_trigger_mask  = (*last_event_header)[1]->GetInt();
    3713            0 :       last_serial_number = (*last_event_header)[2]->GetInt();
    3714            0 :       last_time_stamp    = (*last_event_header)[3]->GetInt();
    3715              :    }
    3716              : 
    3717              :    //printf("last event header: %d %d %d %d\n", last_event_id, last_trigger_mask, last_serial_number, last_time_stamp);
    3718              : 
    3719            0 :    if (event_id == 0)
    3720            0 :       event_id = EVENTID_ALL;
    3721              : 
    3722            0 :    if (trigger_mask == 0)
    3723            0 :       trigger_mask = TRIGGER_ALL;
    3724              : 
    3725              :    //printf("js_bm_receive_event: buffer \"%s\", event_id %d, trigger_mask 0x%04x\n", buffer_name.c_str(), event_id, trigger_mask);
    3726              :  
    3727              :    int status;
    3728              : 
    3729            0 :    HNDLE buffer_handle = 0;
    3730              : 
    3731            0 :    status = bm_get_buffer_handle(buffer_name.c_str(), &buffer_handle);
    3732              : 
    3733            0 :    if (status != BM_SUCCESS) {
    3734              :       // if buffer not already open, we need to open it,
    3735              :       // but we must hold a lock in case multiple RPC handler threads
    3736              :       // try to open it at the same time. K.O.
    3737              :       static std::mutex gMutex;
    3738            0 :       std::lock_guard<std::mutex> lock_guard(gMutex); // lock the mutex
    3739              : 
    3740              :       // we have the lock. now we check if some other thread
    3741              :       // opened the buffer while we were waiting for the lock. K.O.
    3742            0 :       status = bm_get_buffer_handle(buffer_name.c_str(), &buffer_handle);
    3743              : 
    3744            0 :       if (status != BM_SUCCESS) {
    3745            0 :          status = bm_open_buffer(buffer_name.c_str(), 0, &buffer_handle);
    3746            0 :          if (status != BM_SUCCESS && status != BM_CREATED) {
    3747            0 :             MJsonNode* result = MJsonNode::MakeObject();
    3748            0 :             result->AddToObject("status", MJsonNode::MakeInt(status));
    3749            0 :             return mjsonrpc_make_result(result);
    3750              :          }
    3751            0 :          status = bm_set_cache_size(buffer_handle, 0, 0);
    3752            0 :          if (status != BM_SUCCESS) {
    3753            0 :             MJsonNode* result = MJsonNode::MakeObject();
    3754            0 :             result->AddToObject("status", MJsonNode::MakeInt(status));
    3755            0 :             return mjsonrpc_make_result(result);
    3756              :          }
    3757            0 :          int request_id = 0;
    3758            0 :          status = bm_request_event(buffer_handle, EVENTID_ALL, TRIGGER_ALL, GET_NONBLOCKING, &request_id, NULL);
    3759            0 :          if (status != BM_SUCCESS) {
    3760            0 :             MJsonNode* result = MJsonNode::MakeObject();
    3761            0 :             result->AddToObject("status", MJsonNode::MakeInt(status));
    3762            0 :             return mjsonrpc_make_result(result);
    3763              :          }
    3764              :       }
    3765            0 :    }
    3766              : 
    3767            0 :    if (timeout_millisec <= 0)
    3768            0 :       timeout_millisec = 100.0;
    3769              : 
    3770            0 :    double start_time = ss_time_sec();
    3771            0 :    double end_time = start_time + timeout_millisec/1000.0;
    3772              : 
    3773              :    // in "GET_RECENT" mode, we read all avialable events from the event buffer
    3774              :    // and save them in the event stash (MatchEvent()), after we empty the event
    3775              :    // buffer (BM_ASYNC_RETURN), we send the last saved event. K.O.
    3776              : 
    3777              :    while (1) {
    3778            0 :       EVENT_HEADER* pevent = NULL;
    3779              :       
    3780            0 :       status = bm_receive_event_alloc(buffer_handle, &pevent, BM_NO_WAIT);
    3781              : 
    3782            0 :       if (status == BM_SUCCESS) {
    3783              :          //printf("got event_id %d, trigger_mask 0x%04x\n", pevent->event_id, pevent->trigger_mask);
    3784              : 
    3785            0 :          if (get_recent) {
    3786            0 :             if (bm_match_event(event_id, trigger_mask, pevent)) {
    3787            0 :                StashEvent(buffer_name, event_id, trigger_mask, pevent);
    3788              :             } else {
    3789            0 :                MatchEvent(buffer_name, pevent);
    3790              :             }
    3791            0 :             free(pevent);
    3792            0 :             pevent = NULL;
    3793              :          } else {
    3794            0 :             if (bm_match_event(event_id, trigger_mask, pevent)) {
    3795            0 :                StashEvent(buffer_name, event_id, trigger_mask, pevent);
    3796              : 
    3797            0 :                size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
    3798              :                //size_t total_size = ALIGN8(event_size);
    3799            0 :                return MJsonNode::MakeArrayBuffer((char*)pevent, event_size);
    3800              :             }
    3801              : 
    3802            0 :             MatchEvent(buffer_name, pevent);
    3803              :             
    3804            0 :             free(pevent);
    3805            0 :             pevent = NULL;
    3806              :          }
    3807            0 :       } else if (status == BM_ASYNC_RETURN) {
    3808            0 :          if (get_recent) {
    3809              :             //printf("bm_async_return!\n");
    3810            0 :             break;
    3811              :          }
    3812            0 :          ss_sleep(10);
    3813              :       } else {
    3814            0 :          MJsonNode* result = MJsonNode::MakeObject();
    3815            0 :          result->AddToObject("status", MJsonNode::MakeInt(status));
    3816            0 :          return mjsonrpc_make_result(result);
    3817              :       }
    3818              : 
    3819            0 :       if (ss_time_sec() > end_time) {
    3820              :          //printf("timeout!\n");
    3821            0 :          break;
    3822              :       }
    3823            0 :    }
    3824              : 
    3825            0 :    const EVENT_HEADER* pevent = FindEvent(buffer_name, event_id, trigger_mask, last_event_id, last_trigger_mask, last_serial_number, last_time_stamp);
    3826            0 :    if (pevent) {
    3827            0 :       size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
    3828              :       //size_t total_size = ALIGN8(event_size);
    3829            0 :       return MJsonNode::MakeArrayBuffer((char*)pevent, event_size);
    3830              :    }
    3831              :       
    3832            0 :    MJsonNode* result = MJsonNode::MakeObject();
    3833            0 :    result->AddToObject("status", MJsonNode::MakeInt(BM_ASYNC_RETURN));
    3834            0 :    return mjsonrpc_make_result(result);
    3835            0 : }
    3836              : 
    3837              : /////////////////////////////////////////////////////////////////////////////////
    3838              : //
    3839              : // ss_system code goes here
    3840              : //
    3841              : /////////////////////////////////////////////////////////////////////////////////
    3842              : 
    3843            0 : static MJsonNode* js_ss_millitime(const MJsonNode* params)
    3844              : {
    3845            0 :    if (!params) {
    3846            0 :       MJSO *doc = MJSO::I();
    3847            0 :       doc->D("get current MIDAS time using ss_millitime()");
    3848            0 :       doc->P(NULL, 0, "there are no input parameters");
    3849            0 :       doc->R(NULL, MJSON_INT, "current value of ss_millitime()");
    3850            0 :       return doc;
    3851              :    }
    3852              : 
    3853            0 :    return mjsonrpc_make_result(MJsonNode::MakeNumber(ss_millitime()));
    3854              : }
    3855              : 
    3856              : /////////////////////////////////////////////////////////////////////////////////
    3857              : //
    3858              : // Alarm code goes here
    3859              : //
    3860              : /////////////////////////////////////////////////////////////////////////////////
    3861              : 
    3862            0 : static MJsonNode* get_alarms(const MJsonNode* params)
    3863              : {
    3864            0 :    if (!params) {
    3865            0 :       MJSO* doc = MJSO::I();
    3866            0 :       doc->D("get alarm data");
    3867            0 :       doc->P("get_all?", MJSON_BOOL, "get all alarms, even in alarm system not active and alarms not triggered");
    3868            0 :       doc->R("status", MJSON_INT, "return status of midas library calls");
    3869            0 :       doc->R("alarm_system_active", MJSON_BOOL, "value of ODB \"/Alarms/alarm system active\"");
    3870            0 :       doc->R("alarms", MJSON_OBJECT, "alarm data, keyed by alarm name");
    3871            0 :       doc->R("alarms[].triggered", MJSON_BOOL, "alarm is triggered");
    3872            0 :       doc->R("alarms[].active", MJSON_BOOL, "alarm is enabled");
    3873            0 :       doc->R("alarms[].class", MJSON_STRING, "alarm class");
    3874            0 :       doc->R("alarms[].type", MJSON_INT, "alarm type AT_xxx");
    3875            0 :       doc->R("alarms[].bgcolor", MJSON_STRING, "display background color");
    3876            0 :       doc->R("alarms[].fgcolor", MJSON_STRING, "display foreground color");
    3877            0 :       doc->R("alarms[].message", MJSON_STRING, "alarm ODB message field");
    3878            0 :       doc->R("alarms[].condition", MJSON_STRING, "alarm ODB condition field");
    3879            0 :       doc->R("alarms[].evaluated_value?", MJSON_STRING, "evaluated alarm condition (AT_EVALUATED alarms only)");
    3880            0 :       doc->R("alarms[].periodic_next_time?", MJSON_STRING, "next time the periodic alarm will fire (AT_PERIODIC alarms only)");
    3881            0 :       doc->R("alarms[].time_triggered_first", MJSON_STRING, "time when alarm was triggered");
    3882            0 :       doc->R("alarms[].show_to_user", MJSON_STRING, "final alarm text shown to user by mhttpd");
    3883            0 :       return doc;
    3884              :    }
    3885              : 
    3886              :    //MJsonNode* error = NULL;
    3887              : 
    3888            0 :    bool get_all = mjsonrpc_get_param(params, "get_all", NULL)->GetBool();
    3889              : 
    3890              :    int status;
    3891              :    HNDLE hDB;
    3892              : 
    3893            0 :    status = cm_get_experiment_database(&hDB, NULL);
    3894              : 
    3895            0 :    if (status != DB_SUCCESS) {
    3896            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    3897              :    }
    3898              : 
    3899              :    int flag;
    3900              :    int size;
    3901            0 :    int alarm_system_active = 0;
    3902              : 
    3903              :    /* check global alarm flag */
    3904            0 :    flag = TRUE;
    3905            0 :    size = sizeof(flag);
    3906            0 :    status = db_get_value(hDB, 0, "/Alarms/Alarm System active", &flag, &size, TID_BOOL, TRUE);
    3907              : 
    3908            0 :    if (status != DB_SUCCESS) {
    3909            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    3910              :    }
    3911              : 
    3912            0 :    alarm_system_active = flag;
    3913              : 
    3914            0 :    if (!alarm_system_active)
    3915            0 :       if (!get_all) {
    3916            0 :          return mjsonrpc_make_result("status", MJsonNode::MakeInt(SUCCESS),
    3917              :                                      "alarm_system_active", MJsonNode::MakeBool(alarm_system_active!=0),
    3918            0 :                                      "alarms", MJsonNode::MakeObject());
    3919              :       }
    3920              : 
    3921              :    /* go through all alarms */
    3922              :    HNDLE hkey;
    3923            0 :    status = db_find_key(hDB, 0, "/Alarms/Alarms", &hkey);
    3924              : 
    3925            0 :    if (status != DB_SUCCESS) {
    3926            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    3927              :    }
    3928              : 
    3929            0 :    MJsonNode* alarms = MJsonNode::MakeObject();
    3930              : 
    3931            0 :    for (int i = 0;; i++) {
    3932              :       HNDLE hsubkey;
    3933              :       KEY key;
    3934              : 
    3935            0 :       db_enum_link(hDB, hkey, i, &hsubkey);
    3936              : 
    3937            0 :       if (!hsubkey)
    3938            0 :          break;
    3939              : 
    3940            0 :       status = db_get_key(hDB, hsubkey, &key);
    3941              :          
    3942            0 :       const char* name = key.name;
    3943              : 
    3944            0 :       flag = 0;
    3945            0 :       size = sizeof(flag);
    3946            0 :       status = db_get_value(hDB, hsubkey, "Triggered", &flag, &size, TID_INT, TRUE);
    3947              : 
    3948              :       // skip un-triggered alarms
    3949            0 :       if (!flag)
    3950            0 :          if (!get_all)
    3951            0 :             continue;
    3952              : 
    3953            0 :       MJsonNode* a = MJsonNode::MakeObject();
    3954              : 
    3955            0 :       a->AddToObject("triggered", MJsonNode::MakeBool(flag!=0));
    3956              : 
    3957            0 :       flag = 1;
    3958            0 :       size = sizeof(BOOL);
    3959            0 :       status = db_get_value(hDB, hsubkey, "Active", &flag, &size, TID_BOOL, TRUE);
    3960              : 
    3961            0 :       a->AddToObject("active", MJsonNode::MakeBool(flag!=0));
    3962              : 
    3963              :       char alarm_class[NAME_LENGTH];
    3964            0 :       strcpy(alarm_class, "Alarm");
    3965            0 :       size = sizeof(alarm_class);
    3966            0 :       status = db_get_value(hDB, hsubkey, "Alarm Class", alarm_class, &size, TID_STRING, TRUE);
    3967              : 
    3968            0 :       ss_repair_utf8(alarm_class);
    3969            0 :       a->AddToObject("class", MJsonNode::MakeString(alarm_class));
    3970              : 
    3971            0 :       int atype = 0;
    3972            0 :       size = sizeof(atype);
    3973            0 :       status = db_get_value(hDB, hsubkey, "Type", &atype, &size, TID_INT, TRUE);
    3974              :          
    3975            0 :       a->AddToObject("type", MJsonNode::MakeInt(atype));
    3976              : 
    3977              :       char str[256];
    3978              : 
    3979              :       char bgcol[256];
    3980            0 :       strcpy(bgcol, "red");
    3981              : 
    3982            0 :       if (strlen(alarm_class) > 0) {
    3983            0 :          sprintf(str, "/Alarms/Classes/%s/Display BGColor", alarm_class);
    3984            0 :          size = sizeof(bgcol);
    3985            0 :          status = db_get_value(hDB, 0, str, bgcol, &size, TID_STRING, TRUE);
    3986              :       }
    3987              :          
    3988            0 :       ss_repair_utf8(bgcol);
    3989            0 :       a->AddToObject("bgcolor", MJsonNode::MakeString(bgcol));
    3990              : 
    3991              :       char fgcol[256];
    3992            0 :       strcpy(fgcol, "black");
    3993              : 
    3994            0 :       if (strlen(alarm_class) > 0) {
    3995            0 :          sprintf(str, "/Alarms/Classes/%s/Display FGColor", alarm_class);
    3996            0 :          size = sizeof(fgcol);
    3997            0 :          status = db_get_value(hDB, 0, str, fgcol, &size, TID_STRING, TRUE);
    3998              :       }
    3999              :          
    4000            0 :       ss_repair_utf8(fgcol);
    4001            0 :       a->AddToObject("fgcolor", MJsonNode::MakeString(fgcol));
    4002              : 
    4003            0 :       flag = 1;
    4004            0 :       if (strlen(alarm_class) > 0) {
    4005            0 :          size = sizeof(BOOL);
    4006            0 :          sprintf(str, "/Alarms/Classes/%s/Alarm sound", alarm_class);
    4007            0 :          status = db_get_value(hDB, 0, str, &flag, &size, TID_BOOL, TRUE);
    4008              :       }
    4009            0 :       a->AddToObject("alarm_sound", MJsonNode::MakeBool(flag!=0));
    4010              : 
    4011              :       char msg[256];
    4012            0 :       msg[0] = 0;
    4013            0 :       size = sizeof(msg);
    4014            0 :       status = db_get_value(hDB, hsubkey, "Alarm Message", msg, &size, TID_STRING, TRUE);
    4015              :          
    4016            0 :       ss_repair_utf8(msg);
    4017            0 :       a->AddToObject("message", MJsonNode::MakeString(msg));
    4018              : 
    4019              :       char cond[256];
    4020            0 :       cond[0] = 0;
    4021            0 :       size = sizeof(cond);
    4022            0 :       status = db_get_value(hDB, hsubkey, "Condition", cond, &size, TID_STRING, TRUE);
    4023              : 
    4024            0 :       ss_repair_utf8(cond);
    4025            0 :       a->AddToObject("condition", MJsonNode::MakeString(cond));
    4026              : 
    4027            0 :       std::string show_to_user;
    4028              :       
    4029            0 :       if (atype == AT_EVALUATED) {
    4030              :          /* retrieve value */
    4031            0 :          std::string value;
    4032            0 :          al_evaluate_condition(name, cond, &value);
    4033            0 :          show_to_user = msprintf(msg, value.c_str());
    4034            0 :          ss_repair_utf8(value);
    4035            0 :          a->AddToObject("evaluated_value", MJsonNode::MakeString(value.c_str()));
    4036            0 :       } else
    4037            0 :          show_to_user = msg;
    4038              :       
    4039            0 :       ss_repair_utf8(show_to_user);
    4040            0 :       a->AddToObject("show_to_user", MJsonNode::MakeString(show_to_user.c_str()));
    4041              : 
    4042            0 :       str[0] = 0;
    4043            0 :       size = sizeof(str);
    4044            0 :       status = db_get_value(hDB, hsubkey, "Time triggered first", str, &size, TID_STRING, TRUE);
    4045              : 
    4046            0 :       ss_repair_utf8(str);
    4047            0 :       a->AddToObject("time_triggered_first", MJsonNode::MakeString(str));
    4048              : 
    4049            0 :       if (atype == AT_PERIODIC) {
    4050            0 :          DWORD last = 0;
    4051            0 :          size = sizeof(last);
    4052            0 :          db_get_value(hDB, hsubkey, "Checked last", &last, &size, TID_DWORD, TRUE);
    4053              : 
    4054            0 :          if (last == 0) {
    4055            0 :             last = ss_time();
    4056            0 :             db_set_value(hDB, hsubkey, "Checked last", &last, size, 1, TID_DWORD);
    4057              :          }
    4058              : 
    4059            0 :          int interval = 0;
    4060            0 :          size = sizeof(interval);
    4061            0 :          db_get_value(hDB, hsubkey, "Check interval", &interval, &size, TID_INT, TRUE);
    4062              : 
    4063            0 :          time_t tnext = last + interval;
    4064              : 
    4065              :          char ctimebuf[32];
    4066            0 :          ctime_r(&tnext, ctimebuf);
    4067              : 
    4068              :          //ss_repair_utf8(ctimebuf); redundant!
    4069            0 :          a->AddToObject("periodic_next_time", MJsonNode::MakeString(ctimebuf));
    4070              :       }
    4071              : 
    4072            0 :       alarms->AddToObject(name, a);
    4073            0 :    }
    4074              : 
    4075            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(SUCCESS),
    4076              :                                "alarm_system_active", MJsonNode::MakeBool(alarm_system_active!=0),
    4077            0 :                                "alarms", alarms);
    4078              : }
    4079              : 
    4080              : /////////////////////////////////////////////////////////////////////////////////
    4081              : //
    4082              : // Sequencer and file_picker code goes here
    4083              : //
    4084              : /////////////////////////////////////////////////////////////////////////////////
    4085              : 
    4086            0 : static MJsonNode* js_make_subdir(const MJsonNode* params)
    4087              : {
    4088            0 :    if (!params) {
    4089            0 :       MJSO* doc = MJSO::I();
    4090            0 :       doc->D("js_make_subdir");
    4091            0 :       doc->P("subdir", MJSON_STRING, "Create folder experiment_directory/userfiles/subdir");
    4092            0 :       doc->R("status", MJSON_INT, "return status of midas library calls");
    4093            0 :       doc->R("path", MJSON_STRING, "Search path");
    4094            0 :       return doc;
    4095              :    }
    4096              : 
    4097            0 :    MJsonNode* error = NULL;
    4098              : 
    4099            0 :    std::string subdir = mjsonrpc_get_param(params, "subdir", &error)->GetString(); if (error) return error;
    4100              : 
    4101              :    /*---- Not allowed to contain ../ - safety feature ----*/
    4102            0 :    if (subdir.find("..") != std::string::npos) {
    4103            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The subdir is not permitted"));
    4104              :    }
    4105              : 
    4106              :    int status;
    4107              :    HNDLE hDB;
    4108              : 
    4109            0 :    status = cm_get_experiment_database(&hDB, NULL);
    4110              : 
    4111            0 :    if (status != DB_SUCCESS) {
    4112            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    4113              :    }
    4114              : 
    4115            0 :    std::string path = cm_expand_env(cm_get_path().c_str());
    4116            0 :    if (path[path.length()-1] != DIR_SEPARATOR) {
    4117            0 :       path += DIR_SEPARATOR_STR;
    4118              :    }
    4119            0 :    path += "userfiles";
    4120              :    
    4121              :    // Check if the userfiles folder exists
    4122            0 :    if (access(path.c_str(), F_OK) != 0) {
    4123              :       // Create the path if it doesn't exist
    4124            0 :       if (mkdir(path.c_str(), 0777) != 0) {
    4125            0 :          return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
    4126              :       }
    4127              :    }
    4128              : 
    4129              :    // Add subdir to userfiles
    4130            0 :    if (subdir.length() > 0) {
    4131            0 :       if (subdir[0] != DIR_SEPARATOR) {
    4132            0 :          path += DIR_SEPARATOR_STR;
    4133              :       }
    4134            0 :       path += subdir;
    4135              :    }
    4136              :    // Check if the subdir exisits in userfiles, otherwise create it
    4137            0 :    if (access(path.c_str(), F_OK) != 0) {
    4138              :       // Create the path if it doesn't exist
    4139            0 :       if (mkdir(path.c_str(), 0777) != 0) {
    4140            0 :          return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create subdirectory"));
    4141              :       }
    4142              :    }
    4143              :    
    4144              :    
    4145            0 :    MJsonNode* r = MJsonNode::MakeObject();
    4146            0 :    r->AddToObject("status", MJsonNode::MakeInt(SUCCESS));
    4147            0 :    ss_repair_utf8(path);
    4148            0 :    r->AddToObject("path", MJsonNode::MakeString(path.c_str()));
    4149            0 :    return mjsonrpc_make_result(r);
    4150            0 : }
    4151              : 
    4152            0 : static MJsonNode* js_ext_list_files(const MJsonNode* params)
    4153              : {
    4154            0 :    if (!params) {
    4155            0 :       MJSO* doc = MJSO::I();
    4156            0 :       doc->D("js_ext_list_files");
    4157            0 :       doc->P("subdir", MJSON_STRING, "List files in experiment_directory/userfiles/subdir");
    4158            0 :       doc->P("fileext", MJSON_STRING, "Filename extension");
    4159            0 :       doc->R("status", MJSON_INT, "return status of midas library calls");
    4160            0 :       doc->R("path", MJSON_STRING, "Search path");
    4161            0 :       doc->R("subdirs[]", MJSON_STRING, "list of subdirectories");
    4162            0 :       doc->R("files[].filename", MJSON_STRING, "script filename");
    4163            0 :       doc->R("files[].description", MJSON_STRING, "script description");
    4164            0 :       return doc;
    4165              :    }
    4166              : 
    4167            0 :    MJsonNode* error = NULL;
    4168            0 :    std::string subdir = mjsonrpc_get_param(params, "subdir", &error)->GetString(); if (error) return error;
    4169            0 :    std::string fileext  = mjsonrpc_get_param(params, "fileext", &error)->GetString(); if (error) return error;
    4170              : 
    4171              :    /*---- Not allowed to contain ../ - safety feature ----*/
    4172            0 :    if (subdir.find("..") != std::string::npos) {
    4173            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The subdir is not permitted"));
    4174              :    }
    4175              : 
    4176              :    /*---- ext must start with *. and some not allowed ext - safety feature */
    4177            0 :    if (fileext.find("..") != std::string::npos) {
    4178              :       /*
    4179              :        fileext.find("/") != std::string::npos ||
    4180              :        fileext.find("*.") != 0 ||
    4181              :        fileext.find("*.html") != std::string::npos || fileext.find("*.HTML") != std::string::npos ||
    4182              :        fileext.find("*.htm") != std::string::npos || fileext.find("*.HTM") != std::string::npos ||
    4183              :        fileext.find("*.js") != std::string::npos || fileext.find("*.JS") != std::string::npos ||
    4184              :        fileext.find("*.pl") != std::string::npos || fileext.find("*.PL") != std::string::npos ||
    4185              :        fileext.find("*.cgi") != std::string::npos || fileext.find("*.CGI") != std::string::npos ||
    4186              :        fileext.find("*.*") != std::string::npos
    4187              :        ) {
    4188              :       */
    4189            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The filename extension is not permitted"));
    4190              :    }
    4191              :    
    4192              :    int status;
    4193              :    HNDLE hDB;
    4194              : 
    4195            0 :    status = cm_get_experiment_database(&hDB, NULL);
    4196              : 
    4197            0 :    if (status != DB_SUCCESS) {
    4198            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
    4199              :    }
    4200              : 
    4201            0 :    std::string path = cm_expand_env(cm_get_path().c_str());
    4202            0 :    if (path[path.length()-1] != DIR_SEPARATOR) {
    4203            0 :       path += DIR_SEPARATOR_STR;
    4204              :    }
    4205            0 :    path += "userfiles";
    4206              :    
    4207              :    // Check if the userfiles folder exists
    4208            0 :    if (access(path.c_str(), F_OK) != 0) {
    4209              :       // Create the path if it doesn't exist
    4210            0 :       if (mkdir(path.c_str(), 0777) != 0) {
    4211            0 :          return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
    4212              :       }
    4213              :    }
    4214              :    
    4215            0 :    if (subdir.length() > 0) {
    4216            0 :       if (subdir[0] != DIR_SEPARATOR) {
    4217            0 :          path += DIR_SEPARATOR_STR;
    4218              :       }
    4219            0 :       path += subdir;
    4220              :    }
    4221              : 
    4222            0 :    char* flist = NULL;
    4223            0 :    MJsonNode* s = MJsonNode::MakeArray();
    4224              :    
    4225              :    /*---- go over subdirectories ----*/
    4226            0 :    int n = ss_dirlink_find(path.c_str(), "*", &flist);
    4227              : 
    4228            0 :    for (int i=0 ; i<n ; i++) {
    4229            0 :       if (flist[i*MAX_STRING_LENGTH] != '.') {
    4230              :          //printf("subdir %d: [%s]\n", i, flist+i*MAX_STRING_LENGTH);
    4231            0 :          ss_repair_utf8(flist+i*MAX_STRING_LENGTH);
    4232            0 :          s->AddToArray(MJsonNode::MakeString(flist+i*MAX_STRING_LENGTH));
    4233              :       }
    4234              :    }
    4235              :    
    4236            0 :    MJsonNode* f = MJsonNode::MakeArray();
    4237            0 :    time_t modtime = time(NULL);
    4238              :    double fsize;
    4239              :    /*---- go over files with extension fileext in path ----*/
    4240            0 :    n = ss_file_find(path.c_str(), fileext.c_str(), &flist);
    4241              : 
    4242            0 :    if (n == -1) {
    4243              :       // path does not exist, return an error
    4244            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Subdirectory does not exist, create it first."));
    4245              :    }
    4246              :    
    4247            0 :    for (int i=0 ; i<n ; i++) {
    4248              :       //printf("file %d: [%s]\n", i, flist+i*MAX_STRING_LENGTH);
    4249            0 :       MJsonNode* o = MJsonNode::MakeObject();
    4250            0 :       ss_repair_utf8(flist+i*MAX_STRING_LENGTH);
    4251            0 :       o->AddToObject("filename", MJsonNode::MakeString(flist+i*MAX_STRING_LENGTH));
    4252              :       /* description is msl specific, do we need it? */
    4253            0 :       std::string full_name = path;
    4254            0 :       if (full_name.back() != DIR_SEPARATOR)
    4255            0 :          full_name += DIR_SEPARATOR;
    4256            0 :       full_name.append(flist+i*MAX_STRING_LENGTH);
    4257              :       // o->AddToObject("description", MJsonNode::MakeString(full_name.c_str()));
    4258            0 :       o->AddToObject("description", MJsonNode::MakeString("description"));
    4259            0 :       modtime = ss_file_time(full_name.c_str());
    4260            0 :       o->AddToObject("modtime", MJsonNode::MakeInt(modtime));
    4261            0 :       fsize = ss_file_size(full_name.c_str());
    4262            0 :       o->AddToObject("size", MJsonNode::MakeInt(fsize));
    4263            0 :       f->AddToArray(o);
    4264            0 :    }
    4265              : 
    4266            0 :    free(flist);
    4267            0 :    flist = NULL;
    4268              : 
    4269            0 :    MJsonNode* r = MJsonNode::MakeObject();
    4270            0 :    r->AddToObject("status", MJsonNode::MakeInt(SUCCESS));
    4271            0 :    ss_repair_utf8(path);
    4272            0 :    r->AddToObject("path", MJsonNode::MakeString(path.c_str()));
    4273            0 :    r->AddToObject("subdirs", s);
    4274            0 :    r->AddToObject("files", f);
    4275              :    
    4276            0 :    return mjsonrpc_make_result(r);
    4277              : 
    4278            0 : }
    4279              : 
    4280            0 : static MJsonNode* js_ext_save_file(const MJsonNode* params)
    4281              : {
    4282            0 :    if (!params) {
    4283            0 :       MJSO* doc = MJSO::I();
    4284            0 :       doc->D("js_ext_save_file");
    4285            0 :       doc->P("filename", MJSON_STRING, "File name, save in experiment_directory/userfiles/filename");
    4286            0 :       doc->P("script", MJSON_STRING, "ASCII content");
    4287            0 :       doc->R("status", MJSON_INT, "return status of midas library calls");
    4288            0 :       doc->R("error", MJSON_STRING, "error text");
    4289            0 :       return doc;
    4290              :    }
    4291              : 
    4292              :    /* Need to make sure that content cannot be abused (e.g. not Javascript code), how?? */
    4293              :    
    4294            0 :    MJsonNode* error = NULL;
    4295            0 :    std::string filename = mjsonrpc_get_param(params, "filename", &error)->GetString(); if (error) return error;
    4296            0 :    std::string script = mjsonrpc_get_param(params, "script", &error)->GetString(); if (error) return error;
    4297              : 
    4298              :    /*---- filename should not contain the following - safety feature */
    4299            0 :    if (filename.find("..") != std::string::npos ) {
    4300              :       /*
    4301              :        filename.find("*") != std::string::npos ||
    4302              :        filename.find(".html") != std::string::npos || filename.find(".HTML") != std::string::npos ||
    4303              :        filename.find(".htm") != std::string::npos || filename.find(".HTM") != std::string::npos ||
    4304              :        filename.find(".js") != std::string::npos || filename.find(".JS") != std::string::npos ||
    4305              :        filename.find(".pl") != std::string::npos || filename.find(".PL") != std::string::npos ||
    4306              :        filename.find(".cgi") != std::string::npos || filename.find(".CGI") != std::string::npos
    4307              :        ) {
    4308              :       */
    4309            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The filename is not permitted"));
    4310              :    }
    4311              : 
    4312              :    int status;
    4313              :    HNDLE hDB;
    4314              : 
    4315            0 :    status = cm_get_experiment_database(&hDB, NULL);
    4316              : 
    4317            0 :    if (status != DB_SUCCESS) {
    4318            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString("cm_get_experiment_database() error"));
    4319              :    }
    4320              : 
    4321            0 :    std::string path = cm_expand_env(cm_get_path().c_str());
    4322            0 :    path += "userfiles";
    4323              : 
    4324              :    // Check if the userfiles folder exists
    4325            0 :    if (access(path.c_str(), F_OK) != 0) {
    4326              :       // Create the path if it doesn't exist
    4327            0 :       if (mkdir(path.c_str(), 0777) != 0) {
    4328            0 :          return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
    4329              :       }
    4330              :    }
    4331              :    
    4332            0 :    path += DIR_SEPARATOR_STR;
    4333            0 :    path += filename;
    4334              : 
    4335            0 :    FILE* fp = fopen(path.c_str(), "w");
    4336            0 :    if (!fp) {
    4337            0 :       status = SS_FILE_ERROR;
    4338              :       char errstr[256];
    4339            0 :       sprintf(errstr, "fopen() errno %d (%s)", errno, strerror(errno));
    4340            0 :       ss_repair_utf8(errstr);
    4341            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr));
    4342              :    }
    4343              : 
    4344            0 :    fwrite(script.c_str(), script.length(), 1, fp);
    4345              :    //fprintf(fp, "\n");
    4346            0 :    fclose(fp);
    4347            0 :    fp = NULL;
    4348              : 
    4349            0 :    status = CM_SUCCESS;
    4350            0 :    std::string errstr = "no error";
    4351              : 
    4352              :    //ss_repair_utf8(errstr); redundant!
    4353            0 :    return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr.c_str()));
    4354            0 : }
    4355              : 
    4356            0 : static MJsonNode* js_ext_read_file(const MJsonNode* params)
    4357              : {
    4358            0 :    if (!params) {
    4359            0 :       MJSO* doc = MJSO::I();
    4360            0 :       doc->D("js_ext_read_script");
    4361            0 :       doc->P("filename", MJSON_STRING, "File name, read from experiment_directory/userfiles/filename");
    4362            0 :       doc->R("content", MJSON_STRING, "ASCII file content");
    4363            0 :       doc->R("status", MJSON_INT, "return status of midas library calls");
    4364            0 :       doc->R("error", MJSON_STRING, "error text");
    4365            0 :       return doc;
    4366              :    }
    4367              :    
    4368            0 :    MJsonNode* error = NULL;
    4369            0 :    std::string filename = mjsonrpc_get_param(params, "filename", &error)->GetString(); if (error) return error;
    4370              :    
    4371              :    /*---- filename should not contain the following - safety feature */
    4372            0 :    if (filename.find("..") != std::string::npos ||
    4373            0 :        filename.find("*") != std::string::npos) {
    4374            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The filename is not permitted"));
    4375              :    }
    4376              :    
    4377              :    int status;
    4378              :    HNDLE hDB;
    4379              :    
    4380            0 :    status = cm_get_experiment_database(&hDB, NULL);
    4381              :    
    4382            0 :    if (status != DB_SUCCESS) {
    4383            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString("cm_get_experiment_database() error"));
    4384              :    }
    4385              : 
    4386            0 :    std::string path = cm_expand_env(cm_get_path().c_str());
    4387            0 :    if (path[path.length()-1] != DIR_SEPARATOR) {
    4388            0 :       path += DIR_SEPARATOR_STR;
    4389              :    }
    4390            0 :    path += "userfiles";
    4391              : 
    4392              :    // Check if the userfiles folder exists
    4393            0 :    if (access(path.c_str(), F_OK) != 0) {
    4394              :       // Create the path if it doesn't exist
    4395            0 :       if (mkdir(path.c_str(), 0777) != 0) {
    4396            0 :          return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
    4397              :       }
    4398              :    }
    4399              : 
    4400            0 :    path += DIR_SEPARATOR_STR;
    4401            0 :    path += filename;
    4402              :    
    4403            0 :    FILE* fp = fopen(path.c_str(), "r");
    4404            0 :    if (!fp) {
    4405            0 :       status = SS_FILE_ERROR;
    4406              :       char errstr[256];
    4407            0 :       sprintf(errstr, "fopen() errno %d (%s)", errno, strerror(errno));
    4408            0 :       ss_repair_utf8(errstr);
    4409            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr));
    4410              :    }
    4411              :    
    4412            0 :    fseek(fp, 0, SEEK_END);
    4413            0 :    size_t file_size = ftell(fp);
    4414            0 :    rewind(fp);
    4415              :    
    4416            0 :    char* buffer = new char[file_size+1];
    4417            0 :    fread(buffer, file_size, 1, fp);
    4418              :    // Maybe not needed here
    4419            0 :    buffer[file_size] = '\0';
    4420            0 :    fclose(fp);
    4421              :    
    4422            0 :    std::string content = buffer;
    4423            0 :    delete[] buffer;
    4424            0 :    buffer = NULL;
    4425              :    
    4426            0 :    status = CM_SUCCESS;
    4427            0 :    std::string errstr = "no error";
    4428              :    
    4429            0 :    return mjsonrpc_make_result("content", MJsonNode::MakeString(content.c_str()), "status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr.c_str()));
    4430            0 : }
    4431              : 
    4432              : /////////////////////////////////////////////////////////////////////////////////
    4433              : //
    4434              : // Binary files reading for file_picker code goes here
    4435              : //
    4436              : /////////////////////////////////////////////////////////////////////////////////
    4437              : 
    4438            0 : static MJsonNode* js_read_binary_file(const MJsonNode* params) {
    4439            0 :    if (!params) {
    4440            0 :       MJSO* doc = MJSO::I();
    4441            0 :       doc->D("js_read_binary_file");
    4442            0 :       doc->P("filename", MJSON_STRING, "File name, read from experiment_directory/userfiles/filename");
    4443            0 :       doc->R("binary data", MJSON_ARRAYBUFFER, "Binary file content");
    4444            0 :       doc->R("status", MJSON_INT, "Return status of midas library calls");
    4445            0 :       doc->R("error", MJSON_STRING, "Error text");
    4446            0 :       return doc;
    4447              :    }
    4448              :    
    4449            0 :    MJsonNode* error = NULL;
    4450            0 :    std::string filename = mjsonrpc_get_param(params, "filename", &error)->GetString();
    4451            0 :    if (error) return error;
    4452              :    
    4453              :    /*---- filename should not contain the following - safety feature */
    4454            0 :    if (filename.find("..") != std::string::npos ||
    4455            0 :        filename.find("*") != std::string::npos) {
    4456            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The filename is not permitted"));
    4457              :    }
    4458              :    
    4459              :    int status;
    4460              :    HNDLE hDB;
    4461              :    
    4462            0 :    status = cm_get_experiment_database(&hDB, NULL);
    4463              :    
    4464            0 :    if (status != DB_SUCCESS) {
    4465            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString("cm_get_experiment_database() error"));
    4466              :    }
    4467              :    
    4468            0 :    std::string path = cm_expand_env(cm_get_path().c_str());
    4469            0 :    if (path[path.length()-1] != DIR_SEPARATOR) {
    4470            0 :       path += DIR_SEPARATOR_STR;
    4471              :    }
    4472            0 :    path += "userfiles";
    4473              :    
    4474              :    // Check if the userfiles folder exists
    4475            0 :    if (access(path.c_str(), F_OK) != 0) {
    4476              :       // Create the path if it doesn't exist
    4477            0 :       if (mkdir(path.c_str(), 0777) != 0) {
    4478            0 :          return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
    4479              :       }
    4480              :    }
    4481              :    
    4482            0 :    path += DIR_SEPARATOR_STR;
    4483            0 :    path += filename;
    4484              :    
    4485            0 :    FILE* fp = fopen(path.c_str(), "rb");
    4486            0 :    if (!fp) {
    4487            0 :       status = SS_FILE_ERROR;
    4488              :       char errstr[256];
    4489            0 :       sprintf(errstr, "fopen() errno %d (%s)", errno, strerror(errno));
    4490            0 :       ss_repair_utf8(errstr);
    4491            0 :       return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr));
    4492              :    }
    4493              :     
    4494            0 :    fseek(fp, 0, SEEK_END);
    4495            0 :    size_t file_size = ftell(fp);
    4496            0 :    rewind(fp);
    4497              :    
    4498            0 :    char* buffer = new char[file_size];
    4499            0 :    fread(buffer, file_size, 1, fp);
    4500              :    // maybe not needed here
    4501              :    //buffer[file_size] = '\0';
    4502            0 :    fclose(fp);
    4503              :    
    4504            0 :    status = CM_SUCCESS;
    4505            0 :    std::string errstr = "no error";
    4506              :     
    4507            0 :    MJsonNode* result = MJsonNode::MakeArrayBuffer(buffer, file_size);
    4508            0 :    return result;
    4509            0 : }
    4510              : 
    4511              : 
    4512              : /////////////////////////////////////////////////////////////////////////////////
    4513              : //
    4514              : // JSON-RPC management code goes here
    4515              : //
    4516              : /////////////////////////////////////////////////////////////////////////////////
    4517              : 
    4518            0 : static MJsonNode* get_debug(const MJsonNode* params)
    4519              : {
    4520            0 :    if (!params) {
    4521            0 :       MJSO *doc = MJSO::I();
    4522            0 :       doc->D("get current value of mjsonrpc_debug");
    4523            0 :       doc->P(NULL, 0, "there are no input parameters");
    4524            0 :       doc->R(NULL, MJSON_INT, "current value of mjsonrpc_debug");
    4525            0 :       return doc;
    4526              :    }
    4527              : 
    4528            0 :    return mjsonrpc_make_result("debug", MJsonNode::MakeInt(mjsonrpc_debug));
    4529              : }
    4530              : 
    4531            0 : static MJsonNode* set_debug(const MJsonNode* params)
    4532              : {
    4533            0 :    if (!params) {
    4534            0 :       MJSO* doc = MJSO::I();
    4535            0 :       doc->D("set new value of mjsonrpc_debug");
    4536            0 :       doc->P(NULL, MJSON_INT, "new value of mjsonrpc_debug");
    4537            0 :       doc->R(NULL, MJSON_INT, "new value of mjsonrpc_debug");
    4538            0 :       return doc;
    4539              :    }
    4540              : 
    4541            0 :    mjsonrpc_debug = params->GetInt();
    4542            0 :    return mjsonrpc_make_result("debug", MJsonNode::MakeInt(mjsonrpc_debug));
    4543              : }
    4544              : 
    4545            0 : static MJsonNode* get_sleep(const MJsonNode* params)
    4546              : {
    4547            0 :    if (!params) {
    4548            0 :       MJSO *doc = MJSO::I();
    4549            0 :       doc->D("get current value of mjsonrpc_sleep");
    4550            0 :       doc->P(NULL, 0, "there are no input parameters");
    4551            0 :       doc->R(NULL, MJSON_INT, "current value of mjsonrpc_sleep");
    4552            0 :       return doc;
    4553              :    }
    4554              : 
    4555            0 :    return mjsonrpc_make_result("sleep", MJsonNode::MakeInt(mjsonrpc_sleep));
    4556              : }
    4557              : 
    4558            0 : static MJsonNode* set_sleep(const MJsonNode* params)
    4559              : {
    4560            0 :    if (!params) {
    4561            0 :       MJSO* doc = MJSO::I();
    4562            0 :       doc->D("set new value of mjsonrpc_sleep");
    4563            0 :       doc->P(NULL, MJSON_INT, "new value of mjsonrpc_sleep");
    4564            0 :       doc->R(NULL, MJSON_INT, "new value of mjsonrpc_sleep");
    4565            0 :       return doc;
    4566              :    }
    4567              : 
    4568            0 :    mjsonrpc_sleep = params->GetInt();
    4569            0 :    return mjsonrpc_make_result("sleep", MJsonNode::MakeInt(mjsonrpc_sleep));
    4570              : }
    4571              : 
    4572            0 : static MJsonNode* get_time(const MJsonNode* params)
    4573              : {
    4574            0 :    if (!params) {
    4575            0 :       MJSO *doc = MJSO::I();
    4576            0 :       doc->D("get current value of mjsonrpc_time");
    4577            0 :       doc->P(NULL, 0, "there are no input parameters");
    4578            0 :       doc->R(NULL, MJSON_INT, "current value of mjsonrpc_time");
    4579            0 :       return doc;
    4580              :    }
    4581              : 
    4582            0 :    return mjsonrpc_make_result("time", MJsonNode::MakeInt(mjsonrpc_time));
    4583              : }
    4584              : 
    4585            0 : static MJsonNode* set_time(const MJsonNode* params)
    4586              : {
    4587            0 :    if (!params) {
    4588            0 :       MJSO* doc = MJSO::I();
    4589            0 :       doc->D("set new value of mjsonrpc_time");
    4590            0 :       doc->P(NULL, MJSON_INT, "new value of mjsonrpc_time");
    4591            0 :       doc->R(NULL, MJSON_INT, "new value of mjsonrpc_time");
    4592            0 :       return doc;
    4593              :    }
    4594              : 
    4595            0 :    mjsonrpc_time = params->GetInt();
    4596            0 :    return mjsonrpc_make_result("time", MJsonNode::MakeInt(mjsonrpc_time));
    4597              : }
    4598              : 
    4599            0 : static MJsonNode* get_schema(const MJsonNode* params)
    4600              : {
    4601            0 :    if (!params) {
    4602            0 :       MJSO* doc = MJSO::I();
    4603            0 :       doc->D("Get the MIDAS JSON-RPC schema JSON object");
    4604            0 :       doc->P(NULL, 0, "there are no input parameters");
    4605            0 :       doc->R(NULL, MJSON_OBJECT, "returns the MIDAS JSON-RPC schema JSON object");
    4606            0 :       return doc;
    4607              :    }
    4608              : 
    4609            0 :    return mjsonrpc_make_result(mjsonrpc_get_schema());
    4610              : }
    4611              : 
    4612            0 : static MJsonNode* js_get_timezone(const MJsonNode* params)
    4613              : {
    4614            0 :    if (!params) {
    4615            0 :       MJSO *doc = MJSO::I();
    4616            0 :       doc->D("get current server timezone offset in seconds");
    4617            0 :       doc->P(NULL, 0, "there are no input parameters");
    4618            0 :       doc->R(NULL, MJSON_INT, "offset in seconds");
    4619            0 :       return doc;
    4620              :    }
    4621              : 
    4622            0 :    ss_tzset(); // required for localtime_r()
    4623            0 :    time_t rawtime = time(NULL);
    4624              :    struct tm gmt_tms;
    4625            0 :    gmtime_r(&rawtime, &gmt_tms);
    4626            0 :    time_t gmt = ss_mktime(&gmt_tms);
    4627              :    struct tm tms;
    4628            0 :    localtime_r(&rawtime, &tms);
    4629            0 :    time_t offset = rawtime - gmt + (tms.tm_isdst ? 3600 : 0);
    4630              : 
    4631            0 :    return mjsonrpc_make_result(MJsonNode::MakeNumber(offset));
    4632              : }
    4633              : 
    4634              : 
    4635              : /////////////////////////////////////////////////////////////////////////////////
    4636              : //
    4637              : // No RPC handlers beyound here
    4638              : //
    4639              : /////////////////////////////////////////////////////////////////////////////////
    4640              : 
    4641              : struct MethodsTableEntry
    4642              : {
    4643              :    mjsonrpc_handler_t* fHandler = NULL;
    4644              :    bool fNeedsLocking = false;
    4645              : };
    4646              :    
    4647              : typedef std::map<std::string, MethodsTableEntry> MethodsTable;
    4648              : typedef MethodsTable::iterator MethodsTableIterator;
    4649              : 
    4650              : static MethodsTable gMethodsTable;
    4651              : static std::mutex* gMutex = NULL;
    4652              : 
    4653            0 : void mjsonrpc_add_handler(const char* method, mjsonrpc_handler_t* handler, bool needs_locking)
    4654              : {
    4655            0 :    MethodsTableEntry e;
    4656            0 :    e.fHandler = handler;
    4657            0 :    e.fNeedsLocking = needs_locking;
    4658            0 :    gMethodsTable[method] = e;
    4659            0 : }
    4660              : 
    4661            0 : void mjsonrpc_set_std_mutex(void* mutex)
    4662              : {
    4663            0 :    gMutex = (std::mutex*)mutex;
    4664            0 : }
    4665              : 
    4666            0 : void mjsonrpc_init()
    4667              : {
    4668            0 :    if (mjsonrpc_debug) {
    4669            0 :       printf("mjsonrpc_init!\n");
    4670              :    }
    4671              : 
    4672            0 :    if (!gNullNode)
    4673            0 :       gNullNode = MJsonNode::MakeNull();
    4674              : 
    4675              :    // test, debug and control methods for the rpc system
    4676            0 :    mjsonrpc_add_handler("null", xnull);
    4677            0 :    mjsonrpc_add_handler("get_debug",   get_debug);
    4678            0 :    mjsonrpc_add_handler("set_debug",   set_debug);
    4679            0 :    mjsonrpc_add_handler("get_sleep",   get_sleep);
    4680            0 :    mjsonrpc_add_handler("set_sleep",   set_sleep);
    4681            0 :    mjsonrpc_add_handler("get_time",    get_time);
    4682            0 :    mjsonrpc_add_handler("set_time",    set_time);
    4683            0 :    mjsonrpc_add_handler("get_schema",  get_schema);
    4684              :    // interface to alarm functions
    4685            0 :    mjsonrpc_add_handler("al_reset_alarm",    js_al_reset_alarm,    true);
    4686            0 :    mjsonrpc_add_handler("al_trigger_alarm",  js_al_trigger_alarm,  true);
    4687            0 :    mjsonrpc_add_handler("al_trigger_class",  js_al_trigger_class,  true);
    4688              :    // interface to midas.c functions
    4689            0 :    mjsonrpc_add_handler("cm_exist",          js_cm_exist,          true);
    4690            0 :    mjsonrpc_add_handler("cm_msg_facilities", js_cm_msg_facilities);
    4691            0 :    mjsonrpc_add_handler("cm_msg_retrieve",   js_cm_msg_retrieve);
    4692            0 :    mjsonrpc_add_handler("cm_msg1",           js_cm_msg1);
    4693            0 :    mjsonrpc_add_handler("cm_shutdown",   js_cm_shutdown,   true);
    4694            0 :    mjsonrpc_add_handler("cm_transition", js_cm_transition, true);
    4695            0 :    mjsonrpc_add_handler("bm_receive_event", js_bm_receive_event, true);
    4696              :    // interface to odb functions
    4697            0 :    mjsonrpc_add_handler("db_copy",     js_db_copy);
    4698            0 :    mjsonrpc_add_handler("db_paste",    js_db_paste);
    4699            0 :    mjsonrpc_add_handler("db_get_values", js_db_get_values);
    4700            0 :    mjsonrpc_add_handler("db_ls",       js_db_ls);
    4701            0 :    mjsonrpc_add_handler("db_create", js_db_create);
    4702            0 :    mjsonrpc_add_handler("db_delete", js_db_delete);
    4703            0 :    mjsonrpc_add_handler("db_resize", js_db_resize);
    4704            0 :    mjsonrpc_add_handler("db_resize_string", js_db_resize_string);
    4705            0 :    mjsonrpc_add_handler("db_rename", js_db_rename);
    4706            0 :    mjsonrpc_add_handler("db_scl",    js_db_scl);
    4707            0 :    mjsonrpc_add_handler("db_sor",    js_db_sor);
    4708            0 :    mjsonrpc_add_handler("db_link",   js_db_link);
    4709            0 :    mjsonrpc_add_handler("db_reorder", js_db_reorder);
    4710            0 :    mjsonrpc_add_handler("db_key",    js_db_key);
    4711              :    // interface to elog functions
    4712            0 :    mjsonrpc_add_handler("el_retrieve", js_el_retrieve, true);
    4713            0 :    mjsonrpc_add_handler("el_query",    js_el_query,    true);
    4714            0 :    mjsonrpc_add_handler("el_delete",   js_el_delete,   true);
    4715              :    // interface to midas history
    4716            0 :    mjsonrpc_add_handler("hs_get_active_events", js_hs_get_active_events, true);
    4717            0 :    mjsonrpc_add_handler("hs_get_channels", js_hs_get_channels, true);
    4718            0 :    mjsonrpc_add_handler("hs_get_events", js_hs_get_events, true);
    4719            0 :    mjsonrpc_add_handler("hs_get_tags", js_hs_get_tags, true);
    4720            0 :    mjsonrpc_add_handler("hs_get_last_written", js_hs_get_last_written, true);
    4721            0 :    mjsonrpc_add_handler("hs_reopen", js_hs_reopen, true);
    4722            0 :    mjsonrpc_add_handler("hs_read", js_hs_read, true);
    4723            0 :    mjsonrpc_add_handler("hs_read_binned", js_hs_read_binned, true);
    4724            0 :    mjsonrpc_add_handler("hs_read_arraybuffer", js_hs_read_arraybuffer, true);
    4725            0 :    mjsonrpc_add_handler("hs_read_binned_arraybuffer", js_hs_read_binned_arraybuffer, true);
    4726              :    // interface to image history
    4727            0 :    mjsonrpc_add_handler("hs_image_retrieve", js_hs_image_retrieve, true);
    4728              :    // sequencer and file_picker
    4729            0 :    mjsonrpc_add_handler("make_subdir", js_make_subdir, true);
    4730            0 :    mjsonrpc_add_handler("ext_list_files", js_ext_list_files, true);
    4731            0 :    mjsonrpc_add_handler("ext_save_file", js_ext_save_file, true);
    4732            0 :    mjsonrpc_add_handler("ext_read_file", js_ext_read_file, true);
    4733              :    // Read binary files of Uint8Arry
    4734            0 :    mjsonrpc_add_handler("read_binary_file", js_read_binary_file, true);
    4735              :    // interface to ss_system functions
    4736            0 :    mjsonrpc_add_handler("ss_millitime", js_ss_millitime);
    4737              :    // methods that perform computations or invoke actions
    4738            0 :    mjsonrpc_add_handler("get_alarms",  get_alarms);
    4739              :    //mjsonrpc_add_handler("get_messages",  get_messages);
    4740            0 :    mjsonrpc_add_handler("jrpc",  jrpc);
    4741            0 :    mjsonrpc_add_handler("brpc",  brpc);
    4742            0 :    mjsonrpc_add_handler("start_program", start_program);
    4743            0 :    mjsonrpc_add_handler("exec_script", exec_script);
    4744              :    // timezone function
    4745            0 :    mjsonrpc_add_handler("get_timezone", js_get_timezone);
    4746              : 
    4747            0 :    mjsonrpc_user_init();
    4748            0 : }
    4749              : 
    4750            0 : void mjsonrpc_exit()
    4751              : {
    4752            0 :    if (mjsonrpc_debug) {
    4753            0 :       printf("mjsonrpc_exit!\n");
    4754              :    }
    4755              : 
    4756            0 :    js_hs_exit();
    4757            0 :    DeleteEventStash();
    4758            0 : }
    4759              : 
    4760            0 : static MJsonNode* mjsonrpc_make_schema(MethodsTable* h)
    4761              : {
    4762            0 :    MJsonNode* s = MJsonNode::MakeObject();
    4763              : 
    4764            0 :    s->AddToObject("$schema", MJsonNode::MakeString("http://json-schema.org/schema#"));
    4765            0 :    s->AddToObject("id", MJsonNode::MakeString("MIDAS JSON-RPC autogenerated schema"));
    4766            0 :    s->AddToObject("title", MJsonNode::MakeString("MIDAS JSON-RPC schema"));
    4767            0 :    s->AddToObject("description", MJsonNode::MakeString("Autogenerated schema for all MIDAS JSON-RPC methods"));
    4768            0 :    s->AddToObject("type", MJsonNode::MakeString("object"));
    4769              : 
    4770            0 :    MJsonNode* m = MJsonNode::MakeObject();
    4771              : 
    4772            0 :    for (MethodsTableIterator iterator = h->begin(); iterator != h->end(); iterator++) {
    4773              :       // iterator->first = key
    4774              :       // iterator->second = value
    4775              :       //printf("build schema for method \"%s\"!\n", iterator->first.c_str());
    4776            0 :       MJsonNode* doc = iterator->second.fHandler(NULL);
    4777            0 :       if (doc == NULL)
    4778            0 :          doc = MJsonNode::MakeObject();
    4779            0 :       m->AddToObject(iterator->first.c_str(), doc);
    4780              :    }
    4781              : 
    4782            0 :    s->AddToObject("properties", m);
    4783            0 :    s->AddToObject("required", MJsonNode::MakeArray());
    4784              : 
    4785            0 :    return s;
    4786              : }
    4787              : 
    4788            0 : MJsonNode* mjsonrpc_get_schema()
    4789              : {
    4790            0 :    return mjsonrpc_make_schema(&gMethodsTable);
    4791              : }
    4792              : 
    4793              : #ifdef MJSON_DEBUG
    4794              : static void mjsonrpc_print_schema()
    4795              : {
    4796              :    MJsonNode *s = mjsonrpc_get_schema();
    4797              :    s->Dump(0);
    4798              :    std::string str = s->Stringify(1);
    4799              :    printf("MJSON-RPC schema:\n");
    4800              :    printf("%s\n", str.c_str());
    4801              :    delete s;
    4802              : }
    4803              : #endif
    4804              : 
    4805            0 : static std::string indent(int x, const char* p = " ")
    4806              : {
    4807            0 :    if (x<1)
    4808            0 :       return "";
    4809            0 :    std::string s;
    4810            0 :    for (int i=0; i<x; i++)
    4811            0 :       s += p;
    4812            0 :    return s;
    4813            0 : }
    4814              : 
    4815              : struct NestedLine {
    4816              :    int nest;
    4817              :    bool span;
    4818              :    std::string text;
    4819              : };
    4820              : 
    4821              : class NestedOutput
    4822              : {
    4823              : public:
    4824              :    std::vector<NestedLine> fLines;
    4825              : public:
    4826            0 :    void Clear()
    4827              :    {
    4828            0 :       fLines.clear();
    4829            0 :    }
    4830              : 
    4831            0 :    void Output(int nest, bool span, std::string text)
    4832              :    {
    4833            0 :       if (text.length() < 1)
    4834            0 :          return;
    4835              : 
    4836            0 :       NestedLine l;
    4837            0 :       l.nest = nest;
    4838            0 :       l.span = span;
    4839            0 :       l.text = text;
    4840            0 :       fLines.push_back(l);
    4841            0 :    };
    4842              : 
    4843            0 :    std::string Print()
    4844              :    {
    4845            0 :       std::vector<int> tablen;
    4846            0 :       std::vector<std::string> tab;
    4847            0 :       std::vector<std::string> tabx;
    4848              :       
    4849            0 :       tablen.push_back(0);
    4850            0 :       tab.push_back("");
    4851            0 :       tabx.push_back("");
    4852              :       
    4853            0 :       std::string xtab = "";
    4854            0 :       int maxlen = 0;
    4855            0 :       for (int n=0; ; n++) {
    4856            0 :          int len = -1;
    4857            0 :          for (unsigned i=0; i<fLines.size(); i++) {
    4858            0 :             int nn = fLines[i].nest;
    4859            0 :             bool pp = fLines[i].span;
    4860            0 :             if (pp)
    4861            0 :                continue;
    4862            0 :             if (nn != n)
    4863            0 :                continue;
    4864            0 :             int l = fLines[i].text.length();
    4865            0 :             if (l>len)
    4866            0 :                len = l;
    4867              :          }
    4868              :          //printf("nest %d len %d\n", n, len);
    4869            0 :          if (len < 0)
    4870            0 :             break; // nothing with this nest level
    4871            0 :          tablen.push_back(len);
    4872            0 :          tab.push_back(indent(len, " ") + " | ");
    4873            0 :          xtab += indent(len, " ") + " | ";
    4874            0 :          tabx.push_back(xtab);
    4875            0 :          maxlen += 3+len;
    4876            0 :       }
    4877              :       
    4878            0 :       std::string s;
    4879            0 :       int nest = 0;
    4880              :       
    4881            0 :       for (unsigned i=0; i<fLines.size(); i++) {
    4882            0 :          int n = fLines[i].nest;
    4883            0 :          bool p = fLines[i].span;
    4884              :          
    4885            0 :          std::string pad;
    4886              :          
    4887            0 :          if (!p) {
    4888            0 :             int ipad = tablen[n+1] - fLines[i].text.length();
    4889            0 :             pad = indent(ipad, " ");
    4890              :          }
    4891              :          
    4892            0 :          std::string hr = indent(maxlen-tabx[n].length(), "-");
    4893              :          
    4894            0 :          if (n > nest)
    4895            0 :             s += std::string(" | ") + fLines[i].text + pad;
    4896            0 :          else if (n == nest) {
    4897            0 :             s += "\n";
    4898            0 :             if (n == 0 || n == 1)
    4899            0 :                s += tabx[n] + hr + "\n";
    4900            0 :             s += tabx[n] + fLines[i].text + pad;
    4901              :          } else {
    4902            0 :             s += "\n";
    4903            0 :             if (n == 0 || n == 1)
    4904            0 :                s += tabx[n] + hr + "\n";
    4905            0 :             s += tabx[n] + fLines[i].text + pad;
    4906              :          }
    4907              :          
    4908            0 :          nest = n;
    4909            0 :       }
    4910              : 
    4911            0 :       return s;
    4912            0 :    }
    4913              : };
    4914              : 
    4915              : static std::string mjsonrpc_schema_to_html_anything(const MJsonNode* schema, int nest_level, NestedOutput* o);
    4916              : 
    4917            0 : static std::string mjsonrpc_schema_to_html_object(const MJsonNode* schema, int nest_level, NestedOutput* o)
    4918              : {
    4919            0 :    const MJsonNode* d = schema->FindObjectNode("description");
    4920            0 :    std::string description;
    4921            0 :    if (d)
    4922            0 :       description = d->GetString();
    4923              : 
    4924            0 :    std::string xshort = "object";
    4925            0 :    if (description.length() > 1)
    4926            0 :       xshort += "</td><td>" + description;
    4927              : 
    4928            0 :    const MJsonNode* properties = schema->FindObjectNode("properties");
    4929              : 
    4930            0 :    const MJsonNodeVector* required_list = NULL;
    4931            0 :    const MJsonNode* r = schema->FindObjectNode("required");
    4932            0 :    if (r)
    4933            0 :       required_list = r->GetArray();
    4934              : 
    4935            0 :    if (!properties) {
    4936            0 :       o->Output(nest_level, false, "object");
    4937            0 :       o->Output(nest_level+1, true, description);
    4938            0 :       return xshort;
    4939              :    }
    4940              : 
    4941            0 :    const MJsonStringVector *names = properties->GetObjectNames();
    4942            0 :    const MJsonNodeVector *nodes = properties->GetObjectNodes();
    4943              : 
    4944            0 :    if (!names || !nodes) {
    4945            0 :       o->Output(nest_level, false, "object");
    4946            0 :       o->Output(nest_level+1, true, description);
    4947            0 :       return xshort;
    4948              :    }
    4949              : 
    4950            0 :    std::string nest = indent(nest_level * 4);
    4951              : 
    4952            0 :    std::string s;
    4953              : 
    4954            0 :    s += nest + "<table border=1>\n";
    4955              : 
    4956            0 :    if (description.length() > 1) {
    4957            0 :       s += nest + "<tr>\n";
    4958            0 :       s += nest + "  <td colspan=3>" + description + "</td>\n";
    4959            0 :       s += nest + "</tr>\n";
    4960              :    }
    4961              : 
    4962            0 :    o->Output(nest_level, true, description);
    4963              : 
    4964            0 :    for (unsigned i=0; i<names->size(); i++) {
    4965            0 :       std::string name = (*names)[i];
    4966            0 :       const MJsonNode* node = (*nodes)[i];
    4967              : 
    4968            0 :       bool required = false;
    4969            0 :       if (required_list)
    4970            0 :          for (unsigned j=0; j<required_list->size(); j++)
    4971            0 :             if ((*required_list)[j])
    4972            0 :                if ((*required_list)[j]->GetString() == name) {
    4973            0 :                   required = true;
    4974            0 :                   break;
    4975              :                }
    4976              : 
    4977            0 :       bool is_array = false;
    4978            0 :       const MJsonNode* type = node->FindObjectNode("type");
    4979            0 :       if (type && type->GetString() == "array")
    4980            0 :          is_array = true;
    4981              : 
    4982            0 :       if (is_array)
    4983            0 :          name += "[]";
    4984              : 
    4985            0 :       if (!required)
    4986            0 :          name += "?";
    4987              : 
    4988            0 :       o->Output(nest_level, false, name);
    4989              : 
    4990            0 :       s += nest + "<tr>\n";
    4991            0 :       s += nest + "  <td>" + name + "</td>\n";
    4992            0 :       s += nest + "  <td>";
    4993            0 :       s += mjsonrpc_schema_to_html_anything(node, nest_level + 1, o);
    4994            0 :       s += "</td>\n";
    4995            0 :       s += nest + "</tr>\n";
    4996            0 :    }
    4997              : 
    4998            0 :    s += nest + "</table>\n";
    4999              : 
    5000            0 :    return s;
    5001            0 : }
    5002              : 
    5003            0 : static std::string mjsonrpc_schema_to_html_array(const MJsonNode* schema, int nest_level, NestedOutput* o)
    5004              : {
    5005            0 :    const MJsonNode* d = schema->FindObjectNode("description");
    5006            0 :    std::string description;
    5007            0 :    if (d)
    5008            0 :       description = d->GetString();
    5009              : 
    5010            0 :    std::string xshort = "array";
    5011            0 :    if (description.length() > 1)
    5012            0 :       xshort += "</td><td>" + description;
    5013              : 
    5014            0 :    const MJsonNode* items = schema->FindObjectNode("items");
    5015              : 
    5016            0 :    if (!items) {
    5017            0 :       o->Output(nest_level, false, "array");
    5018            0 :       o->Output(nest_level+1, true, description);
    5019            0 :       return xshort;
    5020              :    }
    5021              : 
    5022            0 :    const MJsonNodeVector *nodes = items->GetArray();
    5023              : 
    5024            0 :    if (!nodes) {
    5025            0 :       o->Output(nest_level, false, "array");
    5026            0 :       o->Output(nest_level+1, true, description);
    5027            0 :       return xshort;
    5028              :    }
    5029              : 
    5030            0 :    std::string nest = indent(nest_level * 4);
    5031              : 
    5032            0 :    std::string s;
    5033              : 
    5034              :    //s += "array</td><td>";
    5035              : 
    5036            0 :    s += nest + "<table border=1>\n";
    5037              : 
    5038            0 :    if (description.length() > 1) {
    5039            0 :       s += nest + "<tr>\n";
    5040            0 :       s += nest + "  <td>" + description + "</td>\n";
    5041            0 :       s += nest + "</tr>\n";
    5042              :    }
    5043              : 
    5044            0 :    o->Output(nest_level, true, description);
    5045              : 
    5046            0 :    for (unsigned i=0; i<nodes->size(); i++) {
    5047            0 :       o->Output(nest_level, false, "array of");
    5048              : 
    5049            0 :       s += nest + "<tr>\n";
    5050            0 :       s += nest + "  <td> array of " + mjsonrpc_schema_to_html_anything((*nodes)[i], nest_level + 1, o) + "</td>\n";
    5051            0 :       s += nest + "</tr>\n";
    5052              :    }
    5053              : 
    5054            0 :    s += nest + "</table>\n";
    5055              : 
    5056            0 :    return s;
    5057            0 : }
    5058              : 
    5059            0 : std::string mjsonrpc_schema_to_html_anything(const MJsonNode* schema, int nest_level, NestedOutput* o)
    5060              : {
    5061            0 :    std::string type;
    5062            0 :    std::string description;
    5063              :    //bool        optional = false;
    5064              : 
    5065            0 :    const MJsonNode* t = schema->FindObjectNode("type");
    5066            0 :    if (t)
    5067            0 :       type = t->GetString();
    5068              :    else
    5069            0 :       type = "any";
    5070              : 
    5071            0 :    const MJsonNode* d = schema->FindObjectNode("description");
    5072            0 :    if (d)
    5073            0 :       description = d->GetString();
    5074              : 
    5075              :    //const MJsonNode* o = schema->FindObjectNode("optional");
    5076              :    //if (o)
    5077              :    //   optional = o->GetBool();
    5078              : 
    5079            0 :    if (type == "object") {
    5080            0 :       return mjsonrpc_schema_to_html_object(schema, nest_level, o);
    5081            0 :    } else if (type == "array") {
    5082            0 :       return mjsonrpc_schema_to_html_array(schema, nest_level, o);
    5083              :    } else {
    5084              :       //if (optional)
    5085              :       //   output(nest_level, false, "?");
    5086              :       //else
    5087              :       //   output(nest_level, false, "!");
    5088            0 :       o->Output(nest_level, false, type);
    5089            0 :       o->Output(nest_level+1, true, description);
    5090            0 :       if (description.length() > 1) {
    5091            0 :          return (type + "</td><td>" + description);
    5092              :       } else {
    5093            0 :          return (type);
    5094              :       }
    5095              :    }
    5096            0 : }
    5097              : 
    5098            0 : std::string mjsonrpc_schema_to_text(const MJsonNode* schema)
    5099              : {
    5100            0 :    std::string s;
    5101            0 :    NestedOutput out;
    5102            0 :    out.Clear();
    5103            0 :    mjsonrpc_schema_to_html_anything(schema, 0, &out);
    5104              :    //s += "<pre>\n";
    5105              :    //s += nested_dump();
    5106              :    //s += "</pre>\n";
    5107            0 :    s += out.Print();
    5108            0 :    return s;
    5109            0 : }
    5110              : 
    5111            0 : static void add(std::string* s, const char* text)
    5112              : {
    5113            0 :    assert(s != NULL);
    5114            0 :    if (s->length() > 0)
    5115            0 :       *s += ", ";
    5116            0 :    *s += text;
    5117            0 : }
    5118              : 
    5119            0 : static MJsonNode* mjsonrpc_handle_request(const MJsonNode* request)
    5120              : {
    5121              :    // find required request elements
    5122            0 :    const MJsonNode* version = request->FindObjectNode("jsonrpc");
    5123            0 :    const MJsonNode* method  = request->FindObjectNode("method");
    5124            0 :    const MJsonNode* params  = request->FindObjectNode("params");
    5125            0 :    const MJsonNode* id      = request->FindObjectNode("id");
    5126              : 
    5127            0 :    std::string bad = "";
    5128              : 
    5129            0 :    if (!version)
    5130            0 :       add(&bad, "jsonrpc version is missing");
    5131            0 :    if (!method)
    5132            0 :       add(&bad, "method is missing");
    5133            0 :    if (!params)
    5134            0 :       add(&bad, "params is missing");
    5135            0 :    if (!id)
    5136            0 :       add(&bad, "id is missing");
    5137              : 
    5138            0 :    if (version && (version->GetType() != MJSON_STRING))
    5139            0 :       add(&bad, "jsonrpc version is not a string");
    5140            0 :    if (version && (version->GetString() != "2.0"))
    5141            0 :       add(&bad, "jsonrpc version is not 2.0");
    5142              : 
    5143            0 :    if (method && (method->GetType() != MJSON_STRING))
    5144            0 :       add(&bad, "method is not a string");
    5145              : 
    5146            0 :    if (!method || bad.length() > 0) {
    5147            0 :       MJsonNode* response = mjsonrpc_make_error(-32600, "Invalid request", bad.c_str());
    5148            0 :       response->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
    5149            0 :       if (id) {
    5150            0 :          response->AddToObject("id", id->Copy());
    5151              :       } else {
    5152            0 :          response->AddToObject("id", MJsonNode::MakeNull());
    5153              :       }
    5154              : 
    5155            0 :       if (mjsonrpc_debug) {
    5156            0 :          printf("mjsonrpc: invalid request: reply:\n");
    5157            0 :          printf("%s\n", response->Stringify().c_str());
    5158            0 :          printf("\n");
    5159              :       }
    5160              : 
    5161            0 :       return response;
    5162              :    }
    5163              : 
    5164            0 :    double start_time = 0;
    5165              : 
    5166            0 :    if (mjsonrpc_time) {
    5167            0 :       start_time = GetTimeSec();
    5168              :    }
    5169              : 
    5170            0 :    const std::string ms = method->GetString();
    5171            0 :    const char* m = ms.c_str();
    5172              : 
    5173            0 :    MJsonNode* result = NULL;
    5174              : 
    5175              :    // special built-in methods
    5176              : 
    5177            0 :    if (strcmp(m, "echo") == 0) {
    5178            0 :       result = mjsonrpc_make_result(request->Copy());
    5179            0 :    } else if (strcmp(m, "error") == 0) {
    5180            0 :       result = mjsonrpc_make_error(1, "test error", "test error");
    5181            0 :    } else if (strcmp(m, "invalid_json") == 0) {
    5182            0 :       if (mjsonrpc_debug) {
    5183            0 :          printf("mjsonrpc: reply with invalid json\n");
    5184              :       }
    5185            0 :       return MJsonNode::MakeJSON("this is invalid json data");
    5186            0 :    } else if (strcmp(m, "test_nan_inf") == 0) {
    5187            0 :       double one = 1;
    5188            0 :       double zero = 0;
    5189            0 :       double nan = zero/zero;
    5190            0 :       double plusinf = one/zero;
    5191            0 :       double minusinf = -one/zero;
    5192            0 :       MJsonNode* n = MJsonNode::MakeArray();
    5193            0 :       n->AddToArray(MJsonNode::MakeNumber(nan));
    5194            0 :       n->AddToArray(MJsonNode::MakeNumber(plusinf));
    5195            0 :       n->AddToArray(MJsonNode::MakeNumber(minusinf));
    5196            0 :       result = mjsonrpc_make_result("test_nan_plusinf_minusinf", n);
    5197            0 :    } else if (strcmp(m, "test_arraybuffer") == 0) {
    5198            0 :       if (mjsonrpc_debug) {
    5199            0 :          printf("mjsonrpc: reply with test arraybuffer data\n");
    5200              :       }
    5201            0 :       size_t size = 32;
    5202            0 :       char* ptr = (char*)malloc(size);
    5203            0 :       assert(ptr != NULL);
    5204            0 :       for (size_t i=0; i<size; i++) {
    5205            0 :          ptr[i] = 'A' + i;
    5206              :       }
    5207            0 :       *((short*)(ptr+4*2*1)) = 111; // int16[4]
    5208            0 :       *((int*)(ptr+4*2*2)) = 1234; // int32[4]
    5209            0 :       *((double*)(ptr+4*2*3)) = 3.14; // float64[3]
    5210            0 :       return MJsonNode::MakeArrayBuffer(ptr, size);
    5211              :    } else {
    5212            0 :       MethodsTableIterator s = gMethodsTable.find(ms);
    5213            0 :       if (s != gMethodsTable.end()) {
    5214            0 :          bool lock = s->second.fNeedsLocking;
    5215            0 :          if (lock && gMutex)
    5216            0 :             gMutex->lock();
    5217            0 :          result = s->second.fHandler(params);
    5218            0 :          if (lock && gMutex)
    5219            0 :             gMutex->unlock();
    5220              :       } else {
    5221            0 :          result = mjsonrpc_make_error(-32601, "Method not found", (std::string("unknown method: ") + ms).c_str());
    5222              :       }
    5223              :    }
    5224              : 
    5225            0 :    if (mjsonrpc_debug) {
    5226            0 :       printf("mjsonrpc: handler reply:\n");
    5227            0 :       result->Dump();
    5228            0 :       printf("\n");
    5229              :    }
    5230              : 
    5231            0 :    double end_time = 0;
    5232            0 :    double elapsed_time = 0;
    5233            0 :    if (mjsonrpc_time) {
    5234            0 :       end_time = GetTimeSec();
    5235            0 :       elapsed_time = end_time - start_time;
    5236            0 :       if (mjsonrpc_time > 1) {
    5237            0 :          printf("request took %.3f seconds, method [%s]\n", elapsed_time, m);
    5238              :       }
    5239              :    }
    5240              : 
    5241            0 :    if (result->GetType() == MJSON_ARRAYBUFFER) {
    5242            0 :       return result;
    5243              :    }
    5244              :    
    5245            0 :    const MJsonNode *nerror  = result->FindObjectNode("error");
    5246            0 :    const MJsonNode *nresult = result->FindObjectNode("result");
    5247              : 
    5248            0 :    if (nerror) {
    5249            0 :       result->DeleteObjectNode("result");
    5250            0 :    } else if (nresult) {
    5251            0 :       result->DeleteObjectNode("error");
    5252              :    } else {
    5253            0 :       delete result;
    5254            0 :       result = mjsonrpc_make_error(-32603, "Internal error", "bad dispatcher reply: no result and no error");
    5255              :    }
    5256              : 
    5257            0 :    result->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
    5258              : 
    5259            0 :    if (id) {
    5260            0 :       result->AddToObject("id", id->Copy());
    5261              :    } else {
    5262            0 :       result->AddToObject("id", MJsonNode::MakeNull());
    5263              :    }
    5264              : 
    5265            0 :    if (mjsonrpc_time) {
    5266            0 :       result->AddToObject("elapsed_time", MJsonNode::MakeNumber(elapsed_time));
    5267              :    }
    5268              : 
    5269            0 :    assert(result != NULL);
    5270              : 
    5271            0 :    return result;
    5272            0 : }
    5273              : 
    5274            0 : MJsonNode* mjsonrpc_decode_post_data(const char* post_data)
    5275              : {
    5276              :    //printf("mjsonrpc call, data [%s]\n", post_data);
    5277            0 :    MJsonNode *request = MJsonNode::Parse(post_data);
    5278              : 
    5279            0 :    assert(request != NULL); // Parse never returns NULL - either parsed data or an MJSON_ERROR node
    5280              : 
    5281            0 :    if (mjsonrpc_debug) {
    5282            0 :       printf("mjsonrpc: request:\n");
    5283            0 :       request->Dump();
    5284            0 :       printf("\n");
    5285              :    }
    5286              : 
    5287            0 :    if (mjsonrpc_sleep) {
    5288            0 :       if (mjsonrpc_debug) {
    5289            0 :          printf("mjsonrpc: sleep %d\n", mjsonrpc_sleep);
    5290              :       }
    5291            0 :       for (int i=0; i<mjsonrpc_sleep; i++) {
    5292            0 :          sleep(1);
    5293              :       }
    5294              :    }
    5295              : 
    5296            0 :    if (request->GetType() == MJSON_ERROR) {
    5297            0 :       MJsonNode* reply = mjsonrpc_make_error(-32700, "Parse error", request->GetError().c_str());
    5298            0 :       reply->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
    5299            0 :       reply->AddToObject("id", MJsonNode::MakeNull());
    5300              : 
    5301            0 :       if (mjsonrpc_debug) {
    5302            0 :          printf("mjsonrpc: invalid json: reply:\n");
    5303            0 :          printf("%s\n", reply->Stringify().c_str());
    5304            0 :          printf("\n");
    5305              :       }
    5306              : 
    5307            0 :       delete request;
    5308            0 :       return reply;
    5309            0 :    } else if (request->GetType() == MJSON_OBJECT) {
    5310            0 :       MJsonNode* reply = mjsonrpc_handle_request(request);
    5311            0 :       delete request;
    5312            0 :       return reply;
    5313            0 :    } else if (request->GetType() == MJSON_ARRAY) {
    5314            0 :       const MJsonNodeVector* a = request->GetArray();
    5315              : 
    5316            0 :       if (a->size() < 1) {
    5317            0 :          MJsonNode* reply = mjsonrpc_make_error(-32600, "Invalid request", "batch request array has less than 1 element");
    5318            0 :          reply->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
    5319            0 :          reply->AddToObject("id", MJsonNode::MakeNull());
    5320              :          
    5321            0 :          if (mjsonrpc_debug) {
    5322            0 :             printf("mjsonrpc: invalid json: reply:\n");
    5323            0 :             printf("%s\n", reply->Stringify().c_str());
    5324            0 :             printf("\n");
    5325              :          }
    5326              :          
    5327            0 :          delete request;
    5328            0 :          return reply;
    5329              :       }
    5330              : 
    5331            0 :       MJsonNode* reply = MJsonNode::MakeArray();
    5332              : 
    5333            0 :       for (unsigned i=0; i<a->size(); i++) {
    5334            0 :          MJsonNode* r = mjsonrpc_handle_request(a->at(i));
    5335            0 :          reply->AddToArray(r);
    5336            0 :          if (r->GetType() == MJSON_ARRAYBUFFER) {
    5337            0 :             delete request;
    5338            0 :             delete reply;
    5339            0 :             reply = mjsonrpc_make_error(-32600, "Invalid request", "MJSON_ARRAYBUFFER return is not permitted for batch requests");
    5340            0 :             reply->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
    5341            0 :             reply->AddToObject("id", MJsonNode::MakeNull());
    5342            0 :             return reply;
    5343              :          }
    5344              :       }
    5345              : 
    5346            0 :       delete request;
    5347            0 :       return reply;
    5348              :    } else {
    5349            0 :       MJsonNode* reply = mjsonrpc_make_error(-32600, "Invalid request", "request is not a JSON object or JSON array");
    5350            0 :       reply->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
    5351            0 :       reply->AddToObject("id", MJsonNode::MakeNull());
    5352              : 
    5353            0 :       if (mjsonrpc_debug) {
    5354            0 :          printf("mjsonrpc: invalid json: reply:\n");
    5355            0 :          printf("%s\n", reply->Stringify().c_str());
    5356            0 :          printf("\n");
    5357              :       }
    5358              : 
    5359            0 :       delete request;
    5360            0 :       return reply;
    5361              :    }
    5362              : }
    5363              : 
    5364              : /* emacs
    5365              :  * Local Variables:
    5366              :  * tab-width: 8
    5367              :  * c-basic-offset: 3
    5368              :  * indent-tabs-mode: nil
    5369              :  * End:
    5370              :  */
        

Generated by: LCOV version 2.0-1