Mjsonrpc
MIDAS JSON-RPC interface
JSON general information
JSON is a lightweight data-interchange format usually associated with Javascript and web programming. It is a popular choice as replacement for older general purpose data formats such as XML. JSON is defined by RFC-7159 (read more at http://www.json.org/). When necessary, the overhead of text encoded JSON is reduced by compressing JSON documents (using gzip), or by using binary-encoded JSON.
JSON documents look like this:
{ "Runinfo" : { "State" : 1, "Online Mode" : 1, "Run number" : 13585 } }
Note that the JSON standard is incompatible with IEEE Standard for Floating-Point Arithmetic (IEEE 754), specifically, there is no standard way to encode the special numerical values +Infinity, -Infinity and NaN ("-0.0" is ok).
In MIDAS, JSON support is provided by an ODB data encoder (in odb.c), an ODB "JSON paste" decoder (in json_paste.cxx) and a general purpose JSON encoder/decoder (mjson.h, mjson.cxx, see https://daq.triumf.ca/~daqweb/doc/midas-devel/html/mjson_8h.html and https://daq.triumf.ca/~daqweb/doc/midas-devel/html/class_m_json_node.html).
The MIDAS implementation of JSON has following variances from the JSON standard:
- numerical values +Infinity, -Infinity and NaN are encoded and decoded as JSON strings "Infinity", "-Infinity" and "NaN". This is compatible with most in-browser JSON implementations. See also http://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript and similar.
- numerical values that are usually presented as hex numbers, such a DWORD values, are encoded as JSON strings, i.e. "0x55b961c8".
JSON encoding of ODB data
MIDAS has 3 way to encode ODB data into JSON:
- "save" format for saving all ODB data and metadata, ODB can be fully reloaded or restored from an ODB JSON save file. Links are preserved as links, upper and lower case in names of ODB keys is preserved.
- "db_values" format for exporting ODB data to web pages: links are followed to their final values, ODB key names are converted to lower case for use with case-sensitive languages such as Javascript.
- "list" format encodes a single ODB directory and returns the full information printed by the odbedit "ls -l" command.
For example:
JSON "save" format: db_copy_json_save()
The "save" format encodes all ODB data and metadata. ODB can be fully reloaded or restored from ODB JSON save files.
$ odbedit odbedit> cd runinfo [local:testexpt:S]/Runinfo>json status: 1, json: { "State/key" : { "type" : 7, "access_mode" : 7, "last_written" : 1438212553 }, "State" : 1, "Online Mode/key" : { "type" : 7, "access_mode" : 7, "last_written" : 1436830326 }, "Online Mode" : 1, "Run number/key" : { "type" : 7, "access_mode" : 7, "last_written" : 1438212481 }, "Run number" : 13585, "Transition in progress/key" : { "type" : 7, "access_mode" : 7, "last_written" : 1438212553 }, "Transition in progress" : 0, "Start abort/key" : { "type" : 7, "access_mode" : 7, "last_written" : 1438212552 }, "Start abort" : 0, "Requested transition/key" : { "type" : 7, "access_mode" : 7, "last_written" : 1436923816 }, "Requested transition" : 0, "Start time/key" : { "type" : 12, "item_size" : 32, "access_mode" : 7, "last_written" : 1438212481 }, "Start time" : "Wed Jul 29 16:28:01 2015", "Start time binary/key" : { "type" : 6, "access_mode" : 7, "last_written" : 1438212481 }, "Start time binary" : "0x55b96181", "Stop time/key" : { "type" : 12, "item_size" : 32, "access_mode" : 7, "last_written" : 1438212552 }, "Stop time" : "Wed Jul 29 16:29:12 2015", "Stop time binary/key" : { "type" : 6, "access_mode" : 7, "last_written" : 1438212552 }, "Stop time binary" : "0x55b961c8" } [local:testexpt:S]/Runinfo>
JSON "db_values" format: db_copy_json_values()
The "db_values" format is intended for easy web page development - main thing is to return easy to parse ODB values with minimal metadata (only ODB last_written is provided).
Because most programming languages and most JSON parser (including Javascript) are case sensitive, but ODB key names are not, this JSON encoding normalizes ODB key names by converting them to lower-case. (Converting spaces to underscores is also a possibility).
This allows easy use of parsed JSON in Javascript, i.e. runinfo.state or runinfo["run number"] while avoiding case-matching getter functions (i.e. get_case_insensitive_property(runinfo, "state")).
Symlinks are followed to their final values and subdirectories are recursed.
$ odbedit odbedit> cd runinfo [local:testexpt:S]/Runinfo>jsvalues status: 1, json: { "state/last_written" : 1438212553, "state" : 1, "online mode/last_written" : 1436830326, "online mode" : 1, "run number/last_written" : 1438212481, "run number" : 13585, "transition in progress/last_written" : 1438212553, "transition in progress" : 0, "start abort/last_written" : 1438212552, "start abort" : 0, "requested transition/last_written" : 1436923816, "requested transition" : 0, "start time/last_written" : 1438212481, "start time" : "Wed Jul 29 16:28:01 2015", "start time binary/last_written" : 1438212481, "start time binary" : "0x55b96181", "stop time/last_written" : 1438212552, "stop time" : "Wed Jul 29 16:29:12 2015", "stop time binary/last_written" : 1438212552, "stop time binary" : "0x55b961c8" } [local:testexpt:S]/Runinfo>
JSON "ls" format: db_copy_json_ls()
The "ls" format returns all the data printed by odbedit command "ls -l" and shown by the mhttpd ODB editor web page. It is intended for implementing web ODB editor functions.
Note how subdirectories are encoded as empty JSON objects "{}".
$ odbedit odbedit> cd experiment [local:testexpt:S]/Experiment>jsls jsls "/Experiment", status: 1, json: { "Name/key" : { "type" : 12, "item_size" : 32, "access_mode" : 7, "last_written" : 1448038414 }, "Name" : "testexpt", "Buffer sizes" : { }, "edit on start" : { }, "Security" : { }, "Transition debug flag/key" : { "type" : 7, "access_mode" : 7, "last_written" : 1436830326 }, "Transition debug flag" : 0, "Transition timeout/key" : { "type" : 7, "access_mode" : 7, "last_written" : 1436830326 }, "Transition timeout" : 30000, ...
JSON-RPC general information
- JSON-RPC standard: https://daq.triumf.ca/~daqweb/doc/midas-devel/doc/jsonrpc/JSON-RPC%202.0%20Specification.html
- JSON-RPC home page: http://www.jsonrpc.org/specification
- JSON-RPC via HTTP transport: https://daq.triumf.ca/~daqweb/doc/midas-devel/doc/jsonrpc/simple%20is%20better%20-%20JSON-RPC%202.0%20Transport_%20HTTP.html
- http://www.simple-is-better.org/json-rpc/index.html
Javascript client library
MIDAS provides a simple client library:
- documentation, see the mjsonrpc functions: https://daq.triumf.ca/~daqweb/doc/midas-devel/html/mhttpd_8js.html
- source code, see the mjsonrpc functions: https://daq.triumf.ca/~daqweb/doc/midas-devel/resources/mhttpd.js
The MIDAS JSON RPC client library is based on the Javascript Promise pattern:
- http://www.html5rocks.com/en/tutorials/es6/promises/
- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
A simple example using the Promise API to display an ODB value on a web page:
mjsonrpc_db_get_values(["/runinfo/run number"]).then(function(rpc) { document.getElementById("run_number").innerHTML = rpc.response.data[0]; }).catch(function(error) { mjsonrpc_error_alert(error); });
A partial list of available javasctipt functions. For the full list, and for full documentation, please go to the doxygen-generated documentation at https://daq.triumf.ca/~daqweb/doc/midas-devel/html/mhttpd_8js.html
- function mjsonrpc_set_url (url) - set the URL of the MIDAS JSON-RPC server, if different from web page URL. Cross-site access is fully supported (see CORS).
- function mjsonrpc_call (method, params, id) - call arbitrary RPC method
- function mjsonrpc_start_program (name, id, callback, error_callback)
- function mjsonrpc_stop_program (name, unique, id, callback, error_callback)
- function mjsonrpc_db_get_values (paths, id, callback, error_callback) - read ODB values
- function mjsonrpc_db_paste (paths, values, id, callback, error_callback) - write ODB values
Examples
$ curl -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","id":null,"method":"db_get_values","params":{"paths":["/runinfo/run number"]}}' 'http://localhost:8080?mjsonrpc' {"jsonrpc": "2.0","result":{"data":[324],"status":[1],"last_written":[1443570804]},"id":null} $
Schema (List of all RPC methods)
The MIDAS JSON RPC API is documented via an automatically generated JSON Schema.
The JSON Schema ("describes your JSON data format") is not a well established standard (there is no final RFC) but seems to be in common use. Multiple 3rd party tools exist to visualize, explore and interface with JSON documents described by JSON Schemas. For more general information go here:
The MIDAS JSON RPC Schema is automatically generated by mhttpd and is linked by the mhttpd "Help" page in JSON and in text format.
For reference, a recent copy of the JSON RPC API Schema is pasted here from the mhttpd "help" page. For final, complete and up-to-date schema, please get your own copy from the midas "help" page.
In this schema, "?" means an optional parameter, "[]" means an array parameter. (There is an artefact: all method names have question marks because they are optional as far as the schema generator is concerned).
MIDAS JSON RPC API Schema
------------------------------------------------------------------------ Autogenerated schema for all MIDAS JSON-RPC methods ------------------------------------------------------------------------ cm_exist? | calls MIDAS cm_exist() to check if given MIDAS program is running | ------------------------------------------------------- | params | name | string | name of the program, corresponding to ODB /Programs/name | | unique? | bool | bUnique argument to cm_exist() | ------------------------------------------------------- | result | status | integer | return status of cm_exist() ------------------------------------------------------------------------ cm_shutdown? | calls MIDAS cm_shutdown() to stop given MIDAS program | ------------------------------------------------------- | params | name | string | name of the program, corresponding to ODB /Programs/name | | unique? | bool | bUnique argument to cm_shutdown() | ------------------------------------------------------- | result | status | integer | return status of cm_shutdown() ------------------------------------------------------------------------ db_copy? | get copies of given ODB subtrees in the "save" json encoding | ------------------------------------------------------- | params | paths[] | array of ODB subtree paths, see note on array indices | | | array of | string | ------------------------------------------------------- | result | data[] | copy of ODB data for each path | | | array of | object | | status[] | return status of db_copy_json() for each path | | | array of | integer | | last_written[] | last_written value of the ODB subtree for each path | | | array of | number ------------------------------------------------------------------------ db_create? | get copies of given ODB subtrees in the "save" json encoding | ------------------------------------------------------- | params[] | array of ODB paths to be created | | array of | arguments to db_create() and db_resize() | | | path | string | ODB path | | | type | integer | MIDAS TID_xxx type | | | array_length? | integer | optional array length, default is 1 | | | string_length? | integer | for TID_STRING, optional string length, default is NAME_LENGTH | ------------------------------------------------------- | result | status[] | return status of db_create() for each path | | | array of | integer ------------------------------------------------------------------------ db_get_values? | get values of ODB data from given subtrees | ------------------------------------------------------- | params | paths[] | array of ODB subtree paths, see note on array indices | | | array of | string | ------------------------------------------------------- | result | data[] | values of ODB data for each path, all key names are in lower case, all symlinks are followed | | | array of | any | | status[] | return status of db_copy_json() for each path | | | array of | integer | | last_written[] | last_written value of the ODB subtree for each path | | | array of | number ------------------------------------------------------------------------ db_paste? | write data into ODB | ------------------------------------------------------- | params | paths[] | array of ODB subtree paths, see note on array indices | | | array of | string | | values[] | data to be written using db_paste_json() | | | array of | any | ------------------------------------------------------- | result | status[] | return status of db_paste_json() for each path | | | array of | integer ------------------------------------------------------------------------ get_debug? | get current value of mjsonrpc_debug | ------------------------------------------------------- | params | any | there are no input parameters | ------------------------------------------------------- | result | integer | current value of mjsonrpc_debug ------------------------------------------------------------------------ get_schema? | Get the MIDAS JSON-RPC schema JSON object | ------------------------------------------------------- | params | any | there are no input parameters | ------------------------------------------------------- | result | object | returns the MIDAS JSON-RPC schema JSON object ------------------------------------------------------------------------ null? | RPC method always returns null | ------------------------------------------------------- | params | any | method parameters are ignored | ------------------------------------------------------- | result | null | always returns null ------------------------------------------------------------------------ set_debug? | set new value of mjsonrpc_debug | ------------------------------------------------------- | params | integer | new value of mjsonrpc_debug | ------------------------------------------------------- | result | integer | new value of mjsonrpc_debug ------------------------------------------------------------------------ start_program? | start MIDAS program defined in ODB /Programs/name | ------------------------------------------------------- | params | name | string | name of the program, corresponding to ODB /Programs/name | ------------------------------------------------------- | result | status | integer | return status of ss_system() ------------------------------------------------------------------------ user_example1? | example of user defined RPC method that returns up to 3 results | ------------------------------------------------------- | params | arg | string | example string argment | | optional_arg? | integer | optional example integer argument | ------------------------------------------------------- | result | string | string | returns the value of "arg" parameter | | integer | integer | returns the value of "optional_arg" parameter ------------------------------------------------------------------------ user_example2? | example of user defined RPC method that returns more than 3 results | ------------------------------------------------------- | params | arg | string | example string argment | | optional_arg? | integer | optional example integer argument | ------------------------------------------------------- | result | string1 | string | returns the value of "arg" parameter | | string2 | string | returns "hello" | | string3 | string | returns "world!" | | value1 | integer | returns the value of "optional_arg" parameter | | value2 | number | returns 3.14 ------------------------------------------------------------------------ user_example3? | example of user defined RPC method that returns an error | ------------------------------------------------------- | params | arg | integer | integer value, if zero, throws a JSON-RPC error | ------------------------------------------------------- | result | status | integer | returns the value of "arg" parameter