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