Mjsonrpc: Difference between revisions

From MidasWiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 168: Line 168:
| a || 0-n || n <sup>**</sup>  
| a || 0-n || n <sup>**</sup>  
|}
|}
<sup>**</sup> n=number of data values supplied - see [[#array name only]]
<sup>**</sup> <small>n=number of data values supplied - see [[#array name only]].</small>
 
See [[#Writing an array]] and [[#Reading an array]] for examples.
 
 
== 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/group__mjsonrpc__js.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:
 
<pre>
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);
});
</pre>
 
A partial list of available javascript 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 ==
 
Only a few examples are given below. For more examples:
* look elsewhere on this page
* look at the example experiment examples/javascript1/example.html
* look at the doxygen-generated documentation for mjsonrpc_xxx functions
* look at the implementation of the functions
* look at the implementation of mhttpd internal web pages (functions mhttpd_xxx in mhttpd.js)
 
<pre>
$ 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}
$
</pre>
 
=== Read ODB data ===
The following code illustrates how to use function '''mjsonrpc_db_get_values()''' to read data from the ODB. Some of the data are displayed using InnerHTML. The data are read every 10 seconds. The time changes and a counter increments each time the data are read.
<pre>
<html>
<head>
<title>My Title</title>
<script src='mhttpd.js'>
</script>
<script>
var updatePeriod = 10000; // in msec
var updateTimerId = 0;
var counter = 0;
 
function update()  {
  clearTimeout(updateTimerId);
  load();
  if (updatePeriod > 0)
  updateTimerId = setTimeout('update()', updatePeriod);
}
function load()  {
    counter++;
    document.getElementById('LastUpdated').innerHTML = "Updating..." + new Date;
    document.getElementById('counter').innerHTML = 'Counter: '+ counter
 
    mjsonrpc_db_get_values(["/Runinfo","/Experiment/name"]).then(function(rpc) {
      var runinfo= rpc.result.data[0]
      var name =  rpc.result.data[1]
      document.getElementById("name").innerHTML ='Experiment name ='+ name
      document.getElementById("state").innerHTML ='Run State='+ runinfo.state
      document.getElementById("rn").innerHTML ='Run number='+ runinfo["run number"]
      document.getElementById("status").innerHTML = 'Status: '+ rpc.result.status
    }).catch(function(error) {
      mjsonrpc_error_alert(error);
    });
}
</script>
</head>
<body>
<h2> Javascript code example using mjson_db_get_values with Promises </h2>
<p id="LastUpdated">Last updated: never</p>
<p id="rn">Run Number : unknown</p>
<p id="state">Run State : unknown</p>
<p id="name">Experiment name : unknown</p>
<p id="status">Status : unknown</p>
<p id="counter">Counter: zero</p>
<script>
update()
</script>
</body>
</pre>
The html code when run as a [[Custom Page]] looks like this:
[[File: mjson_example1.jpg|center|Figure 1: code to read from ODB run as a Custom Page]]
<br clear=all>  <!-- clear wraparound after image -->
 
The status value (rpc.result.status) is an array.
* rpc.result.status[0] is the status from reading "/Runinfo", and
* rpc.result.status[1] is the status from reading "/Experiment/name".
A value of 1 means success.
 
=== Write ODB data and read it back ===
The following example illustrates how to write ODB data using the function '''mjsonrpc_db_paste()'''. In this example, when the "Set" button is pressed, data are written to the ODB keys {{Odbpath|path=test}}(integer), {{Odbpath|path=pi}}(float) and {{Odbpath|path=my_string}}(string), all in the ODB subdirectory {{Odbpath|path=/equipment/rpcexample/settings/}}. When the "Read" button is pressed, the three ODB keys are read and the data written to the screen (Figure 2). For convenience, in function set(), the paths of the ODB keys to be written are stored in the array "paths".  Unlike the example above, there is no automatic update.
<pre>
<html>
<head>
<title>My Custom Page Title</title>
<script src='mhttpd.js'>
</script>
<script>
var counter = 0;
 
function load()  {
      counter++;
      document.getElementById('LastUpdated').innerHTML = "Updating..." + new Date;
      mjsonrpc_db_get_values(["/equipment/rpcexample/settings/"]).then(function(rpc) {
        settings= rpc.result.data[0]
        document.getElementById("test").innerHTML ='Integer test ='+  settings.test
        document.getElementById("pi").innerHTML ='Float pi ='+  settings.pi
        document.getElementById("string").innerHTML ='String my_string='+ settings["my string"]
        document.getElementById("status").innerHTML = 'Read Status: '+ rpc.result.status
      }).catch(function(error) {
        mjsonrpc_error_alert(error);
      });
}
 
function set()  {
      var paths=["/equipment/rpcexample/settings/test","/equipment/rpcexample/settings/pi","/equipment/rpcexample/settings/my string"];
      mjsonrpc_db_paste(paths, [10,3.1416,"hallo world"]).then(function(rpc) {
result=rpc.result;      
        document.getElementById("wstatus").innerHTML = 'Write status '+rpc.result.status      
      }).catch(function(error) {
          mjsonrpc_error_alert(error);
      });
}
</script>
</head>
<body>
<h2> Example code using mjsonrpc calls to write and read </h2>
  <p id="LastUpdated">Last updated: never</p>
  <p id="test"> Test : unknown</p>
  <p id="pi">Pi : unknown</p>
  <p id="string">String : unknown</p>
  <p id="status">Read Status : unknown</p>
  <p id="wstatus">Write Status: zero</p>
  <p> <input type=button value='Set values' onClick='set();'> </input>
      <input type=button value='Read values' onClick='load();'></input> >/p>
</body>
</pre>
The output from the above code is shown below.
{| class="wikitable"
|-
! Figure 2a: Initial custom page !! Figure 2b: After pressing "Set" button !! Figure 2c: After pressing "Read" button
|-
|[[File: mjson_example2a.jpg|frame]] || [[File: mjson_example2b.jpg|frame ]] || [[File: mjson_example2c.jpg|frame]]
|}
<br clear=all>  <!-- clear wraparound after image -->
 
After the data is written (Figure 2b) the write status is shown.
The status value (rpc.result.status) is an array with each element showing the status of the three write operations to the three keys, i.e.
* rpc.result.status[0] is the status from writing "test", and
* rpc.result.status[1] is the status from writing "pi".
* rpc.result.status[2] is the status from writing "my_string"
A value of 1 means success.
After the data is read (Figure 2c), the values read and the read status is shown. In this case, the read status only has one value (1=success) because one read operation of the whole subdirectory is performed.


=== Writing an array ===
=== Writing an array ===
Because each array element is accessed individually, each array element value comes with a corresponding status value of odb db_get_data_index(), encoded as an array (i.e. rpc.result.status in example below). Note that the data to be written to the array elements must be enclosed by their own square brackets [] and the number of data values must match the number of array elements listed (except when the array name is used alone -  see [[#note]]). Too few data values will throw an error, too many will be ignored.  
The following example shows using mjsonrpc_db_paste() to write to an array in ODB. See above for [[#supported array index syntax]].
Because each array element is accessed individually, each array element value comes with a corresponding status value of odb db_get_data_index(), encoded as an array (i.e. rpc.result.status in example below). Note that the data to be written to the array elements must be enclosed by their own square brackets [] and the number of data values must match the number of array elements listed (except when the array name is used alone -  see [[#array name only]]). Too few data values will throw an error, too many will be ignored.  
If the array in the ODB is not large enough for the number of elements and data to be written, it will be expanded automatically.
If the array in the ODB is not large enough for the number of elements and data to be written, it will be expanded automatically.


; Writing to a variable and an array  
==== Writing to a variable and an array ====
The following example shows  
The following example shows using mjsonrpc_db_paste()  to
* writing "9" to an integer variable called "test", and  
* write "9" to an integer variable called "test", and to
* writing values to a number of elements of the array "array"
* write values to a number of elements of the array "array"
Both "test" and "array" are keys in the ODB subdirectory {{Odbpath|path=/equipment/rpcexample/settings/}}.
Both "test" and "array" are keys in the ODB subdirectory {{Odbpath|path=/equipment/rpcexample/settings/}}.
<pre>
<pre>
Line 190: Line 371:
: Set Status = 1,1,1,1,1,1,1,1,1  (rpc.result.status)
: Set Status = 1,1,1,1,1,1,1,1,1  (rpc.result.status)
There are 10 elements in the rpc.result.status array, where 1=success. The first element is the status after writing "test", the next eight the status after writing each array element.
There are 10 elements in the rpc.result.status array, where 1=success. The first element is the status after writing "test", the next eight the status after writing each array element.
---------------------


; Array automatically expanded
 
====  automatically expand the array ====
If the array in the ODB contains 12 elements, writing
If the array in the ODB contains 12 elements, writing
  mjsonrpc_db_paste(["/equipment/rpcexample/settings/test","/equipment/rpcexample/settings/array[3-1,4,5,8-10,14]"], [9,[1,2,3,4,5,8,9,10,14] ]).then(function(rpc) { ...
  mjsonrpc_db_paste(["/equipment/rpcexample/settings/test","/equipment/rpcexample/settings/array[3-1,4,5,8-10,14]"], [9,[1,2,3,4,5,8,9,10,14] ]).then(function(rpc) { ...
will automatically expand the array to 15 elements.
will automatically expand the array to 15 elements.


-----------------------
 
; Too few data values  
==== too few data values ====
Supplying too few data values, e.g. writing
Supplying too few data values, e.g. writing
  mjsonrpc_db_paste(["/equipment/rpcexample/settings/array[4,5,8-10]"], [ [4,5,8,9] ]).then(function(rpc) { ...
  mjsonrpc_db_paste(["/equipment/rpcexample/settings/array[4,5,8-10]"], [ [4,5,8,9] ]).then(function(rpc) { ...
should result in an error (bug - crashes program) because there aren't enough data words supplied (4) for the number of indices (5).
should result in an error (bug - crashes program) because there aren't enough data words supplied (4) for the number of indices (5).


--------------------------
 
<div id="array name only"></div>
<div id="array name only"></div>
; Array name only supplied  
==== Array name only supplied ====
If you supply just the array name, then you do not need to supply the same number of elements as the array length. For example,
If you supply just the array name, then you do not need to supply the same number of elements as the array length. For example,
  mjsonrpc_db_paste(["/equipment/rpcexample/settings/array"],[ [7,6,0] ]).then(function(rpc){ ...
  mjsonrpc_db_paste(["/equipment/rpcexample/settings/array"],[ [7,6,0] ]).then(function(rpc){ ...
Line 212: Line 393:
: Set Status = 1  (rpc.result.status)
: Set Status = 1  (rpc.result.status)


---------------------------
 
; Set all elements to one value
==== Set all elements to one value ====
If you want to set the whole array to 1, you might write
If you want to set the whole array to 1, you might write
<pre>
<pre>
Line 223: Line 404:


=== Reading an array ===
=== Reading an array ===
In the same way, you can read all or part of an array.
In the same way, you can read all or part of an array.  See above for [[#Supported array index syntax]].
<pre>
<pre>
var array = new Array();
var array = new Array();
Line 244: Line 425:
Note that the "Array elements" line has read the contents of indices 3-6 and 11 as requested (5 indices in total). The status array contains 8 values. The last 5 values are the status from reading the 5 array elements.
Note that the "Array elements" line has read the contents of indices 3-6 and 11 as requested (5 indices in total). The status array contains 8 values. The last 5 values are the status from reading the 5 array elements.


== 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/group__mjsonrpc__js.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:
<pre>
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);
});
</pre>
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 ==


Only most basic examples are given here. For more examples:
* look elsewhere on this page
* look at the example experiment examples/javascript1/example.html
* look at the doxygen-generated documentation for mjsonrpc_xxx functions
* look at the implementation of the functions
* look at the implementation of mhttpd internal web pages (functions mhttpd_xxx in mhttpd.js)


<pre>
$ 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}
$
</pre>


== Schema (List of all RPC methods) ==
== Schema (List of all RPC methods) ==
Line 323: Line 455:
               | -------------------------------------------------------
               | -------------------------------------------------------
               | result  | status        | integer        | return status of cm_exist()
               | result  | status        | integer        | return status of cm_exist()
------------------------------------------------------------------------
cm_msg1?      | Generate a midas message using cm_msg1()
              | -------------------------------------------------------
              | params  | facility?      | string        | message facility, default is "midas"
              |          | user?          | string        | message user, default is "javascript_commands"
              |          | type?          | integer        | message type, MT_xxx from midas.h, default is MT_INFO
              |          | message        | string        | message text
              | -------------------------------------------------------
              | result  | status        | integer        | return status of cm_msg1()
------------------------------------------------------------------------
------------------------------------------------------------------------
cm_shutdown?  | calls MIDAS cm_shutdown() to stop given MIDAS program
cm_shutdown?  | calls MIDAS cm_shutdown() to stop given MIDAS program
Line 343: Line 484:
               |          |                | array of      | number  
               |          |                | array of      | number  
------------------------------------------------------------------------
------------------------------------------------------------------------
db_create?    | get copies of given ODB subtrees in the "save" json encoding
db_create?    | Create new ODB entries
               | -------------------------------------------------------
               | -------------------------------------------------------
               | params[] | array of ODB paths to be created
               | params[] | array of ODB paths to be created
               |          | array of      | arguments to db_create() and db_resize()
               |          | array of      | arguments to db_create_key() and db_set_num_values()
               |          |                | path          | string  | ODB path
               |          |                | path          | string  | ODB path to be created
               |          |                | type          | integer | MIDAS TID_xxx type
               |          |                | type          | integer | MIDAS TID_xxx type
               |          |                | array_length?  | integer | optional array length, default is 1
               |          |                | array_length?  | integer | optional array length, default is 1
               |          |                | string_length? | integer | for TID_STRING, optional string length, default is NAME_LENGTH
               |          |                | string_length? | integer | for TID_STRING, optional string length, default is NAME_LENGTH
               | -------------------------------------------------------
               | -------------------------------------------------------
               | result  | status[]      | return status of db_create() for each path
               | result  | status[]      | return status of db_create_key(), db_set_num_values() and db_set_data() (for TID_STRING) for each path
              |          |                | array of      | integer
------------------------------------------------------------------------
db_delete?    | delete ODB keys
              | -------------------------------------------------------
              | params  | paths[]        | array of ODB paths to delete
              |          |                | array of      | string
              | -------------------------------------------------------
              | result  | status[]      | return status of db_delete_key() for each path
               |          |                | array of      | integer
               |          |                | array of      | integer
------------------------------------------------------------------------
------------------------------------------------------------------------
Line 375: Line 524:
               | -------------------------------------------------------
               | -------------------------------------------------------
               | result  | status[]      | return status of db_paste_json() for each path
               | result  | status[]      | return status of db_paste_json() for each path
              |          |                | array of      | integer
------------------------------------------------------------------------
db_resize?    | Change size of ODB arrays
              | -------------------------------------------------------
              | params  | paths[]        | array of ODB paths to resize
              |          |                | array of      | string
              |          | new_lengths[]  | array of new lengths for each ODB path
              |          |                | array of      | integer
              | -------------------------------------------------------
              | result  | status[]      | return status of db_set_num_values() for each path
               |          |                | array of      | integer
               |          |                | array of      | integer
------------------------------------------------------------------------
------------------------------------------------------------------------
Line 432: Line 591:
               | result  | status        | integer        | returns the value of "arg" parameter
               | result  | status        | integer        | returns the value of "arg" parameter
</pre>
</pre>
[[Category:Javascript library]]
[[Category:Javascript library]]

Revision as of 17:40, 20 January 2016


MIDAS JSON-RPC interface

Links


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:

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,
  ...

Access to ODB arrays

Some (not all) JSON-RPC methods allow access to individual array elements in ODB, i.e. /equipment/rpcexample/settings/a[10].

RPC methods that can do this are:

  • db_copy
  • db_get_values
  • db_paste

Supported array index syntax

An array index syntax has been implemented to make it easier to write individual array elements (Table 1)

Table 1
Array Syntax Array Elements Number of elements/data values
a[1] 1 1
a[1,2,3] 1,2,3 3
a[1-3] 1,2,3 3
a[3-1] 3,2,1 3
a[1,2,3-5,6] 1,2,3,4,5,6 6
a[1-3,4-6,7-9] 1,2,3,4,5,6,7,8,9 9
a[3-0,6-4,9-7] 3,2,1,6,5,4,9,8,7 9
a[4,2,5-6,8] 4,2,5,6,8 5
a 0-n n **

** n=number of data values supplied - see #array name only.

See #Writing an array and #Reading an array for examples.


JSON-RPC general information

Javascript client library

MIDAS provides a simple client library:

The MIDAS JSON RPC client library is based on the Javascript Promise pattern:

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 javascript 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

Only a few examples are given below. For more examples:

  • look elsewhere on this page
  • look at the example experiment examples/javascript1/example.html
  • look at the doxygen-generated documentation for mjsonrpc_xxx functions
  • look at the implementation of the functions
  • look at the implementation of mhttpd internal web pages (functions mhttpd_xxx in mhttpd.js)
$ 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}
$ 

Read ODB data

The following code illustrates how to use function mjsonrpc_db_get_values() to read data from the ODB. Some of the data are displayed using InnerHTML. The data are read every 10 seconds. The time changes and a counter increments each time the data are read.

<html>
<head>
<title>My Title</title>
<script src='mhttpd.js'> 
</script>
<script>
var updatePeriod = 10000; // in msec
var updateTimerId = 0;
var counter = 0;

function update()  {
   clearTimeout(updateTimerId);
   load();
   if (updatePeriod > 0)
   updateTimerId = setTimeout('update()', updatePeriod);
}
function load()   {
    counter++;
    document.getElementById('LastUpdated').innerHTML = "Updating..." + new Date;
    document.getElementById('counter').innerHTML = 'Counter: '+ counter

    mjsonrpc_db_get_values(["/Runinfo","/Experiment/name"]).then(function(rpc) {
       var runinfo= rpc.result.data[0]
       var name =  rpc.result.data[1]
       document.getElementById("name").innerHTML ='Experiment name ='+ name
       document.getElementById("state").innerHTML ='Run State='+ runinfo.state
       document.getElementById("rn").innerHTML ='Run number='+ runinfo["run number"]
       document.getElementById("status").innerHTML = 'Status: '+ rpc.result.status
    }).catch(function(error) {
       mjsonrpc_error_alert(error);
    });
}
</script>
</head>
<body>
<h2> Javascript code example using mjson_db_get_values with Promises </h2>
<p id="LastUpdated">Last updated: never</p>
<p id="rn">Run Number : unknown</p>
<p id="state">Run State : unknown</p>
<p id="name">Experiment name : unknown</p>
<p id="status">Status : unknown</p>
<p id="counter">Counter: zero</p>
<script>
update()
</script>
</body>

The html code when run as a Custom Page looks like this:

Figure 1: code to read from ODB run as a Custom Page


The status value (rpc.result.status) is an array.

  • rpc.result.status[0] is the status from reading "/Runinfo", and
  • rpc.result.status[1] is the status from reading "/Experiment/name".

A value of 1 means success.

Write ODB data and read it back

The following example illustrates how to write ODB data using the function mjsonrpc_db_paste(). In this example, when the "Set" button is pressed, data are written to the ODB keys test(integer), pi(float) and my_string(string), all in the ODB subdirectory /equipment/rpcexample/settings/. When the "Read" button is pressed, the three ODB keys are read and the data written to the screen (Figure 2). For convenience, in function set(), the paths of the ODB keys to be written are stored in the array "paths". Unlike the example above, there is no automatic update.

<html>
<head>
<title>My Custom Page Title</title>
<script src='mhttpd.js'> 
</script>
<script>
var counter = 0;

function load()  {
      counter++;
      document.getElementById('LastUpdated').innerHTML = "Updating..." + new Date;
      mjsonrpc_db_get_values(["/equipment/rpcexample/settings/"]).then(function(rpc) {
         settings= rpc.result.data[0]
         document.getElementById("test").innerHTML ='Integer test ='+  settings.test
         document.getElementById("pi").innerHTML ='Float pi ='+  settings.pi
         document.getElementById("string").innerHTML ='String my_string='+ settings["my string"]
         document.getElementById("status").innerHTML = 'Read Status: '+ rpc.result.status
      }).catch(function(error) {
         mjsonrpc_error_alert(error);
      });
}

function set()  {
      var paths=["/equipment/rpcexample/settings/test","/equipment/rpcexample/settings/pi","/equipment/rpcexample/settings/my string"];
      mjsonrpc_db_paste(paths, [10,3.1416,"hallo world"]).then(function(rpc) {
	 result=rpc.result;	      
         document.getElementById("wstatus").innerHTML = 'Write status '+rpc.result.status	      
      }).catch(function(error) {
          mjsonrpc_error_alert(error);
      });
}
</script>
</head>
<body>
<h2> Example code using mjsonrpc calls to write and read </h2>
  <p id="LastUpdated">Last updated: never</p>
  <p id="test"> Test : unknown</p>
  <p id="pi">Pi : unknown</p>
  <p id="string">String : unknown</p>
  <p id="status">Read Status : unknown</p>
  <p id="wstatus">Write Status: zero</p>
  <p> <input type=button value='Set values' onClick='set();'> </input>
       <input type=button value='Read values' onClick='load();'></input> >/p>
</body>

The output from the above code is shown below.

Figure 2a: Initial custom page Figure 2b: After pressing "Set" button Figure 2c: After pressing "Read" button
Mjson example2a.jpg
Mjson example2b.jpg
Mjson example2c.jpg


After the data is written (Figure 2b) the write status is shown. The status value (rpc.result.status) is an array with each element showing the status of the three write operations to the three keys, i.e.

  • rpc.result.status[0] is the status from writing "test", and
  • rpc.result.status[1] is the status from writing "pi".
  • rpc.result.status[2] is the status from writing "my_string"

A value of 1 means success. After the data is read (Figure 2c), the values read and the read status is shown. In this case, the read status only has one value (1=success) because one read operation of the whole subdirectory is performed.

Writing an array

The following example shows using mjsonrpc_db_paste() to write to an array in ODB. See above for #supported array index syntax. Because each array element is accessed individually, each array element value comes with a corresponding status value of odb db_get_data_index(), encoded as an array (i.e. rpc.result.status in example below). Note that the data to be written to the array elements must be enclosed by their own square brackets [] and the number of data values must match the number of array elements listed (except when the array name is used alone - see #array name only). Too few data values will throw an error, too many will be ignored. If the array in the ODB is not large enough for the number of elements and data to be written, it will be expanded automatically.

Writing to a variable and an array

The following example shows using mjsonrpc_db_paste() to

  • write "9" to an integer variable called "test", and to
  • write values to a number of elements of the array "array"

Both "test" and "array" are keys in the ODB subdirectory /equipment/rpcexample/settings/.

  mjsonrpc_db_paste(["/equipment/rpcexample/settings/test","/equipment/rpcexample/settings/array[3-1,4,5,8-10]"], [9,[1,2,3,4,5,8,9,10]]).then(function(rpc) {
       document.getElementById("wstatus").innerHTML ='Set Status='+ rpc.result.status
  }).catch(function(error) {
       mjsonrpc_error_alert(error);
    });

Assuming the array was filled with zeroes to start with, after writing to the array and reading it back, it would now contain the values

Array= 0,3,2,1,4,5,0,0,8,9,10,11
Set Status = 1,1,1,1,1,1,1,1,1 (rpc.result.status)

There are 10 elements in the rpc.result.status array, where 1=success. The first element is the status after writing "test", the next eight the status after writing each array element.


automatically expand the array

If the array in the ODB contains 12 elements, writing

mjsonrpc_db_paste(["/equipment/rpcexample/settings/test","/equipment/rpcexample/settings/array[3-1,4,5,8-10,14]"], [9,[1,2,3,4,5,8,9,10,14] ]).then(function(rpc) { ...

will automatically expand the array to 15 elements.


too few data values

Supplying too few data values, e.g. writing

mjsonrpc_db_paste(["/equipment/rpcexample/settings/array[4,5,8-10]"], [ [4,5,8,9] ]).then(function(rpc) { ...

should result in an error (bug - crashes program) because there aren't enough data words supplied (4) for the number of indices (5).


Array name only supplied

If you supply just the array name, then you do not need to supply the same number of elements as the array length. For example,

mjsonrpc_db_paste(["/equipment/rpcexample/settings/array"],[ [7,6,0] ]).then(function(rpc){ ...

will write the first three elements of the array to 7,6,0. Other array elements will be untouched. In this case, there is only one status value for the whole array.

Array= 7,6,0,1,4,5,0,0,8,9,10,11
Set Status = 1 (rpc.result.status)


Set all elements to one value

If you want to set the whole array to 1, you might write

var ones=new Array(10);
for (i=0; i<ones.length; i++)
  ones[i]=1;
mjsonrpc_db_paste(["/equipment/rpcexample/settings/array"], [ones]).then(function(rpc) { ...

Reading an array

In the same way, you can read all or part of an array. See above for #Supported array index syntax.

var array = new Array();
mjsonrpc_db_get_values(["/Runinfo","/Experiment/name","/Equipment/rpcexample/Settings/array","/Equipment/rpcexample/Settings/array[3-6,11]"]).then(function(rpc) {
      document.getElementById("status").innerHTML = 'Status : '+ result.status  
      var runinfo= rpc.result.data[0] 
      var name =  rpc.result.data[1]
      array = rpc.result.data[2]   // whole array
      document.getElementById("array").innerHTML ='Array='+ array
      array=rpc.result.data[3]    // array indices
      document.getElementById("array_ele").innerHTML ='Array elements='+ array
}).catch(function(error) {
   mjsonrpc_error_alert(error);
});

This example gives

Array=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0
Array elements=4,5,6,7,12
Status : 1,1,1,1,1,1,1,1 (rpc.result.status)

Note that the "Array elements" line has read the contents of indices 3-6 and 11 as requested (5 indices in total). The status array contains 8 values. The last 5 values are the status from reading the 5 array elements.



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_msg1?       | Generate a midas message using cm_msg1()
               | -------------------------------------------------------
               | params   | facility?      | string         | message facility, default is "midas"
               |          | user?          | string         | message user, default is "javascript_commands"
               |          | type?          | integer        | message type, MT_xxx from midas.h, default is MT_INFO
               |          | message        | string         | message text
               | -------------------------------------------------------
               | result   | status         | integer        | return status of cm_msg1()
------------------------------------------------------------------------
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?     | Create new ODB entries
               | -------------------------------------------------------
               | params[] | array of ODB paths to be created
               |          | array of       | arguments to db_create_key() and db_set_num_values()
               |          |                | path           | string  | ODB path to be created
               |          |                | 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_key(), db_set_num_values() and db_set_data() (for TID_STRING) for each path
               |          |                | array of       | integer
------------------------------------------------------------------------
db_delete?     | delete ODB keys
               | -------------------------------------------------------
               | params   | paths[]        | array of ODB paths to delete
               |          |                | array of       | string 
               | -------------------------------------------------------
               | result   | status[]       | return status of db_delete_key() 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
------------------------------------------------------------------------
db_resize?     | Change size of ODB arrays
               | -------------------------------------------------------
               | params   | paths[]        | array of ODB paths to resize
               |          |                | array of       | string 
               |          | new_lengths[]  | array of new lengths for each ODB path
               |          |                | array of       | integer
               | -------------------------------------------------------
               | result   | status[]       | return status of db_set_num_values() 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