https://daq00.triumf.ca/MidasWiki/api.php?action=feedcontributions&user=Stefan+Ritt&feedformat=atomMidasWiki - User contributions [en]2024-03-28T23:40:39ZUser contributionsMediaWiki 1.39.6https://daq00.triumf.ca/MidasWiki/index.php?title=/Equipment_ODB_tree&diff=3419/Equipment ODB tree2024-01-17T13:03:10Z<p>Stefan Ritt: /* Unit */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
== Links ==<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[Frontend user code]] <br />
* [[Event Structure]]<br />
* [[Equipment List Parameters]]<br />
* <br />
</div><br />
<br />
== Purpose ==<br />
The ODB /Equipment tree contains user and system information related to a frontend. <br />
<br />
== Creating the /Equipment tree ==<br />
An empty /Equipment tree will be created by mlogger.<br />
<br />
If an [[ Frontend Operation#Equipments and Events|Equipment]] is defined in [[Frontend user code]], the first time the frontend is run, if that equipment does not exist in the ODB, it<br />
will be created (using db_create_record) in the ODB as a subtree under the /Equipment tree with the values supplied in the [[Frontend user code]]. <br />
<br />
== Example of /Equipment tree ==<br />
<br />
The following is an example of the /Equipment tree from an experiment obtained with the [[odbedit|odbedit ls command]] listing all the defined equipments in the experiment :<br />
<br />
[local:pol:S]Settings>ls /Equipment -l<br />
Key name Type #Val Size Last Opn Mode Value<br />
---------------------------------------------------------------------------<br />
POL_ACQ DIR<br />
Scaler DIR<br />
Trigger DIR<br />
VME DIR<br />
SisPulser DIR<br />
INFO DIR<br />
HISTO DIR<br />
<br />
<br />
The following is a listing of one of the equipments in the /Equipment tree shown above :<br />
<br />
[local:pol:S]Settings>ls /Equipment/info/ -lr <br />
Key name Type #Val Size Last Opn Mode Value<br />
---------------------------------------------------------------------------<br />
INFO DIR<br />
Common DIR<br />
Event ID WORD 1 2 9h 0 RWD 3<br />
Trigger mask WORD 1 2 9h 0 RWD 8<br />
Buffer STRING 1 32 9h 0 RWD SYSTEM<br />
Type INT 1 4 9h 0 RWD 1<br />
Source INT 1 4 9h 0 RWD 0<br />
Format STRING 1 8 9h 0 RWD MIDAS<br />
Enabled BOOL 1 4 9h 0 RWD y<br />
Read on INT 1 4 9h 0 RWD 257<br />
Period INT 1 4 9h 0 RWD 100<br />
Event limit DOUBLE 1 8 9h 0 RWD 0<br />
Num subevents DWORD 1 4 9h 0 RWD 0<br />
Log history INT 1 4 2h 0 RWD 60<br />
Frontend host STRING 1 32 9h 0 RWD lxpol.triumf.ca<br />
Frontend name STRING 1 32 9h 0 RWD pol_fevme<br />
Frontend file name STRING 1 256 9h 0 RWD pol_fevme.cxx<br />
Status STRING 1 256 9h 0 RWD pol_fevme@lxpol.triumf.ca<br />
Status color STRING 1 32 9h 0 RWD #00FF00<br />
Hidden BOOL 1 4 9h 0 RWD n<br />
<div id="Example Variables subtree"></div><br />
Variables DIR<br />
DBUG FLOAT 9 4 2h 0 RWD<br />
[0] 0<br />
[1] 101<br />
[2] 10201<br />
[3] 10201<br />
[4] 101<br />
[5] 2<br />
[6] 4<br />
[7] 1<br />
[8] 1<br />
<br />
CYCL FLOAT 15 4 2h 0 RWD<br />
[0] 1<br />
[1] 9800<br />
[2] 98<br />
[3] 100<br />
[4] 9<br />
[5] 98<br />
[6] 9800<br />
[7] 9<br />
[8] 9<br />
[9] 8.9682<br />
[10] 8.966<br />
[11] 0.0504<br />
[12] 0<br />
[13] 8.7906<br />
[14] 0<br />
SUMS DOUBLE 4 8 2h 0 RWD<br />
[0] 0<br />
[1] 50002<br />
[2] 0<br />
[3] 0<br />
<div id="Example Statistics subtree"></div><br />
Statistics DIR<br />
Events sent DOUBLE 1 8 8h 0 RWD 176<br />
Events per sec. DOUBLE 1 8 8h 0 RWD 0<br />
kBytes per sec. DOUBLE 1 8 8h 0 RWD 0<br />
<div id="Example Settings subtree"></div><br />
Settings DIR<br />
Names DBUG STRING 12 32 9h 0 RWD <br />
[0] Data still buffered<br />
[1] Num SIS LNE events<br />
[2] LNE count from SIS<br />
[3] LNE preset value<br />
[4] number of bins<br />
[5] data bytes code<br />
[6] num enabled channels<br />
[7] discard first bin<br />
[8] discard first cycle<br />
[9] not used<br />
[10] not used<br />
[11] not used<br />
Names CYCL STRING 15 32 9h 0 RWD <br />
[0] Scan type code<br />
[1] Number good cycles<br />
[2] Number supercycles<br />
[3] Cycle num within supercycle<br />
[4] Sweep number<br />
[5] Num cycles skipped<br />
[6] Num cycles histogrammed<br />
[7] DAC Increment number<br />
[8] DAC Set Value (volts)<br />
[9] DAC Readback (volts)<br />
[10] ADC1 Average<br />
[11] ADC2 Average<br />
[12] ADC3 Average<br />
[13] ADC4 Average<br />
[14] spare<br />
<br />
Names SUMS STRING 4 32 9h 0 RWD <br />
[0] SC Sum ch17<br />
[1] SC Sum ch18<br />
[2] SC Sum ch19<br />
[3] SC Sum ch20<br />
<br />
== Keys in the <span style="color: purple;">''/Equipment''</span> ODB tree ==<br />
<br />
=== <span style="color: purple;">''<equipment-name>''</span> subtree ===<br />
This subtree in the [[#top|/Equipment ODB tree]] The [[#top|/Equipment ODB tree]] <br />
will be named for an [[Frontend Operation#Equipments and Events|Equipment]] defined in the [[Frontend user code]]. No two subtrees in an experiment can have the same name. An <equipment-name> subtree will be present for every defined equipment in all the frontend(s) run on the experiment. <br />
<br />
<div id="initial values"></div><br />
This subtree will be created (if it does not already exist) the first time the defining frontend is run, and the '''initial values''' set are defined in the [[Equipment List Parameters]] in the [[Frontend user code]] that defines this particular equipment.<br />
<br />
'''Note:''' that if these parameters are later changed in the ODB, they will '''not''' be overwritten by the values in the frontend, if the frontend is restarted.<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
==== <span style="color: purple;">''Common''</span> subtree ====<br />
This subtree in an [[#<equipment-name> subtree|/Equipment/<equipment-name> subtree]] contains the parameters used by the system to process the [[Frontend Operation#Equipments and Events|Equipment]]. <br />
<br />
The initial values of the Common parameters are set when this tree is created (see [[#initial value|initial values]]).<br />
<br />
<br><br />
---------<br />
<br><br />
===== <span style="color: purple;">''Event ID''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' WORD<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the Event ID of this equipment. See '''[[Equipment List Parameters#EventID|EventID]]''' for details.<br />
<br />
<br><br />
---------<br />
<br><br />
===== <span style="color: purple;">''Trigger mask''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' WORD<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the Trigger mask of this equipment. See '''[[Equipment List Parameters#TriggerMask|TriggerMask]]''' for details.<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Buffer''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the Buffer parameter of this equipment. See <br />
'''[[Equipment List Parameters#Buffer|Buffer]]''' for details.<br />
For Events that are to be logged as banks, Buffer is set to "SYSTEM".<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Type''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' INT<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the Type of this equipment. See '''[[Equipment List Parameters#Type|Type]]''' for details.<br />
The Type is one of the defined '''Equipment Flags'''. <br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Source''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' INT<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the Interrupt or Event Source of this equipment. See '''[[Equipment List Parameters#Source|Source]]''' for details.<br />
<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Format''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the data format used for generating the event of this equipment. See '''[[Equipment List Parameters#Format|Format]]''' for details.<br />
<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Enabled''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' BOOL<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] indicated whether this equipment is active. See '''[[Equipment List Parameters#Enabled|Enabled]]''' for details.<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Read on''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' INT<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] specifies when the read-out of an event occurs or is enabled. It is set to one or a combination of [[ReadOn Flags]]. See '''[[Equipment List Parameters#ReadOn|Read on]]''' for details.<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Period''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' INT<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] specifies the time interval or time-out (units are milliseconds). See '''[[Equipment List Parameters#Period|Period]]''' for details.<br />
<br />
<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Event limit''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' DOUBLE<br />
* '''Default:''' <br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] specifies the time interval or time-out. See '''[[Equipment List Parameters#Event limit|Event limit]]''' for details.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Num subevents''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' DWORD<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] enables the super-event capability. See '''[[Equipment List Parameters#Number of subevents|Number of subevents]]''' for details.<br />
<br />
<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Log history''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' INT<br />
* '''Default:''' See [[#initial value|initial value]]<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] enables the [[History System]] for this equipment. See '''[[Equipment List Parameters#Log History|Log History]]''' for details.<br />
A value of 0 disables history logging. This parameter also controls how frequently the history events are generated.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Frontend host''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
* '''Default:''' <br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the name of the Frontend host running the [[Frontend user code]] that defines this particular equipment.<br />
It is filled by the system.<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Frontend name''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
* '''Default:''' <br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the [[Frontend user code#frontend Name|Frontend name]] defined in the [[Frontend user code]] that defines this particular equipment.<br />
It is filled by the system.<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Frontend file name''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
* '''Default:''' <br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the filename of the [[Frontend user code]] that defines this particular equipment.<br />
It is filled by the system.<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Status''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
* '''Default:''' <br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains the combination of [[#Frontend name|Frontend name]] and [[#Frontend host|Frontend host]] Frontend host that define this particular equipment. It is filled by the system and used by the [[Status Page#mhttpd status page]].<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Status color''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
* '''Default:''' "#00FF00"<br />
</div> <br />
<br />
<br />
This key in the [[#Common subtree|Common subtree]] contains a colour code to be used for the display of this equipment on the [[Status Page#mhttpd status page]].<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Hidden''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' BOOL<br />
* '''Default:''' "n"<br />
</div> <br />
<br />
This key in the [[#Common subtree|Common subtree]] if set to "y" will prevent the display of this equipment on the [[Status Page#mhttpd status page]].<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Write cache size''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' INT<br />
* '''Default:''' 10000000<br />
</div> <br />
<br />
This key in the [[#Common subtree|Common subtree]] controls the size of a cache (in bytes) that the midas frontend framework will use to buffer events before sending them to other clients (like the [[Logger]]). Optimising the size of the cache may improve the performance of your frontend, especially if you're sending large events. The minimum (and default) value is 10MB (10000000 bytes).<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
==== <span style="color: purple;">''Variables''</span> subtree ====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' DIR<br />
</div> <br />
<br />
This is a subtree in the [[#Top|/Equipment ODB Tree]].<br />
<br />
If either<br />
* the [[#Read on|Read on]] parameter contains the [[ReadOn Flags|RO_ODB flag]] or <br />
* [[#Log history|Log history]] is non-zero, i.e. History logging is enabled <br />
the event data will be copied to the ODB and listed in this subtree. <br />
<br />
In that case, this subtree will contain array(s) of event data named for the bankname(s) in the readout function (see [[Frontend user code#Event Readout routine]].<br />
See [[#Example Variables subtree|example]] above.<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
==== <span style="color: purple;">''Statistics''</span> subtree ====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' DIR<br />
</div> <br />
<br />
This subtree in the [[#Top|/Equipment ODB Tree]] contains statistics written by the system.<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
<br />
===== <span style="color: purple;">''Events sent''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' DOUBLE<br />
</div> <br />
This key in the [[#Statistics subtree|Statistics]] subtree contains the number of events of [[#Event ID|Event ID]] sent. It is filled by the system and used for display on the [[Status Page#mhttpd status page]]. See [[#Example Statistics subtree|example]] above. <br />
<br />
<br><br />
---------<br />
<br><br />
<br />
<br />
===== <span style="color: purple;">''Events per sec''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' DOUBLE<br />
</div> <br />
This key in the [[#Statistics subtree|Statistics]] subtree contains the number of events of [[#Event ID|Event ID]] sent per second. It is filled by the system and used for display on the [[Status Page#mhttpd status page]].<br />
<br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''kBytes per sec''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' DOUBLE<br />
</div> <br />
This key in the [[#Statistics subtree|Statistics]] subtree contains the number of kBytes per second for events of [[#Event ID|Event ID]] sent. It is filled by the system and used for display on the [[Status Page#mhttpd status page]].<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
==== <span style="color: purple;">''Settings''</span> subtree ====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' DIR<br />
</div> <br />
<br />
This subtree in the [[#Top|/Equipment ODB Tree]] contains user-defined settings, some of which may be used by the system.<br />
Any values defined in this subtree will appear in [[Event Notification (Hot-Link)#experim_dot_h|experim.h]] files.<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Names''</span> and <span style="color: purple;">''Names <variable>''</span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING (ARRAY)<br />
</div> <br />
<br />
These optional key(s) in the [[#Settings subtree|Settings]] subtree are used to define the names of variables in the [[#Variables subtree|Variables subtree]].<br />
<br />
Say you have an entry in Variables called "CURR" that contains 3 floats. You can then specify the names of these 3 elements here (i.e. if you're storing the current of 3 channels, you use Names to define the name of each channel).<br />
<br />
You can either create a key called "Names" that will apply to all Variables, or create separate keys for each Variable (e.g. "Names CURR").<br />
<br />
The Names array(s) (if present) will be used by the [[Equipment Page]], and by the [[History System]] to generate tags. See [[#Example Settings subtree|example]] above. <br />
<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Unit <variable>''</span></span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
</div> <br />
<br />
These optional key(s) in the [[#Settings subtree|Settings]] subtree are used to define the units of variables in the [[#Variables subtree|Variables subtree]].<br />
<br />
Say you have an entry in Variables called "CURR". You can specify that the current is measured in mA by creating a key called "Unit CURR" and setting it to "mA".<br />
<br />
These keys (if present) will be used by the [[Equipment Page]].<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Format <variable>''</span></span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
</div> <br />
<br />
These optional key(s) in the [[#Settings subtree|Settings]] subtree are used to define the formatting of variables in the [[#Variables subtree|Variables subtree]].<br />
<br />
A value of "%f2" for example shows a value with two digits after the period. A complete list of formats is listed on the [[Custom Page#Formatting|MIDAS custom page documentation]].<br />
<br />
These keys (if present) will be used by the [[Equipment Page]].<br />
<br />
<br><br />
---------<br />
<br><br />
<br />
===== <span style="color: purple;">''Editable''</span></span> =====<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* '''Type:''' STRING<br />
</div> <br />
<br />
This optional key in the [[#Settings subtree|Settings]] subtree is used to by the [[Equipment Page]] to decide whether to show an "editable" interface for each variable, or just show the present value.<br />
<br />
It should contain a comma-separated list of Variables that are allowed to be edited on the [[Equipment Page]].<br />
<br />
<br />
<br />
[[Category:ODB Tree]] [[Category:Equipment]] [[Category:History]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3408Custom plots with mplot2024-01-04T11:48:17Z<p>Stefan Ritt: Added data-alpha<n></p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Applies to !! Meaning<br />
|-<br />
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)<br />
|-<br />
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)<br />
|-<br />
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are used (relative to data-odb-path)<br />
|-<br />
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are used (relative to data-odb-path)<br />
|-<br />
| data-x<n> || scatter, histogram || ODB path to X data for the <n>-th graph if several graphs are used (relative to data-odb-path)<br />
|-<br />
| data-y<n> || scatter || ODB path to Y data for the <n>-th graph if several graphs are used (relative to data-odb-path)<br />
|-<br />
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)<br />
|-<br />
| data-h<n> || histogram || ODB path to Y data for the <n>-th histogram if several histograms are used (relative to data-odb-path)<br />
|-<br />
| data-label<n> || scatter, histogram || Label for <n>-th graph or histogram if several are used<br />
|-<br />
| data-alpha<n> || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.<br />
|-<br />
| data-nx || colormap || Number of X bins for a color map.<br />
|-<br />
| data-ny || colormap || Number of Y bins for a color map.<br />
|- <br />
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is "loop through all X indices for the lowest Y index before moving to next Y index", e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).<br />
|-<br />
| data-title || All plot types || Title of the graph shown on top<br />
|-<br />
| data-x-text || All plot types || Label shown below the X-axis<br />
|-<br />
| data-y-text || All plot types || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || scatter, histogram || Minimum of the X-axis<br />
|-<br />
| data-x-max || scatter, histogram || Maximum of the X-axis<br />
|-<br />
| data-x-log || scatter, histogram || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || scatter, histogram || Minimum of the Y-axis<br />
|-<br />
| data-y-max || scatter, histogram || Maximum of the Y-axis<br />
|-<br />
| data-y-log || scatter, histogram || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || colormap|| Maximum of the Z-axis<br />
|-<br />
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"getFromODB": true,<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the <code>plot_example.html</code> file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]<br />
<br />
= Setting parameters and data programmatically =<br />
<br />
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg; // obtain plot from ID<br />
p.param.title.text = "Different titel"; // change plot title<br />
p.param.plot[0].line.color = 2; // change line color of first plot to index 2<br />
</pre><br />
<br />
To change the data of the plot, use the function <code>setData(index, x, y)</code> for scatter plots or <code>setData(index, h)</code> for histograms like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg; // obtain plot from ID<br />
let x = Array.from({length: 20}, (_, i) => i); // create x array with 20 elements 0,1,2,...19<br />
let y = x.map(x => x*x); // create y array with y_i = x_i^2<br />
p.setData(0, x, y);<br />
</pre></div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3407Custom plots with mplot2024-01-04T10:15:50Z<p>Stefan Ritt: Replaced data-xy by data-z</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Applies to !! Meaning<br />
|-<br />
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)<br />
|-<br />
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)<br />
|-<br />
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are used (relative to data-odb-path)<br />
|-<br />
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are used (relative to data-odb-path)<br />
|-<br />
| data-x<n> || scatter, histogram || ODB path to X data for the <n>-th graph if several graphs are used (relative to data-odb-path)<br />
|-<br />
| data-y<n> || scatter || ODB path to Y data for the <n>-th graph if several graphs are used (relative to data-odb-path)<br />
|-<br />
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)<br />
|-<br />
| data-h<n> || histogram || ODB path to Y data for the <n>-th histogram if several histograms are used (relative to data-odb-path)<br />
|-<br />
| data-label<n> || scatter, histogram || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-nx || colormap || Number of X bins for a color map.<br />
|-<br />
| data-ny || colormap || Number of Y bins for a color map.<br />
|- <br />
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is "loop through all X indices for the lowest Y index before moving to next Y index", e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).<br />
|-<br />
| data-title || All plot types || Title of the graph shown on top<br />
|-<br />
| data-x-text || All plot types || Label shown below the X-axis<br />
|-<br />
| data-y-text || All plot types || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || scatter, histogram || Minimum of the X-axis<br />
|-<br />
| data-x-max || scatter, histogram || Maximum of the X-axis<br />
|-<br />
| data-x-log || scatter, histogram || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || scatter, histogram || Minimum of the Y-axis<br />
|-<br />
| data-y-max || scatter, histogram || Maximum of the Y-axis<br />
|-<br />
| data-y-log || scatter, histogram || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || colormap|| Maximum of the Z-axis<br />
|-<br />
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"getFromODB": true,<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the <code>plot_example.html</code> file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]<br />
<br />
= Setting parameters and data programmatically =<br />
<br />
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg; // obtain plot from ID<br />
p.param.title.text = "Different titel"; // change plot title<br />
p.param.plot[0].line.color = 2; // change line color of first plot to index 2<br />
</pre><br />
<br />
To change the data of the plot, use the function <code>setData(index, x, y)</code> for scatter plots or <code>setData(index, h)</code> for histograms like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg; // obtain plot from ID<br />
let x = Array.from({length: 20}, (_, i) => i); // create x array with 20 elements 0,1,2,...19<br />
let y = x.map(x => x*x); // create y array with y_i = x_i^2<br />
p.setData(0, x, y);<br />
</pre></div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Quickstart_Linux&diff=3404Quickstart Linux2024-01-03T10:18:39Z<p>Stefan Ritt: /* MIDAS Package Installation */</p>
<hr />
<div>{{Pagelinks}}<br />
== Introduction ==<br />
This quickstart shows you how to install the MIDAS packages on a linux box and create a '''MIDAS experiment'''. <br />
The linux box will be called the MIDAS '''''experiment host'''''.<br />
<br />
==Installation==<br />
Log on to the ''[[#Introduction|Experiment Host]]'' and decide on a name for the MIDAS experiment (the ''experiment name'').<br />
In these instructions, the MIDAS packages and experiment will be installed under the username "johnfoo". <br />
The ODB and other shared memory buffers for the MIDAS experiment will reside on the ''experiment host'' (localhost).<br />
MIDAS and the other required packages will be installed under directory {{Filepath|path=$HOME/packages}}. The MIDAS ''experiment directory'' will be {{Filepath|path=$HOME/online}} and the MIDAS ''experiment name'' will be "e777". The name of the ''experiment host'' will be "mhostpc". <br />
<br />
;NOTE<br />
: The user should substitute directory paths for the MIDAS packages and experiment directory as required, and names for ''experiment host'', username, ''experiment name'' appropriate to his/her own setup.<br />
<br />
== Experimental setup ==<br />
These instructions describe setting up an experiment that will run ONLY on the MIDAS ''experiment host'' computer (i.e. '''localhost only'''). In this case, the MIDAS ''experiment host'' has access to any hardware required, and all frontends, logger, analyzer etc. run on the one computer. <br />
<br />
Modifications to run an experiment with additional frontend(s) running '''remotely''' (i.e. on remote cpu(s)) will also be described.<br />
Follow the instructions for setting up a localhost-only experiment until indicated. <br />
<br />
In both cases, the main MIDAS applications (e.g. [[mlogger]], [[mhttpd]]) run on the MIDAS ''experiment host''.<br />
<br />
==Environment Variables==<br />
The following Environment variables should to be added to the {{File|name=.cshrc}} or {{File|name=.bashrc}} file (depending on your shell) in the {{Filepath|path=$HOME}} directory on the ''experiment host'', so that they will be defined at login. '''Substitute appropriate values for your own setup'''. <br />
<small><br />
{| class="wikitable"<br />
|-<br />
! csh !! bash !! comment<br />
|-<br />
| setenv MIDASSYS $HOME/packages/midas || export MIDASSYS=$HOME/packages/midas ||Base directory of the MIDAS package<br />
|-<br />
| setenv MIDAS_EXPTAB $HOME/online/exptab || export MIDAS_EXPTAB=$HOME/online/exptab || MIDAS experiment table <br />
|-<br />
| setenv MIDAS_EXPT_NAME e777 || export MIDAS_EXPT_NAME=e777 || MIDAS experiment name<br />
|-<br />
| setenv PATH .:$MIDASSYS/bin:$PATH || export PATH=$PATH:$MIDASSYS/bin || path<br />
|}<br />
</small><br />
Logout and login again, or source {{File|name=.cshrc}} (source {{File|name=.bashrc}}) for the changes to take effect.<br />
;NOTE<br />
: See [[MIDAS environment variables]] for a list of environment variables used by MIDAS.<br />
<br />
== ROOT Package Installation ==<br />
MIDAS has optional integration with [https://root.cern.ch ROOT]. ROOT is not required for acquiring data or storing it in midas files, but is used by some of the analysis tools (including the example analyzer that will be built later in this Quickstart). It is also possible to write midas data directly to ROOT files if desired.<br />
<br />
If you require ROOT integration, follow the installation instructions on the [https://root.cern.ch ROOT website] and set the ROOTSYS environment variable.<br />
<br />
If you do not require ROOT integration, leave the ROOTSYS variable undefined.<br />
<br />
== Adding SSL support ==<br />
<br />
We recommend running mhttpd (the MIDAS web server) behind an industry-grade web proxy like apache httpd or nginx. httpd/nginx will then handle the SSL security. It is however also possible to build mhttpd with built-in SSL support. See [[Mhttpd|the mhttpd documentation]] for more details about web security.<br />
<br />
If you wish to enable SSL support in mhttpd itself:<br />
<br />
<small><br />
[mhostpc] cd $MIDASSYS<br />
[mhostpc] make mbedtls<br />
</small><br />
<br />
== MIDAS Package Installation ==<br />
The MIDAS package will be installed on the ''experiment host'' in the directory given by MIDASSYS - see [[#Environment Variables]].<br />
<br />
Install the MIDAS and other package(s) from the MIDAS git repository in an xterm as follows:<br />
<small><br />
[mhostpc] mkdir $HOME/packages<br />
[mhostpc] cd $HOME/packages<br />
[mhostpc] git clone https://bitbucket.org/tmidas/midas --recurse-submodules<br />
</small><br />
<br />
If you already cloned MIDAS and forgot the "--recurse-submodules", you can do a "git submodule update --init --recursive" later. If you want to pull MIDAS later to update to the current version, you need either "git pull --recurse-submodules". To tell git to always recurse, you can set a global flag via "git config submodule.recurse true".<br />
<br />
The MIDAS Makefile will auto-detect whether MSCB, ROOT, MYSQL, SQLITE, ODBC etc. are available (or have been installed). If you need these features, make sure they are available before building the MIDAS package.<br />
<small><br />
[mhostpc] cd $MIDASSYS<br />
[mhostpc] mkdir build<br />
[mhostpc] cd build<br />
[mhostpc] cmake .. ### configure build process (see note below about cmake/cmake3)<br />
[mhostpc] make install ### build and install midas to bin/ and lib/ directories<br />
[mhostpc] cd ..<br />
[mhostpc] ls -l bin/odbedit ### check that odbedit has been created (do not run it yet)<br />
</small> <br />
<br />
MIDAS requires cmake version 3.10 or later. On some systems, you have to call "cmake3 ..", on others just "cmake ..". Some systems might require you to install cmake first (see the [https://cmake.org/download/ cmake website; installation is very simple).<br />
<br />
In case of problems see <br />
* [[Installation#Installing MIDAS|bitbucket is down]] or <br />
* [[Installation/Compilation problems#SSL certificate errors|SSL certificate errors]].<br />
<br />
==ROOTANA Package Installation==<br />
<br />
rootana is an optional framework for analyzing midas data in realtime and displaying plots (either by a graphical interface or via webpages). Most of the features are now available in manalyzer, which is included in the base midas install.<br />
<br />
If you require the legacy ROOTANA package, you can install it with:<br />
<small><br />
[mhostpc] cd $HOME/packages<br />
[mhostpc] git clone https://bitbucket.org/tmidas/rootana<br />
[mhostpc] cd rootana<br />
[mhostpc] make<br />
</small><br />
<br />
add ROOTANASYS=$HOME/packages/rootana to the login file.<br />
<br />
Install the JSROOT package:<br />
<pre><br />
cd $HOME/packages<br />
git clone https://github.com/linev/jsroot.git<br />
</pre><br />
<br />
add JSROOTSYS=$HOME/packages/jsroot to the login file.<br />
<br />
==ROODY Package Installation==<br />
<br />
ROODY is a legacy package for displaying live ROOT histograms. It is generally not used by new experiments.<br />
<br />
If ROODY is needed,<br />
<small><br />
[mhostpc] cd $HOME/packages<br />
[mhostpc] git clone https://bitbucket.org/tmidas/roody<br />
[mhostpc] cd roody<br />
[mhostpc] make<br />
</small><br />
<br />
== Create the Experiment file exptab ==<br />
In this example, the experimental directory is {{Filepath|path=$HOME/online}}. Create this directory on the ''experiment host'' (localhost):<br />
<small><br />
[mhostpc] mkdir $HOME/online<br />
[mhostpc] cd $HOME/online</small><br />
Then create the [[exptab]] file {{Filepath|path=$HOME/online/exptab}} containing the three parameters:<br />
# the experiment name <br />
# location of MIDAS shared memory buffers<br />
# username<br />
as follows (using parameters for your own experiment):<br />
<small><br />
[mhostpc] cat > exptab<br />
e777 /home/johnfoo/online johnfoo</small><br />
<br />
The path of the {{File|name=exptab}} file is given by MIDAS_EXPTAB - see [[#Environment Variables]].<br />
<br />
== Create shared memory files ==<br />
At this point you should be able to run [[odbedit]] on the ''experiment host'' (localhost). The first time {{Utility|name=odbedit}} is run, it will create the required {{File|name=.*.SHM}} files in the MIDAS experiment directory {{Filepath|path=$HOME/online}}. These are the saved files for the MIDAS shared memory.<br />
<br />
The default directory {{Filepath|path=$HOME/online}} will contain the MIDAS messages file ({{File|name=midas.log}}) and<br />
any data files you may create. If you want an alternative location for the data files see [[#Customizing your experiment]].<br />
<br />
Run {{Utility|name=odbedit}}, and type {{Odbedit cmd|cmd=ls}} to list the default directories. <br />
<small>[mhostpc] odbedit<br />
[local:e777:S]/>ls<br />
Experiment<br />
System<br />
Programs<br />
Logger<br />
Runinfo<br />
Alarms</small> <br />
<br />
<br />
== Run the MIDAS Web Server ==<br />
<br />
=== Using apache ===<br />
<br />
Recommended for production systems with maximum security is to run mhttpd behind an industry-standard SSL/https proxy with password protection. We document instructions for apache here, but you could nginx instead.<br />
<br />
To do this using apache httpd on CentOS-7, do this:<br />
* configure apache httpd per instructions for [https://daq00.triumf.ca/DaqWiki/index.php/Ubuntu#Install_apache_httpd_proxy_for_midas_and_elog Ubuntu] or [https://daq00.triumf.ca/DaqWiki/index.php/SLinstall#Configure_HTTPS_server_.28CentOS7.29 CentOS 7]<br />
* run mhttpd<br />
* use web browser to connect to https://mhostpc/<br />
* enter the user name and password configured in apache httpd<br />
* the midas status page should open<br />
<br />
=== Standalone (less secure) ===<br />
<br />
For simplified installation, one can use standalone mhttpd with built-in https and password protection. This configuration<br />
is only as secure as the underlying mongoose and OpenSSL web server and encryption packages. Use the odbedit tool to edit settings in the [[Webserver_ODB_tree|Webserver ODB tree]]:<br />
<br />
* Set "Enable https port" to "y"<br />
* Set "https port passwords" to "y"<br />
<br />
Many current operating systems implement a built-in firewall which will block outside access to mhttpd. See instructions for [https://daq00.triumf.ca/DaqWiki/index.php/SLinstall#Enable_firewall_for_MIDAS_.28CentOS7.29 CentOS 7].<br />
<br />
Start [[mhttpd]] on the ''experiment host'' (localhost) like this:<br />
<small>[mhostpc] mhttpd</small><br />
<br />
You will get the following messages: <br />
<small><br />
[mhttpd,ERROR] [mhttpd.cxx:13880:mongoose,ERROR] mongoose web server cannot find password file "/home/johnfoo/online/htpasswd.txt"<br />
[mhttpd,ERROR] [mhttpd.cxx:13881:mongoose,ERROR] please create password file: touch /home/johnfoo/online/htpasswd.txt<br />
[mhttpd,ERROR] [mhttpd.cxx:13818:mongoose,ERROR] mongoose web server password file "/home/johnfoo/online/htpasswd.txt" has no passwords for realm "exptname"<br />
[mhttpd,ERROR] [mhttpd.cxx:13819:mongoose,ERROR] please add passwords by running: htdigest /home/johnfoo/online/htpasswd.txt exptname midas<br />
mongoose_init: Error: Cannot initialize authorization object!<br />
Error: Could not start the mongoose web server, see messages and midas.log, bye!<br />
</small><br />
<br />
Create the password file by following the instructions printed by mhttpd. The http digest domain name is the experiment name, suggested default user name is "midas". You will be asked to type in a password. (htdigest is part of the httpd-tools package)<br />
<br />
<small><br />
[mhostpc] htdigest -c /home/johnfoo/online/htpasswd.txt exptname midas<br />
Adding password for midas in realm exptname.<br />
New password:<br />
Re-type new password:<br />
</small><br />
It is a good idea to set the password file {{Filepath|path=htpasswd.txt}} readable and writable by owner only.<br />
<br />
<br />
Run {{Utility|name=mhttpd}} again.<br />
<br />
If you get error messages like <small><code>https port "[::]:8443" requested, but mhttpd compiled without MG_ENABLE_SSL</code></small> then you need to re-compile MIDAS after [[Mhttpd#Build_mhttpd_with_SSL_support|adding SSL support]].<br />
<br />
Otherwise, you will get the following messages: <br />
<small><br />
HTTP Digest authentication with realm "exptname" and password file "/home/johnfoo/online/htpasswd.txt"<br />
Hostlist off, connections from anywhere will be accepted<br />
Listening on "http://localhost:8080", passwords OFF, hostlist OFF<br />
Listening on "http://[::1]:8080", passwords OFF, hostlist OFF<br />
[mhttpd,ERROR] [mhttpd.cxx:15739:mongoose_listen,ERROR] cannot find SSL certificate file "/home/johnfoo/online/ssl_cert.pem"<br />
[mhttpd,ERROR] [mhttpd.cxx:15740:mongoose_listen,ERROR] please create SSL certificate file using openssl: cd $MIDASSYS; openssl req -new -nodes -newkey rsa:2048 -sha256 -out ssl_cert.csr -keyout ssl_cert.key -subj "/C=/ST=/L=/O=midas/OU=mhttpd/CN=localhost"; openssl x509 -req -days 365 -sha256 -in ssl_cert.csr -signkey ssl_cert.key -out ssl_cert.pem; cat ssl_cert.key >> ssl_cert.pem<br />
[mhttpd,ERROR] [mhttpd.cxx:15741:mongoose_listen,ERROR] or using certbot (recommened): setup certbot per Let's Encrypt instructions, certificates are typically saved in /etc/letsencrypt/live/$HOSTNAME/, copy fullchain.pem and privkey.pem to $MIDASSYS; cd $MIDASSYS; cat fullchain.pem privkey.pem > ssl_cert.pem<br />
[mhttpd,ERROR] [mhttpd.cxx:15918:mongoose_init,ERROR] Failed to listen on a TCP port enabled in ODB /WebServer<br />
Error: Could not start the mongoose web server, see messages and midas.log, bye!<br />
</small><br />
<br />
Create a self-signed certificate suitable for initial testing by executing the commands printed by mhttpd.<br />
<br />
For production use, you should create a properly signed certificate, see [[Mhttpd#Create an SSL certificate|create your own SSL certificate]] or you should run mhttpd behind an SSL/https proxy (apache httpd).<br />
<br />
<br />
Now restart {{Utility|name=mhttpd}}<br />
<small>[mhostpc] mhttpd<br />
HTTP Digest authentication with realm "test_pol" and password file "/home/johnfoo/online/htpasswd.txt"<br />
Hostlist off, connections from anywhere will be accepted<br />
Listening on "http://localhost:8080", passwords OFF, hostlist OFF<br />
Listening on "http://[::1]:8080", passwords OFF, hostlist OFF<br />
Mongoose web server will use https certificate file "./ssl_cert.pem"<br />
Listening on "https://[::]:8443", passwords enabled, hostlist OFF<br />
Mongoose web server is using 1 threads<br />
Mongoose web server is using 2 threads<br />
Mongoose web server is using 3 threads<br />
Mongoose web server is using 4 threads</small><br />
<br />
Now point a web browser running on the same host computer (localhost) to https://localhost:8443. <br />
If the web browser is running on a different computer, go to URL of the form<br />
<small><br />
https://mhostpc.triumf.ca:8443 (substitute your host machine name and domain for "mhostpc.triumf.ca")<br />
</small><br />
If you are using the default SSL certificate you will probably get a message: "This Connection is Untrusted". Click "I understand the risks" and add an exception. This is because the test certificate is self-signed. Then confirm an exception. <br />
<br />
You should then see an authentication box asking you for the user name and password. The user name is "midas". Enter the password you just created. The Midas [[Status Page]] should appear with multiple buttons for run control as well as equipment listing (no equipments will be listed as yet) and application listings. Please refer to [[mhttpd]] (the MIDAS Web-based Run Control utility) for further information. You can start and stop runs from the main status page, and use the [[ODB Page]] to access the database (ODB).<br />
<br />
; Note <br />
: Default ports of 8080 and 8443 are used by [[mhttpd]]. To use some other ports, change the relevant values in the [[Webserver_ODB_tree|Webserver ODB tree]].<br />
<br />
== Run the MIDAS logger ==<br />
Start the MIDAS logger [[mlogger]] on the ''experiment host'' (localhost) :<br />
<small>[mhostpc] mlogger</small> <br />
This should start without error. It is usually run as a daemon, however in this case it is run in a terminal to check for errors. Starting the midas logger [[mlogger]] will automatically create more keys in the {{Odbpath|path=/Logger}} ODB tree.<br />
<br />
From the MIDAS status page, go to the "Programs" page, then the "Logger" page and set ODB variables:<br />
* Required -> set to "y"<br />
* Start command -> set to "mlogger -D"<br />
* Alarm Class -> set to "Alarm"<br />
<br />
To test this, go to the "Programs" page, press "stop logger", then "start logger".<br />
<br />
Start a run, wait 10 seconds, stop the run.<br />
<br />
From the MIDAS "Status" page, go to the "logging channels -> channel #0" and set ODB variables:<br />
* Output -> "FILE"<br />
<br />
Start a run, go to the "Status" page, observe that the output file is written with lz4 compression and has the "mid.lz4" extension.<br />
<br />
Stop the run.<br />
<br />
== Run test frontend ==<br />
<br />
Run <code>fetest</code><br />
<br />
On the MIDAS "Status" page there will be 5 new equipment entries:<br />
<br />
{| class="wikitable"<br />
|+ Equipment created by the fetest program<br />
|-<br />
! Equipment name !! Data it sends !! How often data is sent<br />
|-<br />
| test_random || Random data banks || 1Hz<br />
|-<br />
| test_slow || Example slow control data for testing the midas history plots || 1Hz, even when a run isn't in progress<br />
|-<br />
| test_rare || Data that look like a sine wave || 0.1Hz, even when a run isn't in progress<br />
|-<br />
| test_bulk || Large events || 1Hz<br />
|-<br />
| test_rpc || None. It shows an example of a "remote procedure call" (RPC) handler. Javascript/C++/Python code can connect to the program, and it will respond with the current time (in this toy example) || never<br />
|}<br />
<br />
Note the event counters for test_slow and test_rare are already incrementing.<br />
<br />
Start a run.<br />
<br />
Observe that the event counters for test_bulk and test_random are also incrementing now, and that the output data file size is growing.<br />
<br />
Click on the "Event dump" page and then the "Run" button. You'll see the raw data that is being written to disk. (The data from most of the equipment is double-precision floating point numbers; the data from the "bulk" equipment is just a bunch of zeros).<br />
<br />
Return the status page and stop the run.<br />
<br />
== Create a history plot ==<br />
<br />
Go to the MIDAS "History" page.<br />
<br />
Say "new", panel name "test". In the history editor select event "test_slow", and click the "save" button (you can ignore the rest of the options for now). If the "test_slow" event isn't listed, make sure that the mlogger program is running, as described above.<br />
<br />
History plot "test" should open with a sine-wave plot. Data on the plot should update as long as "fetest" is running (there is no need to start a run).<br />
<br />
== Clients run on Localhost only ==<br />
;NOTE: <br />
* If creating a MIDAS experiment with a '''REMOTE''' frontend, '''continue by following the instructions''' [[#Running with one or more REMOTE frontends]].<br />
* If all clients are running on the Experiment Host (i.e. '''localhost'''), continue with the following instructions:<br />
<br />
=== Frontend and Analyzer (localhost) ===<br />
<br />
THIS SECTION IS VERY OBSOLETE<br />
<br />
There are several examples of [[Frontend user code]] ({{File|name=frontend.c}}) in the MIDAS package available under {{Filepath|path=$MIDASSYS/examples/}}. Choose a suitable example from this directory that you can later modify for your own particular setup (e.g. for a slow control, choose the {{Filepath|path=../slowcont/}} example). <br />
<br />
A '''frontend''' is a program that usually reads data from the hardware and sends it to a buffer to be logged and analyzed. You can find documentation about the frontend structure under [[Frontend Application]], [[Frontend Operation]] and [[Frontend user code]].<br />
<br />
The example chosen here is from {{Filepath|path=$MIDASSYS/examples/experiment/}}. It does '''not need any hardware''' and produces simulated events. <br />
<br />
On the MIDAS ''experiment host'' (localhost), copy the example to the experiment directory and build it.<br />
<small><br />
[mhostpc] cd $HOME/online<br />
[mhostpc] cp $MIDASSYS/examples/experiment/* .<br />
[mhostpc] make</small><br />
The analyzer will only build if ROOT has previously been installed.<br />
At this point the frontend and the analyzer should be ready if no errors were generated during the build. Try the frontend and analyzer by starting them in xterms. <br />
<small><br />
[mhostpc] frontend<br />
[mhostpc] analyzer</small><br />
<br />
=== Equipment ODB tree ===<br />
Starting the frontend will automatically create the {{Odbpath|path=/Equipment}} ODB tree and one or more equipments. In this case, the equipments "trigger" and "scaler" have been defined by code in {{File|name=frontend.c}} and created the first time frontend runs.<br />
<small><br />
[mhostpc] odbedit<br />
[local:e777:S]/>ls /equipment<br />
Trigger <br />
Scaler </small><br />
<br />
View the MIDAS main status web page (by pointing your browser to https://localhost:8443/ or appropriate port if not using the default).<br />
Observe on the [[Status Page]] that the two Equipments (Trigger, Scaler) have appeared in the Equipment section (Figure 1) and are coloured green.<br />
<br><br />
'''Click thumbnail to enlarge'''<br />
[[File:quickstart_status_page.png|thumb|left|Figure 1: web server status page showing example frontend equipments]]<br />
<div style="clear: both"></div> <!-- clear wraparound after thumbnail --><br />
<br />
Rather than using odbedit, you can view the contents of these equipments by clicking on the ODB button, then on {{Odbpath|path=/Equipment}}, then {{Odbpath|path=Trigger}} or {{Odbpath|path=Scaler}}. See [[ODB Page]] for more information.<br />
<br />
=== Start a run ===<br />
Start a run by pressing the Start button on the [[Status Page]].<br />
The Equipment display on the Status Page will now show some event statistics (Figure 2)<br />
'''Click thumbnail to enlarge'''<br />
[[File:sample_frontend_status.png|thumb|left|Figure 2: Equipments shown on Status Page showing event statistics]]<br />
<div style="clear: both"></div> <!-- clear wraparound after thumbnail --><br />
<br />
<br />
<br />
The frontend will show an event display that will update when a run is started, e.g.<br />
<br />
Sample Frontend connected to <local>. Press "!" to exit 18:48:2546:08<br />
==========================================================================<br />
Run status: Running Run number 2 |/ /<br />
===========================================================================<br />
Equipment Status Events Events/sec Rate[B/s] ODB->FE FE->OD<br />
---------------------------------------------------------------------------<br />
Trigger OK 13948 99.0 5413.0 0 140 <br />
Scaler OK 15 0.3 15.4 0 15 <br />
<br />
The [[Status Page]] will also show events being generated (click the refresh button if necessary). <br />
<br />
=== Event Dump ===<br />
While a run is in progress, the midas application [[mdump]] will provide you an event dump of the collected data from the running frontend, e.g.<br />
<small><br />
mhostpc> mdump -l 50<br />
<span style="color:whitesmoke">blank</span><br />
- MIDAS revision: Mon Nov 2 11:50:51 2015 -0800 - 3b66779 -- Enter <!> to Exit ------- Midas Dump ---<br />
------------------------ Event# 1 ------------------------<br />
Evid:0001- Mask:0000- Serial:0- Time:0x567a0c5d- Dsize:40/0x28<br />
#banks:2 - Bank list:-ADC0TDC0-<br />
<span style="color:whitesmoke">blank</span><br />
Bank:ADC0 Length: 8(I*1)/2(I*4)/4(Type) Type:Unsigned Integer*2<br />
1-> 0x0167 0x03c6 0x0069 0x0073 <br />
<span style="color:whitesmoke">blank</span><br />
Bank:TDC0 Length: 8(I*1)/2(I*4)/4(Type) Type:Unsigned Integer*2<br />
1-> 0x0051 0x00ff 0x004a 0x00ec <br />
</small><br />
For further data processing/analysis, either the midas analyzer or the rootana can used for data display as well. <br />
<br />
=== Create a startup script (localhost) ===<br />
It is useful to create a script that will automatically start all the required clients for the experiment. This is called a startup script. Here, the startup script will be {{File|name=start_daq.sh}} (any name can be used). Create {{Filepath|path=$HOME/online/bin/start_daq.sh}} on the ''experiment host'' as follows (supply the [[mhttpd]] ports if default is not used):<br />
<br />
<small>#!/bin/sh<br />
# start_daq.sh<br />
cd $HOME/online<br />
odbedit -c clean<br />
# start mhttpd on default port. (Mongoose https version)<br />
mhttpd -D # you can optionally restrict access to localhost and other specified hosts - see [[mhttpd]]<br />
xterm -e ./frontend &<br />
xterm -e ./analyzer &<br />
mlogger -D<br />
#end file</small><br />
<br />
Before running this script, you will need to shutdown any running clients:<br />
<small>mhostpc> odbedit -c "sh all"</small><br />
or you can use the {{Utility|name=mhttpd}} [[Programs Page]] to shut them down.<br />
<br />
The script will start them as daemons. <br />
By running the startup script start_daq.sh, several MIDAS applications will be started in sequence:<br />
# Cleanup previous MIDAS application (if any).<br />
# Start the MIDAS web server [mhttpd]<br />
# Start the frontend application in its own xterm (for debugging purpose).<br />
# Start the analyzer application in its own xterm (for debugging purpose).<br />
# Start the MIDAS Data logger [mlogger]<br />
<small>[mhostpc] sh ./start_daq</small><br />
<br />
You may wish to modify the script to restart missing clients only. <br />
<br />
Next [[#Customizing your experiment|customize your experiment]].<br />
<br />
== Running with one or more REMOTE frontends ==<br />
These instructions assume you have already followed the instructions to setup an experiment running on the MIDAS experiment host (localhost), i.e. you have setup the MIDAS Environment variables, downloaded MIDAS packages, setup the [[mhttpd]] password and have {{Utility|name=mhttpd}} running successfully.<br />
<br />
In the case of '''remote''' frontend(s), the remote cpu(s) typically have access to some or all of the hardware. They might be VMIC cpus running in VME crates for example, connected to the MIDAS Host via Ethernet. They read data from the hardware and send it back to the MIDAS ''experiment host'' to be logged and analyzed.<br />
<br />
=== Experimental setup (Remote) ===<br />
These instructions assume that the MIDAS ''experiment host'' computer is 64-bit (if not see [[#Build 32-bit MIDAS libraries|note below]]), and there is one ''remote host'', a 32-bit machine named "rlxhost". <br />
<br />
The ''remote host'' mounts the {{Filepath|path=/home}} disk of the ''experiment host'', so that it '''has access to the MIDAS packages''', the {{Filepath|path=$HOME/online}} directory and shares the .cshrc (or .bashrc) script. It is convenient to set up ssh keys (use ssh-keygen) so that the ''remote host'' can be accessed without supplying the password from the ''experiment host''.<br />
<br />
If your setup is different, you will have to make changes as appropriate. <br />
<br />
=== Build 32-bit MIDAS libraries ===<br />
NOTE<br />
<p style="margin-left:40px;margin-right:40px;"><br />
If the MIDAS ''experiment host'' computer is 32-bit (i.e. the same as the ''remote host''), which is a simpler setup, you will have built the 32-bit MIDAS libraries already, and can skip the rest of this section. Note however, that in the .cshrc file in the [[#Modify .cshrc (.bashrc) for Remote host|following section]] you will not need the lines starting "# select 64-bit or 32-bit MIDAS and ROOT". The PATH should contain <span style="color:#214200; font-weight:normal; font-style:italic">$MIDASSYS/bin</span> on both experiment host and remote host. This will point to the 32-bit MIDAS libraries. <br />
------------------------------<br />
</p><br />
<br><br />
Assuming the MIDAS host computer is 64-bit, you will have already built the 64-bit MIDAS libraries.<br />
In this case, if the remote frontend is 32-bit, you will need to build the 32-bit MIDAS libraries on the 64-bit machine, i.e.<br />
<small><br />
[mhostpc] cd /home/packages/midas<br />
[mhostpc] make linux32 ### build the 32-bit MIDAS libraries on 64-bit machine<br />
[mhostpc] ls -l linux-m32/bin/odbedit ### check that the 32-bit odbedit has been created </small><br />
Do not run {{Utility|name=odbedit}} yet on the ''remote host''!<br />
<br />
If the "make linux32" command fails, you may need to install 32bit support for your compiler (e.g. "apt install g++-multilib").<br />
<br />
=== Modify .cshrc (.bashrc) for Remote host ===<br />
Add the following to the {{File|name=.cshrc}} (modified appropriately if using {{File|name=.bashrc}}). When running with a REMOTE Host, the application [[mserver]] will be started (using a particular port) on the MIDAS ''experiment host'' machine only. Note that the environment variable MIDAS_SERVER_HOST is defined on the remote machine(s) but NOT on the ''experiment host''. <br />
<br />
If ROOT has been installed properly and the script <ROOT installation directory>/bin/thisroot.sh is called from .cshrc or .bashrc, the ROOTSYS environment variable should be defined and the ROOT bin directory should be in the PATH environment variable. Make sure that this is the case.<br />
<br />
<small><br />
# setenv MIDAS_HOST mhostpc # substitute your experiment host name<br />
# setenv REMOTE_HOST rlxhost # substitute your remote host name<br />
# setup the MIDAS mserver host<br />
#<br />
switch (`hostname`)<br />
case $MIDAS_HOST*: <br />
unsetenv MIDAS_SERVER_HOST ## MIDAS_SERVER_HOST not defined<br />
breaksw<br />
default:<br />
setenv MIDAS_SERVER_HOST $MIDAS_HOST.triumf.ca:1175 ## for remote host, define MIDAS_SERVER_HOST, use your domain here<br />
## as hostname + domain name + default mserver port<br />
## port must match that of [[mserver]] used in start_daq.sh <br />
endsw<br />
#<br />
# select 64-bit or 32-bit MIDAS<br />
#<br />
switch (`uname -i`)<br />
case i386:<br />
setenv PATH .:$MIDASSYS/linux-m32/bin:$PATH<br />
breaksw<br />
default:<br />
setenv PATH .:$MIDASSYS/linux/bin:$PATH<br />
endsw<br />
#<br />
setenv PATH .:$HOME/online/bin:$HOME/packages/roody/bin:$PATH<br />
#<br />
</small><br />
After executing {{File|name=.cshrc}} (or {{File|name=.bashrc}}) or logging out, on the MIDAS host (mhostpc) make sure environment variable MIDAS_SERVER_HOST is NOT defined<br />
<small>[mhostpc] echo $MIDAS_SERVER_HOST<br />
[mhostpc] MIDAS_SERVER_HOST: Undefined variable.</small><br />
<br />
and on the '''''remote host''''', make sure MIDAS_SERVER_HOST IS defined: <br />
<span style="color:green; font-size:80%">[rlxhost] echo $MIDAS_SERVER_HOST<br />
[rlxhost] MIDAS_SERVER_HOST</span><br />
<br />
Also on the ''remote host'', make sure that the correct (32-bit) {{Utility|name=odbedit}} will be used (do not run it yet!)<br />
<span style="color:green; font-size:80%">[rlxhost] which odbedit<br />
/home/johnfoo/packages/midas/linux-m32/bin/odbedit</span><br />
<br />
=== Grant Remote host access permission === <br />
Give permission for the remote host(s) to access the experiment by following the instructions to [[Security#MIDAS programs on remote machines|allow MIDAS programs on remote machines]]. If the MIDAS web server [[mhttpd]] is running, use [[ODB Page]] to edit the ODB keys listed, otherwise use [[odbedit]] on the ''experiment host'' machine.<br />
<br />
=== Start mserver on Experiment Host ===<br />
The application [[mserver]] is not required when running an experiment on the ''experiment host'' (localhost) only, but it '''IS required''' for access from a '''REMOTE''' frontend.<br />
Start [[mserver]] on MIDAS host (default port 1175) or use the "-p" option for a different port.<br />
The port must match the port defined for environment variable MIDAS_SERVER_HOST in {{File|name=.cshrc}} (or {{File|name=.bashrc}}) [[#Modify .cshrc (.bashrc) for Remote host|above]].<br />
<small>[mhostpc] mserver -D</small><br />
<br />
The environment variables on the REMOTE host should be:<br />
<span style="color:green; font-size:80%"><br />
[rlxhost] printenv | grep MIDAS<br />
MIDASSYS=/home/e777/packages/midas<br />
MIDAS_EXPT_NAME=e777<br />
MIDAS_SERVER_HOST=mhostpc.triumf.ca:1175</span><br />
(a different port may be used - see above)<br />
<br />
=== Check access to ODB from Remote host ===<br />
Run {{Utility|name=odbedit}} on the remote host to check access to ODB. <br />
<span style="color:green; font-size:80%">[rlxhost] odbedit<br />
[mhostpc.triumf.ca:1175:e777:Stopped]/>ls </span><br />
If it does not work correctly, check that <br />
* MIDAS_SERVER_HOST is defined on REMOTE host with same port as [[mserver]]<br />
* <span style="color:darkcyan;font-style:italic">mserver</span> is running on MIDAS host with the correct port<br />
* MIDAS_SERVER_HOST is NOT defined on MIDAS host<br />
* remote access permission to REMOTE host has been granted as described above.<br />
<br />
=== Build the analyzer on Experiment Host ===<br />
The analyzer will be built and run on the MIDAS ''experiment host'' machine.<br />
Select an example from $MIDASSYS/examples/ (see [[#Frontend and Analyzer (localhost)]]) and copy it to $HOME/online on the MIDAS host machine. Since we are assuming a 64-bit MIDAS host and a 32-bit remote host, edit the example Makefile so only the analyzer will be built. <br />
<br />
=== Build the frontend (32bit) for Remote host ===<br />
The frontend runs on a 32-bit remote host in this example. It therefore must either<br />
* be built 32-bit on the 64-bit MIDAS host with the "-m32" flag <br />
* or built on the REMOTE host<br />
and in either case the [[#Build 32-bit MIDAS libraries|32-bit MIDAS libraries]] used for linking. <br />
<br />
To avoid confusion, create a different subdirectory for the frontend, e.g.<br />
<span style="color:green; font-size:80%">[rlxhost] mkdir $HOME/online/fe_32</span><br />
Copy the Makefile and frontend code from the same example to this directory.<br />
Modify the Makefile as needed, and only build the frontend. <br />
<br />
Once built, start the frontend in an xterm on the REMOTE host. <br />
<span style="color:green; font-size:80%">[rlxhost] $HOME/online/fe_32/frontend</span><br />
Check that it connects successfully to the experiment and the Equipments appear on the [[Status Page]] as described under [[#Frontend and Analyzer (localhost)]]. The Equipment Status (Figure 2) should show that the Equipments are now running on the remote cpu, i.e. "SampleFrontend@rlxhost.triumf.ca" rather than "Sample Frontend@localhost".<br />
<br />
=== Run the Analyzer on Experiment host ===<br />
Start the analyzer in an xterm on the MIDAS ''experiment host'' <br />
<small>[johnfoo@mhostpc e777] $HOME/online/analyzer</small><br />
<br />
Continue by starting a run and dumping the data as described under [[#Frontend and Analyzer (localhost)]].<br />
<br />
=== Create experiment startup script ===<br />
It is useful to create a script that will automatically start all the required clients for the experiment. This is called a startup script (any name can be used). Create {{Filepath|path=$HOME/online/bin/start_daq.sh}} on the ''experiment host'' as follows, (supply the [[mhttpd]] ports if default is not used) :<br />
<small><br />
#!/bin/sh<br />
# start_daq.sh<br />
cd $HOME/online<br />
#<br />
switch (`hostname`)<br />
case $MIDAS_HOST*: ## MIDAS_HOST is assigned in .cshrc (.bashrc)<br />
echo "Good, we are on $MIDAS_HOST"<br />
breaksw<br />
case $REMOTE_HOST*: ## REMOTE_HOST is assigned in .cshrc (.bashrc)<br />
echo "startup script should be executed on $MIDAS_HOST"<br />
endsw<br />
#<br />
odbedit -c clean<br />
# start mhttpd on default port (Mongoose https version)<br />
mhttpd -D # you can optionally restrict access to localhost and/or other specified hosts - see [[mhttpd]]<br />
# start mserver on default port (use argument -p to use a different port) <br />
mserver -D # start mserver on default port (1175) or a different port using -p option - see [[mserver]]<br />
# the port must match that of MIDAS_SERVER_HOST in .cshrc (.bashrc) - see above<br />
# access must be specifically allowed - see [[#Grant Remote host access permission||above]]<br />
xterm -e ./analyzer &<br />
## start frontend on remote host<br />
#ssh $REMOTE_HOST $HOME/online/bin/start_frontend -O >& $HOME/online/bin/start_frontend.log &<br />
mlogger -D<br />
#end file<br />
</small><br />
Note that command to start the frontend on the remote host is commented out. It can be uncommented once the start_frontend script is written and tested.<br />
<br />
Shut down any clients that are running already by running odbedit on the MIDAS ''experiment host'' and execute the new startup script {{File|name=start_daq.sh}} (make it executable if desired)<br />
<small><br />
[mhostpc] odbedit<br />
[local:e777:S]/>sh all<br />
[mhostpc] ./start_daq.sh </small><br />
and check that {{Utility|name=odbedit}} still works on the ''REMOTE host''<br />
<span style="color:green; font-size:80%"><br />
[rlxhost] odbedit <br />
[mhostpc.triumf.ca:1175:e777:Stopped]/>ls </span><br />
<br />
You may wish to modify the script to restart missing clients only.<br />
<br />
<br />
=== Create script to start frontend === <br />
The startup file {{File|name=start_daq.sh}} invoked a script {{File|name=start_frontend}} to start the remote frontend. This line was commented out (see [[#Create experiment startup script|above]]). Once the frontend is working, create a script to start the frontend (e.g. file {{Filepath|path=$HOME/online/bin/start_frontend}}).<br />
<small><br />
#!/bin/tcsh<br />
# Script to start frontend running on $REMOTE_HOST<br />
#<br />
# This script runs on the REMOTE HOST<br />
#<br />
echo "starting frontend for experiment $MIDAS_EXPT_NAME "<br />
xterm -geometry 150x50+800+0 -fg white -bg blue -title "Frontend" -e "$HOME/online/fe_32/frontend" &<br />
echo "Frontend task has been started"<br />
exit<br />
</small><br />
Check that this script works by running it on the REMOTE host. If successful, <br />
run the commented command in an xterm on the ''experiment host''<br />
<small>[mhostpc] ssh $REMOTE_HOST $HOME/online/bin/start_frontend -O >& $HOME/online/bin/start_frontend.log & </small><br />
This command relies on password-less access set up with SSH keys (see [[#Experimental setup (Remote)]]).<br />
If successful, uncomment line in {{File|name=start_daq.sh}}, and run the script to check that it can start the remote client(s) from the MIDAS ''experiment host'' (first shutting down all running clients) :<br />
<small><br />
[mhostpc] odbedit -c "sh all"<br />
[mhostpc] ./start_daq.sh </small><br />
<br />
Next [[#Customizing your experiment|customize your experiment]].<br />
<br />
<br />
== Customizing your experiment ==<br />
<br />
Here are some of the more common options provided for users to customize their experiments according to their own preferences:<br />
<br />
; Write frontend(s) to read out and control your hardware<br />
: Modify the example frontend and Makefile (from $MIDASSYS/examples/) you have already copied and built so it accesses your hardware. <br />
<br />
: The example Frontend.c is an example of [[Frontend user code]]. You can find documentation about the frontend structure under [[Frontend Application]], [[Frontend Operation]] and [[Frontend user code]]. You may need to set up some [[Event Notification (Hot-Link)|Hot-Links]] in the frontend. The MIDAS package includes device drivers for some of the more common hardware. See [[MIDAS Driver Library]] for more information. <br />
<br />
; Modify analyzer(s) for your data <br />
: Modify the example analyzer code as necessary to analyze the data from your frontends.<br />
<br />
; Slow Controls <br />
: Control your slow-controls equipment with slow-control frontend(s) optionally using the [[#MIDAS Package Installation|MSCB]] system. <br />
<br />
; Data logger <br />
: Set up the data logger [[mlogger]] to log data to a storage device. Many options are available - see [[Data Logger]], [[mlogger]] and [[/Logger ODB tree]] for instructions.<br />
<br />
; Allow programs to be restarted from the [[Programs Page]] <br />
: See mhttpd [[Programs Page]] and [[/Programs ODB tree]]<br />
<br />
; Write scripts which run at start and end of run <br />
: See [[/Programs ODB tree#Execute on start run]] and [[/Programs ODB tree#Execute on stop run]]<br />
<br />
; Create a shutdown script <br />
: Create a script similar to the startup script to shutdown the experiment<br />
<br />
; Lazy logger<br />
: Set up the [[lazylogger]] for archiving data files for storage.<br />
<br />
; Create edit-on-start parameters <br />
: See [[Edit-on-start Parameters]]<br />
<br />
; Set up the alarm system <br />
: See [[Alarm System]] and [[Alarms Page]]<br />
<br />
; Set up history system <br />
: See [[History System]] and [[History Page]]<br />
<br />
; Set up the Electronic logbook (Elog) <br />
: See [[Elog]] for details<br />
<br />
; Set up the Sequencer <br />
: The [[Sequencer Page#Sequencer]] can be used to run a sequence of runs, changing parameters as needed. <br />
<br />
; Write a webserver Custom page <br />
: To better control and monitor your experiment, you may want to write a [[Custom Page]].<br />
<br />
== Start MIDAS on boot ==<br />
<br />
To start MIDAS mhttpd, mlogger and elogd at boot time, add following cron entries:<br />
<pre><br />
[iris@iris00 ~]$ crontab -l<br />
@reboot /home/iris/packages/midas/bin/mhttpd -D<br />
@reboot /home/iris/packages/midas/bin/mlogger -D<br />
@reboot /home/iris/packages/elog/elogd -c /home/iris/packages/elog/elogd.cfgiris -D<br />
[iris@iris00 ~]$ <br />
</pre><br />
<br />
[[Category:Installation]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3403Custom plots with mplot2023-12-23T14:03:09Z<p>Stefan Ritt: /* Setting parameters and data programmatically */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-x-log || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-y-log || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"getFromODB": true,<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the <code>plot_example.html</code> file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]<br />
<br />
= Setting parameters and data programmatically =<br />
<br />
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg; // obtain plot from ID<br />
p.param.title.text = "Different titel"; // change plot title<br />
p.param.plot[0].line.color = 2; // change line color of first plot to index 2<br />
</pre><br />
<br />
To change the data of the plot, use the function <code>setData(index, x, y)</code> for scatter plots or <code>setData(index, h)</code> for histograms like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg; // obtain plot from ID<br />
let x = Array.from({length: 20}, (_, i) => i); // create x array with 20 elements 0,1,2,...19<br />
let y = x.map(x => x*x); // create y array with y_i = x_i^2<br />
p.setData(0, x, y);<br />
</pre></div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3402Custom plots with mplot2023-12-23T14:02:09Z<p>Stefan Ritt: /* Setting parameters and data programmatically */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-x-log || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-y-log || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"getFromODB": true,<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the <code>plot_example.html</code> file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]<br />
<br />
= Setting parameters and data programmatically =<br />
<br />
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
p.param.title.text = "Different titel";<br />
p.param.plot[0].line.color = 2;<br />
</pre><br />
<br />
To change the data of the plot, use the function <code>setData(index, x, y)</code> for scatter plots or <code>setData(index, h)</code> for histograms like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
let x = Array.from({length: 20}, (_, i) => i); // create x array with 20 elements 0,1,2,...19<br />
let y = x.map(x => x*x); // create y array with y_i = x_i^2<br />
p.setData(0, x, y);<br />
</pre></div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3401Custom plots with mplot2023-12-23T14:00:52Z<p>Stefan Ritt: /* Setting parameters and data programmatically */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-x-log || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-y-log || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"getFromODB": true,<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the <code>plot_example.html</code> file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]<br />
<br />
= Setting parameters and data programmatically =<br />
<br />
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
p.param.title.text = "Different titel";<br />
p.param.plot[0].line.color = 2;<br />
</pre><br />
<br />
To change the data of the plot, use the function <code>setData(index, x, y)</code> for scatter plots or <code>setData(index, h)</code> for histograms like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
let x = Array.from({length: 20}, (_, i) => i);<br />
let y = x.map(x => x*x);<br />
p.setData(0, x, y);<br />
</pre></div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3400Custom plots with mplot2023-12-22T16:49:44Z<p>Stefan Ritt: /* JSON parameter set */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-x-log || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-y-log || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"getFromODB": true,<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the <code>plot_example.html</code> file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]<br />
<br />
= Setting parameters and data programmatically =<br />
<br />
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
p.param.title.text = "Different titel";<br />
p.param.plot[0].line.color = 2;<br />
</pre><br />
<br />
To change the data of the plot, use the function <code>setData(index, x, y)</code> for scatter plots or <code>setData(index, h)</code> for histograms like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
let x = Array(20);<br />
let y = Array(20);<br />
p.setData(0, x, y);<br />
</pre></div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3399Custom plots with mplot2023-12-22T16:48:50Z<p>Stefan Ritt: /* Setting parameters and data programmatically */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-x-log || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-y-log || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the <code>plot_example.html</code> file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]<br />
<br />
= Setting parameters and data programmatically =<br />
<br />
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
p.param.title.text = "Different titel";<br />
p.param.plot[0].line.color = 2;<br />
</pre><br />
<br />
To change the data of the plot, use the function <code>setData(index, x, y)</code> for scatter plots or <code>setData(index, h)</code> for histograms like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
let x = Array(20);<br />
let y = Array(20);<br />
p.setData(0, x, y);<br />
</pre></div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3398Custom plots with mplot2023-12-22T16:48:17Z<p>Stefan Ritt: /* Setting parameters and data programmatically */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-x-log || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-y-log || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the <code>plot_example.html</code> file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]<br />
<br />
= Setting parameters and data programmatically =<br />
<br />
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
p.param.title.text = "Different titel";<br />
p.param.plot[0].line.color = 2;<br />
</pre><br />
<br />
To change the data of the plot, use the function <code>setData(index, x, y)</code) for scatter plots or <code>setData(index, h)</code> for histograms like<br />
<br />
<pre><br />
let p = document.getElementById("MyPlot").mpg;<br />
let x = Array(20);<br />
let y = Array(20);<br />
p.setData(0, x, y);<br />
</pre></div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3397Custom plots with mplot2023-12-22T16:42:37Z<p>Stefan Ritt: /* More examples */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-x-log || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-y-log || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the <code>plot_example.html</code> file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]<br />
<br />
= Setting parameters and data programmatically =</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3396Custom plots with mplot2023-12-18T10:32:24Z<p>Stefan Ritt: /* Optional parameters */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-x-log || Use logarithmic X-axis if "true"<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-y-log || Use logarithmic Y-axis if "true"<br />
|-<br />
| data-z-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-z-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the plot_example.html file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3395Custom plots with mplot2023-12-18T10:16:36Z<p>Stefan Ritt: /* JSON parameter set */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-x-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code>&lt;div&gt;</code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the plot_example.html file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3394Custom plots with mplot2023-12-18T10:15:36Z<p>Stefan Ritt: /* Optional parameters */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-h<n> || Y data for the <n>-th histogram if several histograms are used<br />
|-<br />
| data-label<n> || Label for <n>-th graph or histogram if serveral are used<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-x-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code><div></code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the plot_example.html file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3393Custom plots with mplot2023-12-15T19:43:16Z<p>Stefan Ritt: /* JSON parameter set */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-x-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code><div></code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.<br />
<br />
= More examples =<br />
<br />
More plot examples are contained in the plot_example.html file in the midas/resource/ directory which produces following page:<br />
<br />
[[File:Plots.png]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=File:Plots.png&diff=3392File:Plots.png2023-12-15T19:43:05Z<p>Stefan Ritt: </p>
<hr />
<div>Plot examples</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3391Custom plots with mplot2023-12-15T19:32:47Z<p>Stefan Ritt: /* Optional parameters */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
Several options are possible to modify the plot. Following table gives an overview of the various parameters:<br />
<br />
{| class="wikitable"<br />
|+ Available parameters for "mplot" div<br />
|-<br />
! Parameter !! Meaning<br />
|-<br />
| data-odb-path || Path into the ODB where the data for the plot is stored<br />
|-<br />
| data-x || X data for the first graph<br />
|-<br />
| data-y || Y data for the first graph<br />
|-<br />
| data-x1 || X data for the first graph if several graphs are used<br />
|-<br />
| data-y1 || Y data for the first graph if several graphs are used<br />
|-<br />
| data-x<n> || X data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-y<n> || Y data for the <n>-th graph if several graphs are used<br />
|-<br />
| data-h || Y data for a histogram<br />
|-<br />
| data-xy || Data for a color map. The array must have the dimension n*m with n, m the dimensions in x and y of the color map<br />
|-<br />
| data-title || Title of the graph shown on top<br />
|-<br />
| data-x-text || Label shown below the X-axis<br />
|-<br />
| data-y-text || Label shown left of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the X-axis<br />
|-<br />
| data-x-max || Maximum of the X-axis<br />
|-<br />
| data-y-min || Minimum of the Y-axis<br />
|-<br />
| data-y-max || Maximum of the Y-axis<br />
|-<br />
| data-x-min || Minimum of the Z-axis (color axis for color maps)<br />
|-<br />
| data-x-max || Maximum of the Z-axis<br />
|-<br />
| data-overlay || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.<br />
|}<br />
<br />
= Overlay function =<br />
<br />
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:<br />
<br />
<pre><br />
function overlay(plot, ctx) {<br />
ctx.fillStyle = "red";<br />
plot.drawTextBox(ctx, "First overlay line\nSecond overlay line", 120, 150);<br />
}<br />
</pre><br />
<br />
= JSON parameter set =<br />
<br />
Instead of defining all parameters with <code>data-xxx</code> tags, the parameters might be defined inside the <code><div></code> tag using JSON encoding. Following text gives a complete example of all parameters:<br />
<br />
<pre><br />
<div class="mplot" style="height: 400px;width: 700px;"><br />
{<br />
"showMenuButtons": true,<br />
<br />
"color": {<br />
"background": "#FFFFFF",<br />
"axis": "#808080",<br />
"grid": "#D0D0D0",<br />
"label": "#404040"<br />
},<br />
<br />
"title": {<br />
"color": "#404040",<br />
"backgroundColor": "#808080",<br />
"textSize": 20,<br />
"text": "Customized scatter plot"<br />
},<br />
<br />
"legend": {<br />
"show": true,<br />
"color": "#D0D0D0",<br />
"backgroundColor": "#FFFFFF",<br />
"textColor": "#404040",<br />
"textSize": 16<br />
},<br />
<br />
"xAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "x [mm]",<br />
"textSize" : 14<br />
}<br />
},<br />
<br />
"yAxis": {<br />
"log": false,<br />
"min": -10,<br />
"max": 10,<br />
"grid": true,<br />
"textSize": 10,<br />
"title": {<br />
"text": "Scaler [Hz]",<br />
"textSize" : 10<br />
}<br />
},<br />
<br />
"plot": [<br />
{<br />
"type": "scatter",<br />
"odbPath": "/System/Tmp/Plot",<br />
"x": "X2",<br />
"y": "Y2",<br />
"label": "High threshold",<br />
"marker": {<br />
"draw": true,<br />
"lineColor": 3,<br />
"fillColor": 3,<br />
"style": "cross",<br />
"size": 10,<br />
"lineWidth": 2<br />
},<br />
<br />
"line": {<br />
"draw": false,<br />
"fill": true,<br />
"color": 0,<br />
"style": "solid",<br />
"width": 1<br />
}<br />
}<br />
]<br />
<br />
}<br />
</div><br />
</pre><br />
<br />
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like "red" or "#FF0000", or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3390Custom plots with mplot2023-12-15T19:16:45Z<p>Stefan Ritt: /* Introduciton */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code> as two float arrays of the same size.<br />
<br />
= Optional parameters =<br />
<br />
TBD</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&diff=3389Custom plots with mplot2023-12-15T17:34:38Z<p>Stefan Ritt: Created page with "{{Pagelinks}} = Introduciton = Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js..."</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduciton =<br />
<br />
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the <code>mplot.js</code> library and creating a <code>&lt;div class="mplot"&gt;</code> element. Following example shows the code for a simple page for a scatter plot:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="mplot.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');mplot_init()"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<div class="mplot" style="height: 360px;width: 700px;"<br />
data-odb-path="/Path/To/Data"<br />
data-x="X" data-y="Y"><br />
</div><br />
<br />
</div><br />
</pre><br />
<br />
And here is the resulting page:<br />
<br />
[[File:Simple plot.png]]<br />
<br />
The data is stored in the ODB under the X-array <code>/Path/To/Data/X</code> and the Y-array under <code>/Path/To/Data/Y</code><br />
<br />
= Optional parameters =<br />
<br />
TBD</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=File:Simple_plot.png&diff=3388File:Simple plot.png2023-12-15T17:31:32Z<p>Stefan Ritt: </p>
<hr />
<div>Simple plot using mplot</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&diff=3387Custom Page2023-12-15T17:18:58Z<p>Stefan Ritt: /* mjshistory */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}<br />
<br />
= Purpose =<br />
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].<br />
<br />
= Introduction =<br />
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.<br />
<br />
We note that MIDAS has provided a number of different ways of providing custom pages over the years. A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.<br />
<br />
= Examples of Custom Pages =<br />
<br />
Click on the thumbnails to enlarge.<br />
<br />
{|<br />
|-<br />
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || '''Example 1'''<br />
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of "fills" and "labels". Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).<br />
|-<br />
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || '''Example 2'''<br />
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a "virtual" X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.<br />
<br />
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. <br />
<br />
For details using Xvfb server, please contact Ryu Sawada <sawada@icepp.s.u-tokyo.ac.jp>.<br />
|-<br />
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || '''Example 3'''<br />
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value. <br />
|}<br />
<br />
= Access a Custom Page from the Regular MIDAS pages =<br />
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.<br />
<br />
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in<br />
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.<br />
See [[Custom Page Features#Resource files]] for more information.<br />
<br />
If the key {{Odbpath|path=/Custom/myPage&}} (see Note) is created, e.g.<br />
odbedit> ls /Custom<br />
Path /home/expt/online/custom<br />
myPage& mypage.html<br />
<br />
the custom link on the left navigation bar will be <code>myPage</code> and the URL for the resulting custom page will be of the form <code>http://myhost.mydomain:myport/cmd=?Custom&page=myPage</code> (see also [[mhttpd#usage]]). <br />
Clicking on <code>myPage</code> will display the custom page in the same window.<br />
<br />
;Note<br />
: Without the "&" symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.<br />
<br />
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.<br />
<br />
odbedit> ls -r /Custom<br />
Path /home/expt/online/custom<br />
myPage& mypage.html<br />
Calorimeter<br />
HV hv.html<br />
Rates rates.html<br />
Baeam<br />
Beamline beamline.html<br />
Accelerator accel.html<br />
<br />
The pages then include the subdirectory in the URL, like <br />
<br />
http://localhost:8080?cmd=custom&page=beam/Beamline<br />
<br />
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as<br />
<br />
<body class="mcss" onload="mhttpd_init('Calorimeter/HV');"><br />
<br />
in order to keep the submenu open after you select the custom page.<br />
<br />
= How to write a custom page =<br />
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.<br />
<br />
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions. The advantages of using these modb* javascript functions are<br />
<br />
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.<br />
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.<br />
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.<br />
<br />
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient. In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call). It will also require more coding to implement the custom page with only MjsonRPC calls.<br />
<br />
== How to use the standard MIDAS navigation bars on your custom page == <br />
<br />
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax <br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');"><br />
<br />
<!-- header and side navigation will be filled in mhttpd_start --><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
ADD YOUR HTML/JS CODE here...<br />
</div><br />
</pre><br />
<br />
The call <code>mhttpd_init('myPage')</code> is executed when the page is loaded, and <code>myPage</code> is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.<br />
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].<br />
<br />
= modb* Javascript scheme = <br />
<br />
The general scheme of the custom page scheme is to write <code>&lt;div class="modb..."&gt;&lt;/div&gt;</code> or <code>&lt;span class="modb..."&gt;&lt;/span&gt;</code> tags with special class names, most of them starting with "modb..." ("MIDAS-ODB"). Use a <code>&lt;div&gt;</code> tag if you want the element to appear in a separate line, and use the <code>&lt;span&gt;</code> tag if you want to display the element in-line. The following description uses only <code>&lt;div&gt;</code> tags, but all of them can be changed to <code>&lt;span&gt;</code>. All HTML tags with "modb..." names are scanned by the <code>mhttp_init('name')</code> function upon page load, and their inner contents is replaced by the requested ODB value or some graphics. The contents is then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to <code>mhttpd_init('name', interval)</code> where "interval" is in milliseconds.<br />
<br />
== modbset(path, value) ==<br />
<br />
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:<br />
<br />
<code><br />
modbset("odb path", value)<br />
</code><br />
<br />
<code><br />
modbset(["odb path1", "odb path2", ...], [value1, value2, ...])<br />
</code><br />
<br />
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.<br />
<br />
== modb ==<br />
<br />
This special HTML div tag (abbreviation stands for Midas ODB) <code>&lt;div class="modb" data-odb-path="/Some/Path" onchange="func()"&gt;</code> can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &lt;script&gt;...&lt;/script&gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.<br />
<br />
The current value of the ODB entry is available inside the "onchange" function as '''this.value'''. Following tag will call a function which logs the current run number in the JavaScript console window:<br />
<br /><br /><br />
<code>&lt;div class="modb" data-odb-path="/Runinfo/Run number" onchange="func(this.value)"&gt;<br />
<br />
&lt;script&gt;function func(value) {<br />
console.log(value);<br />
}&lt;/script&gt;<br />
</code><br />
<br /><br /><br />
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to '''this.value''' as a JavaSctipt object such as<br />
<br /><br /><br />
<code>&lt;div class="modb" data-odb-path="/Runinfo" onchange="func(this.value)"&gt;<br />
<br />
&lt;script&gt;function func(value) {<br />
console.log(value["run number"]);<br />
}&lt;/script&gt;<br />
<br /><br />
</code><br />
<br /><br /><br />
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket '''value["run number"]''' as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as '''value.state''' for /Runinfo/State for example.<br />
<br />
== modbvalue ==<br />
<br />
This special HTML div tag (abbreviation stands for "Midas ODB VALUE") <br />
<br /><br /><br />
<code>&lt;div class="modbvalue" data-odb-path="/Some/Path"&gt;&lt;/div&gt;</code> <br />
<br /><br /><br />
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:<br />
<br />
{| class="wikitable"<br />
|+ Table 1: List of valid options for modbvalue tag<br />
|-<br />
! Option !! Example !! Meaning<br />
|-<br />
| data-name || class="modbvalue" || Tells the framework to replace this tag with an ODB value<br />
|-<br />
| data-odb-path || data-odb-path = "/Runinfo/Run number" || Path to the value in the ODB<br />
|-<br />
| data-odb-editable || data-odb-editable="1" || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.<br />
|-<br />
| data-format || data-format="f3" || Specify format of data shown. See Table 2 below for options.<br />
|-<br />
| data-size || data-size="8" || Specify size (in chars) of edit box if one modifies the value. Default is 10.<br />
|-<br />
| data-formula || data-formula="2*x+3" || Specify an optional formula to process with the current ODB value stored in x<br />
|-<br />
| data-validate || data-validate="func" || Specify an optional validation function which gets called before submitting data (see below)<br />
|}<br />
<br />
=== Validation ===<br />
<br />
Before a modified value is submitted to the ODB, an optional validation function can be called via the <code>data-validate</code> option. The function<br />
will be called with the current value and a reference to the current modbvalue element. If the function returns <code>false</code>, then the value<br />
is not sent to the ODB.<br />
<br />
Following example shows a function which just rejects the submission of values above 1000:<br />
<br />
&lt;div class="modbvalue" ... data-odb-editable="1" data-validate="my_validate"&gt;&lt;/div&gt;<br />
<br />
&lt;script src="controls.js"&gt;&lt;/script&gt; &lt;!-- needed of dlgAlert() --&gt;<br />
&lt;script&gt;<br />
function my_validate(value, element) {<br />
if (value > 1000) {<br />
dlgAlert("Value cannot be above 1000");<br />
return false;<br />
}<br />
return true;<br />
}<br />
&lt;/script&gt;<br />
<br />
Following function corrects the return value to 1000 if it's above 1000:<br />
<br />
&lt;script&gt;<br />
function my_validate2(value, element) {<br />
if (value > 1000) {<br />
element.childNodes[0].value = 1000;<br />
return true;<br />
}<br />
&lt;/script&gt;<br />
<br />
=== Confirmation ===<br />
<br />
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. <br />
This is done with the option <code>data-confirm=&lt;string&gt;</code>. A dialog box is then shown with the <code>&lt;string&gt;</code> as the main text and two buttons with "OK" and "Cancel".<br />
Hitting "OK" finally writes the value to the ODB, hitting "Cancel" keeps the old value.<br />
<br />
Following example shows an example:<br />
<br />
&lt;div class="modbvalue" ... data-odb-editable="1" data-confirm="Are you sure to change the value?"&gt;&lt;/div&gt;<br />
<br />
Following dialog box is then showed:<br />
<br />
[[File:Confirm.png|frame|left|Optional confirm dialog]]<br />
<br />
<br clear=all><br />
<br />
=== Formatting ===<br />
<br />
Table 2 below lists the format specifiers supported with a modbvalue tag <code>data-format="..."</code>:<br />
<br />
{| class="wikitable"<br />
|+ Table 2: Format specifiers for modbvalue tag <code>data-format="..."</code>.<br />
|-<br />
! Option !! Valid for* !! Example !! Meaning<br />
|-<br />
| %d || int || 1234 || Shows a number in decimal encoding<br />
|-<br />
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator<br />
|-<br />
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding<br />
|-<br />
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like <code>data-format="%d / %x"</code> to produce <code>1234 / 0x4D2</code><br />
|-<br />
| %f&lt;x&gt; || float || 1.234 || Shows a floating point number with &lt;x&gt; digits after the decimal. A value of f0 shows only the integer part.<br />
|-<br />
| %p&lt;x&gt; || float || 1.23 || Shows a floating point number with &lt;x&gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012<br />
|-<br />
| %e&lt;x&gt; || float || 1.23e-05 || Shows a floating point number with &lt;x&gt; digits after the decimal in exponential format. Useful for small number such as pressures.<br />
|-<br />
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.<br />
|-<br />
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats<br />
|}<br />
<br />
* Note that valid for "int" means all integral ODB types (regardless of size or signed/unsigned) and valid for "float" means both float and double.<br />
<br />
== modbbutton ==<br />
<br />
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the "Run number" to 100, one can use the following tag:<br />
<br /><br /><br />
<code>&lt;button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="100" data-validate="my_validate"&gt;[Button Text]&lt;/button&gt;</code><br />
<br />
<br />
The optional <code>data-validate</code> function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.<br />
<br />
== modbbox ==<br />
<br />
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the '''data-color''' is used, otherwise the '''data-background-color''' is used<br />
<br /><br /><br />
<code>&lt;div class="modbbox" data-odb-path="/Logger/Write Data" data-formula="x > 0" style="width: 30px; height: 30px; border: 1px solid black" data-color="lightgreen" data-background-color="red"&gt;&lt;/div&gt;</code><br />
<br />
Optionally, a <code>data-formula</code> can be specified. The formula sees the ODB value in the variable <code>x</code>, and can do any boolean operation. If the result of this is true, then the box gets the <code>data-color</code>, otherwise the <code>data-background-color</code>.<br />
Examples for these formulas are <code>x > 10</code> for a comparison or <code>x & 1</code> which will do a bitwise AND operation and is true only for odd numbers.<br />
<br />
== modbcheckbox ==<br />
<br />
This tag generates a check box which can set a certain ODB entry to true or false. To set the "Write data" flag for the logger true or false, one can use the following tag:<br />
<br /><br /><br />
<code>&lt;input type="checkbox" class="modbcheckbox" data-odb-path="/Logger/Write data" data-validate="my_validate" /&gt;</code><br />
<br /><br /><br />
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional <code>data-validate</code> function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for <code>modbvalue</code> tag.<br />
<br />
== modbselect ==<br />
<br />
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under <code>/Runinfo/Run number</code>.<br />
<br />
&lt;select class="modbselect" data-odb-path="/Runinfo/Run number"&gt;<br />
&lt;option value="1"&gt;1&lt;/option&gt;<br />
&lt;option value="5"&gt;5&lt;/option&gt;<br />
&lt;option value="10"&gt;10&lt;/option&gt;<br />
&lt;/select&gt;<br />
<br />
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify <code>data-auto-options="1"</code> to enable this behaviour. For ODB key <code>x</code>, you will need to create an ODB entry called <code>Options x</code> in the same directory; this "options" key should be a list, with each element in the list being an allowable option.<br />
<br />
<!-- Will read options from "/Equipment/Example/Settings/Options Something" in this case --><br />
&lt;select class="modbselect" data-odb-path="/Equipment/Example/Settings/Something" data-auto-options="1"&gt;<br />
&lt;/select&gt;<br />
<br />
The benefit of the <code>data-auto-options="1"</code> approach is that the same options will be shown on the regular ODB browser webpage. <br />
<br />
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.<br />
<br />
== modbhbar ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbhbar" style="width: 500px; height: 18px; color: lightgreen;" data-odb-path="/Runinfo/Run number" data-max-value="10" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 500px" || Total width of the horizontal bar || Yes<br />
|-<br />
| style="height: 18px" || Height of the horizontal bar || Yes<br />
|-<br />
| style="color: red" || Color of horizontal bar || Transparent if not present<br />
|-<br />
| style="background-color: red" || Background color of horizontal bar || Transparent if not present<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-min-value || Left limit of bar range || 0 if not present<br />
|-<br />
| data-max-value || Right limit of bar range || 1 if not present<br />
|-<br />
| data-log || Logarithmic display || No<br />
|-<br />
| data-print-value || If "1", data value is shown as text overlay || No<br />
|-<br />
| data-format || Specify format of data shown. See Table 2 above for options || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
== mhaxis ==<br />
<br />
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="mhaxis" style="width: 500px; height: 22px;" data-min-value="0" data-max-value="10" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a horizontal axis next to the bar. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 500px" || Total width of the axis, must match the width of the horizontal bar || Yes<br />
|-<br />
| style="height: 18px" || Height of the axis, must be enough to display labels || Yes<br />
|-<br />
| style="vertical-align: top" || Must be "top" if the axis is below the bar || "bottom" if not given<br />
|-<br />
| data-min-value || Left limit of axis range || Yes<br />
|-<br />
| data-max-value || Right limit of axis range || Yes<br />
|-<br />
| data-log || Logarithmic display || No<br />
|}<br />
<br />
== modbvbar ==<br />
<br />
Same as <code>modbhbar</code>, except the bar grows vertically instead of horizontally.<br />
<br />
== mvaxis ==<br />
<br />
Same as <code>mhaxis</code>, except the axis is shown vertically instead of horizontally.<br />
<br />
== modbthermo ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbthermo" style="width: 30px; height: 100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30" data-color="blue" data-print-value="1" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it's good for testing. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 30px" || Width of the thermometer || Yes<br />
|-<br />
| style="height: 100px" || Total height of the thermometer || Yes<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-max-value || Upper range || Yes<br />
|-<br />
| data-min-value || Lower range || 0 if not present<br />
|-<br />
| data-color || Color of thermometer || Black if not present<br />
|-<br />
| data-background-color || Color of thermometer background || Transparent if not present<br />
|-<br />
| data-print-value || If "1", data value is shown below the thermometer || No<br />
|-<br />
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
== modbgauge ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbgauge" style="width: 100px; height: 50px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10" data-color="darkgreen"&gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a circular gauge ranging from 0 to 10. Depending on the ODB value "Run number". Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 100px" || Width of the gauge || Yes<br />
|-<br />
| style="height: 50px" || Total height of the gauge || Yes<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-max-value || Upper range || Yes<br />
|-<br />
| data-min-value || Lower range || 0 if not present<br />
|-<br />
| data-color || Color of gauge || Black if not present<br />
|-<br />
| data-background-color || Color of gauge background || Transparent if not present<br />
|-<br />
| data-print-value || If "1", data value is shown below the gauge || No<br />
|-<br />
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No<br />
|-<br />
| data-scale || If "1", the min and max values of the range are shown below the gauge || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.<br />
<br />
== Changing properties of controls dynamically ==<br />
<br />
All custom controls can be configured to call a user's function when the control is first set up, or when the value changes. This is done by specifying the '''onload''' and/or '''onchange''' functions.<br />
<br />
* '''onload''' is called only once, when the control's value is first read from the ODB.<br />
* '''onchange''' is called each time the value in the ODB changes.<br />
<br />
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:<br />
<br />
<code>onchange="this.dataset.color=this.value > 30?'red':'blue';"</code><br />
<br />
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:<br />
<br />
<pre><br />
<script><br />
function check_therm(elem) {<br />
if (elem.value > 30) {<br />
elem.dataset.color = "red";<br />
} else {<br />
elem.dataset.color = "blue";<br />
}<br />
};<br />
</script><br />
<br />
....<br />
<br />
<div class="modbthermo" style="width: 30px; height: 100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30" data-color="blue" data-print-value="1" onchange="check_therm(this)"></div><br />
<br />
</pre><br />
<br />
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.<br />
<br />
If you want the same function to be called for both onload and onchange, you could set <code>onload="onchange()"</code> and have the "real" function in onchange.<br />
<br />
== Changing values of indicators programmatically ==<br />
<br />
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. <br />
<br />
For such cases, the <code>data-odb-path</code> attribute can be removed and the the display value can be changed via JavaScript code like following:<br />
<br />
<pre><br />
<br />
<div id="mythermo" class="modbthermo" data-min-value="-10" data-max-value="30"></div><br />
<br />
...<br />
let t = document.getElementById("mythermo");<br />
t.setValue(15);<br />
...<br />
<br />
</pre><br />
<br />
= mjshistory =<br />
<br />
Custom pages can contain one or more specific history panels usually shown on the "History" page. This makes it easy to combine current readings of values together with the history of these values.<br />
<br />
To enable interactive history panels, following lines have to be added to your custom page:<br />
<br />
Inisde the <head> tag:<br />
<br />
<script src="mhistory.js"></script><br />
<br />
Inside the <body> tag:<br />
<br />
<body ... onload="mhistory_init();"><br />
<br />
The following tag:<br />
<br />
&lt;div class="mjshistory" data-group="<group>" data-panel="<panel>" style="width: 320px; height: 200px;" &gt;&lt;/div&gt;<br />
<br />
shows a history panel defined in the ODB under /History/Display/&lt;group&gt;/&lt;panel&gt; (replace &lt;group&gt;/&lt;panel&gt; with groups and panels from your experiment).<br />
<br />
Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
| data-group || ODB group of history. Has to match a group under /History/Display || Yes<br />
|-<br />
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&lt;group&gt;/ || Yes<br />
|-<br />
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&lt;group&gt;/&lt;panel&gt;/Timescale is used. || No<br />
|-<br />
| style="width: 320px" || Width of the history panel || No<br />
|-<br />
| style="height: 200px" || Height of the history panel || No<br />
|-<br />
| style="border: 1px solid black" || Border around the history panel || No<br />
|}<br />
<br />
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.<br />
<br />
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users <br />
the possibility to show the history of that variable. Just put a button next to the value and call '''mhistory_dialog(&lt;group&gt;, &lt;panel&gt;)''' from that button like:<br />
<br />
&lt;span class="modbvalue" data-odb-path="/Some/Path"&gt;&lt;/span&gt;<br />
<button onclick="mhistory_dialog('group','panel')"><img src="icons/activity.svg"></button><br />
<br />
The history panel will then be opened when the user clicks the button:<br />
<br />
[[File:History dialog.png|frame|left|Floating history dialog]]<br />
<br />
<br clear=all><br />
<br />
If one wants to avoid the definition of a history panel in the ODB, a "direct variable plot" can be done with the function '''mhistory_dialog_var(&lt;variable&gt;)''' where &lt;variable&gt; has the format like the variable definition in the ODB (e.g. "System:Trigger per sec."). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as '''mhistory_dialog_var("System:Trigger per sec.", {"Timescale": "24h"});'''<br />
<br />
A full example of a custom page with a history panel is shown below.<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css" type="text/css"><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="controls.js"></script><br />
<script src="mhistory.js"></script><br />
</head><br />
<body onload="mhttpd_init('history_example'); mhistory_init();"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div> <br />
<div id="mmain"><br />
<div class="mjshistory" data-group="EPICS" data-panel="Logging" style="width: 500px; height: 300px;" ></div><br />
</div><br />
</body><br />
</html><br />
</pre><br />
<br />
= mplot =<br />
<br />
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.<br />
<br />
= Complete Example =<br />
<br />
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:<br />
<br />
<pre><br />
/Custom<br />
Path /midas/resources<br />
Test a_example.html<br />
</pre><br />
<br />
== Code ==<br />
The file '''a_example.html''' contains the following code:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html class="mcss"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<title>Example</title><br />
<br />
<style><br />
.mtable td { padding: 10px; }<br />
</style><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('Example');"><br />
<br />
<!-- header and side navigation will be filled in mhttpd_init --><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<table class="mtable"><br />
<tr><br />
<th colspan="2" class="mtableheader">Status</th><br />
</tr><br />
<tr><br />
<td style="width: 200px;"><br />
Run number:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Run number" data-odb-editable="1"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Last run start:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Start time"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Last run stop:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Stop time"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Check box:<br />
</td><br />
<td><br />
<!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --><br />
<input type="checkbox" class="modbcheckbox" data-odb-path="/Logger/Write data"></input><br />
<br />
<div class="modb" data-odb-path="/Logger/Write data" onchange="dlgAlert('Flag has changed');"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Color box:<br />
</td><br />
<td><br />
<!-- box changes color according to /Logger/Write data --><br />
<div class="modbbox" style="width: 30px; height: 30px;" data-odb-path="/Logger/Write data"<br />
data-color="lightgreen" data-background-color="red"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Horizontal bars:<br />
</td><br />
<td><br />
<div class="modbhbar" style="width:300px;height:20px;color:orange;" data-odb-path="/Runinfo/Run number"<br />
data-max-value="10" data-print-value="1"></div><br /><br />
<br />
<div class="mhaxis" style="width:500px;height:22px;" data-min-value="0" data-max-value="10"></div><br />
<div class="modbhbar" style="width: 500px; height: 18px;color:lightblue" data-odb-path="/Runinfo/Run number"<br />
data-max-value="10"></div><br /><br />
<br />
<div class="modbhbar" style="width: 200px; height: 10px;color:lightgreen;background-color:white"<br />
data-odb-path="/Runinfo/Run number" data-min-value="0.1" data-max-value="10" data-log="1"></div><br />
<div class="mhaxis" style="width:200px;height:22px;vertical-align:top;" data-min-value="0.1"<br />
data-max-value="10" data-line="0" data-log="1"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Vertical bars:<br />
</td><br />
<td><br />
<span class="mvaxis" style="width:100px;height:200px;text-align:right;" data-min-value="0" data-max-value="20"></span><span class="modbvbar"<br />
style="width:20px;height:200px;color:yellow;" data-odb-path="/Runinfo/Run number"<br />
data-min-value="0" data-max-value="20"></span><br />
<span class="modbvbar" style="width:10px;height:200px;vertical-align:top;color:red" data-odb-path="/Runinfo/Run number" data-min-value="0.1"<br />
data-max-value="10" data-log="1"></span><span class="mvaxis" style="width:100px;height:200px;text-align:left;" data-min-value="0.1"<br />
data-max-value="10" data-log="1"></span><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td><br />
Thermometer:<br />
</td><br />
<td><br />
<div class="modbthermo" style="width:30px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="darkgreen"></div><br />
<div class="modbthermo" style="width:60px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-scale="1"<br />
onchange="this.dataset.color=this.value > 9?'red':'blue';"></div><br />
<div class="modbthermo" style="width:30px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-background-color="white" data-value="1"></div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td><br />
Gauges:<br />
</td><br />
<td><br />
<div class="modbgauge" style="width:100px;height:50px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10"<br />
data-color="darkgreen" data-background-color="lightgrey" ></div><br />
<div class="modbgauge" style="width:100px;height:65px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10"<br />
data-color="red" data-value="1" data-scale="1"></div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td colspan="2" style="text-align: center;"><br />
<!-- div around image with "relative" position as anchor for labels and bars --><br />
<div style="position:relative;width:300px;margin:auto"><br />
<br />
<img src="tank.gif"> <!-- background image of tank --><br />
<br />
<!-- label next to valve --><br />
<div class="modbvalue" data-odb-path="/Runinfo/Run number" data-odb-editable="1"<br />
style="position:absolute;top:157px;left:288px;"></div><br />
<br />
<!-- vertical bar inside tank, render red if value > 9 --><br />
<div class="modbvbar" style="position:absolute;top:80px;left:10px;width:104px;height:170px;"<br />
data-odb-path="/Runinfo/Run number" data-max-value="11" data-color="green"<br />
onchange="this.firstChild.style.backgroundColor=(this.value > 9)?'red':'green';"></div><br />
<br />
<!-- thermometer inside tank --><br />
<div class="modbthermo" style="position:absolute;top:140px;left:20px;width:20px;height:100px;"<br />
data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-value="1"></div><br />
<br />
</div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td colspan="2" style="text-align: center;"><br />
<!-- three buttons to change an ODB entry (run number in this example) --><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="1">Set run<br />
number to 1<br />
</button><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="5">Set run<br />
number to 5<br />
</button><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="10">Set run<br />
number to 10<br />
</button><br />
</td><br />
</tr><br />
</table><br />
</div><br />
<br />
</body><br />
</html><br />
</pre><br />
<br />
which results in the page shown in Figure 1 below:<br />
<br />
[[File:Custom17.png|frame|left|Figure 1 Example custom page using most features]]<br />
<br />
<div style="clear: both"></div> <!-- clear wraparound after thumbnail --><br />
<br />
= Old custom page feature =<br />
<br />
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].<br />
<br />
<br />
[[Category:mhttpd Pages]] [[Category:Custom]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&diff=3385Odbxx2023-12-06T10:34:57Z<p>Stefan Ritt: /* Creating new bits of the ODB */</p>
<hr />
<div>A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a "magic" map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.<br />
<br />
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/progs/odbxx_test.cxx odbxx_test.cxx].<br />
<br />
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]] page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.<br />
<br />
== Basic usage ==<br />
<br />
The simplest usage is like:<br />
<br />
<pre>// Grab a bit of the ODB<br />
midas::odb exp("/Experiment");<br />
<br />
// Simple read<br />
std::cout << "The current transition timeout is " << exp["Transition timeout"] << std::endl;<br />
<br />
// Make a change. The new value is automatically sent to the ODB.<br />
// Most C++ operators are supported (++, += etc), or you can do a simple<br />
// re-assignment like `exp["Transition timeout"] = 12345;`.<br />
exp["Transition timeout"] += 100;<br />
<br />
// Read the new value<br />
std::cout << "The transition timeout is now " << exp["Transition timeout"] << std::endl;<br />
</pre><br />
<br />
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:<br />
<br />
<pre>int curr_timeout = exp["Transition timeout"];</pre><br />
<br />
'''Note''': The ODB directory you connect to ("/Experiment" in the above example), has to start with a "/" to tell the midas::odb object<br />
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.<br />
<br />
== Automatic refreshing ==<br />
<br />
You may temporarily disable the automatic updating to/from the ODB<br />
using <code>odb::set_auto_refresh_write(false)</code> and <code>odb::set_auto_refresh_read(false)</code>.<br />
<br />
If auto-refresh is enabled (the default), your new values are sent to<br />
the ODB as soon as you touch the value in the <code>midas::odb</code> object. The ODB<br />
is queried for new values whenever you access the value. In the above<br />
example, the ODB is queried 4 times (during construction of <code>exp</code>, and<br />
each time <code>exp["Transition timeout"]</code> is mentioned), and written to 1<br />
time (when <code>exp["Transition timeout"]</code> is assigned to).<br />
<br />
See the [[#Callback functions]] section below for details on how to have a function<br />
called when a value changes.<br />
<br />
== Arrays/vectors ==<br />
<br />
ODB arrays are represented by std vectors.<br />
<br />
You can access/edit individual elements using []:<br />
<br />
<pre>odb["Example"][1] = 1.2;</pre><br />
<br />
You can completely re-assign content using a std::vector or std::array:<br />
<br />
<pre>std::vector<float> vec = std::vector<float>(10);<br />
odb["Example"] = vec;<br />
</pre><br />
<br />
You can resize arrays using <code>odb::resize()</code>. If the existing array is longer,<br />
it will be truncated; if shorter it will be extended with default values<br />
(0 or an empty string).<br />
<br />
<pre>odb["Example"].resize(5); // Now is 5 elements long</pre><br />
<br />
Note that arithmetic operators are supported for arrays, and will apply<br />
the operation to ALL ELEMENTS IN THE ARRAY:<br />
<br />
<pre>// Create the vector<br />
std::vector<float> vec = std::vector<float>(2);<br />
vec[0] = 3;<br />
vec[1] = 5;<br />
<br />
// Assign in ODB<br />
odb["Example"] = vec;<br />
<br />
// Multiply ALL elements by 2<br />
odb["Example"] *= 2;<br />
<br />
// odb["Example"] now contains {6, 10}.<br />
</pre><br />
<br />
You can directly iterate over arrays/vectors:<br />
<br />
<pre>// Iterating using standard begin/end.<br />
for (auto it = o["Int Array"].begin(); it != o["Int Array"].end(); it++) {<br />
int val = *it;<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (int val : o["Int Array"]) {<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
== Strings ==<br />
<br />
Strings in the ODB are returned as std::string (unlike the midas.h <code>db_get_value()</code><br />
family of functions, where strings are returned as char*). You may have vectors of strings.<br />
<br />
== Creating new bits of the ODB ==<br />
<br />
You can automatically create bits of the ODB by passing a struct to the<br />
<code>midas::odb</code> constructor, then calling <code>odb::connect()</code>, like:<br />
<br />
<pre>// Define the ODB structure<br />
midas::odb o = {<br />
{"Int32 Key", 42},<br />
{"Bool Key", true},<br />
{"Subdir", {<br />
{"Float key", 1.2f}, // floats must be explicitly specified<br />
}},<br />
{"Int Array", {1, 2, 3}},<br />
{"Double Array", {1.2, 2.3, 3.4}},<br />
{"String Array", {"Hello1", "Hello2", "Hello3"}},<br />
{"Large Array", std::array<int, 10>{} }, // array with explicit size<br />
{"Large String", std::string(63, '\0') }, // string with explicit size<br />
};<br />
<br />
// Then sync the structure. This function<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings");<br />
<br />
// If you make the `write_defaults` argument true, then the function<br />
// - overwrites the value of any keys that are in the ODB with the value in your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings", true);<br />
<br />
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
// - deletes any keys that are in the ODB but not your code<br />
// - updates the order of keys in the ODB to match your code<br />
o.connect_and_fix_structure("/Test/Settings");<br />
</pre><br />
<br />
Note that the ODB path in teh odb::connect() call must start with a '/'. The ODB root path like <code>o.connect("/");</code> is not allowed in this call.<br />
<br />
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
existing_key["MyNewSubKey"] = 1.23;<br />
</pre><br />
<br />
You can also create new keys by providing a default value when reading a value. If the key doesn't already exist, the default value will be used.<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
double val = existing_key["MyNewSubKey"](1.23);<br />
</pre><br />
<br />
== Iterating over subkeys ==<br />
<br />
You can iterate over subkeys using normal iterator functions.<br />
<br />
<pre>// Iterating using standard begin/end.<br />
midas::odb exp("/Experiment");<br />
<br />
for (auto it = exp.begin(); it != exp.end(); it++) {<br />
midas::odb& subkey = *it;<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (midas::odb& subkey : exp) {<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
You can check whether a subkey exists using <code>odb::is_subkey()</code>.<br />
<br />
== Callback functions ==<br />
<br />
You may also set up callback functions that are called whenever a value<br />
changes, using the <code>odb::watch()</code> function. Note that you must call<br />
<code>cm_yield()</code> (from midas.h) periodically for this to work - deep down it<br />
is <code>cm_yield()</code> itself that calls your callback function.<br />
<br />
The callback functions can either be a "normal" function, a C++ lambda, or a member function of a C++ class.<br />
In all cases it should accept one argument - a <code>midas::odb</code> object (passed<br />
by reference) that contains the new state.<br />
<br />
<pre>// Example with a lambda:<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch([](midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
});<br />
</pre><br />
<br />
<pre>// Example with a "normal" function:<br />
void my_function(midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
}<br />
<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch(my_function);<br />
</pre><br />
<br />
<pre>// Example with a member function of a class:<br />
#include <functional><br />
<br />
void MyClass::my_function(midas::odb& arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
}<br />
<br />
void MyClass::some_initialisation_code() {<br />
// Arguments to std::bind() are: "member function to call", "object to call that function on", "placeholder for midas::odb arg"<br />
std::function<void(midas::odb&)> callback = std::bind(&MyClass::my_function, this, std::placeholders::_1);<br />
<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch(callback);<br />
}<br />
</pre><br />
<br />
== Utility functions ==<br />
<br />
There are various utility functions which can be used:<br />
<br />
==== void odb::create(const char *name, int type) ====<br />
<br />
Simple wrapper around db_create_key() to create a single key in the ODB. <code>type</code> is one of TID_xxx.<br />
<br />
==== void odb::delete_key() ====<br />
<br />
This member function of a midas::odb object deletes that object from the ODB:<br />
<br />
<pre>midas::odb o("/Some/ODB/Path");<br />
o.delete_key();<br />
</pre><br />
<br />
==== int odb::delete_key(const std::string &name) ====<br />
<br />
This function deletes a key or a subtree in the ODB passed by its path in the <code>name</code> argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.<br />
<br />
==== bool odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====<br />
<br />
These functions set and retrieve the debug flag. If the debug flag is <code>true</code> all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.<br />
<br />
== Example code ==<br />
<br />
A full working example exploring most of the features can be found in<br />
<code>odbxx/odbxx_test.cxx</code>. The test executable will be compiled as<br />
<code>build/odbxx/odbxx_test</code> (it is not installed in the `bin` directory).</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3384Sequencer2023-12-04T15:19:32Z<p>Stefan Ritt: /* Sequencer Commands */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. <br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
['test.msl', 'Yes'])<br />
<br />
After a script has been loaded, all '''PARAM''' values are extracted and placed under '''/Sequencer/Param/Defaults'''. Before a script is actually started, the default values may be modified and must be placed under '''/Sequencer/Param/Value''' before the sequencer is started. To start the script, set '''Start script''' to '''yes'''. This can be done in a combined way on a custom page via<br />
<br />
modbset(['/Sequencer/Param/Value/First parameter',<br />
'/Sequencer/Param/Value/Other parameter',<br />
'/Sequencer/Command/Start script'],<br />
['123', '456', 'Yes'])<br />
<br />
The parameter names of course have to be adjusted to the current script.<br />
<br />
The starting of the script via above '''modbset''' can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||EXIT<br />
||Exit script immediately<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script. Any ASCII result from the script is stored as a string in the MSL variable $SCRIPT_RESULT. The ''script'' can be a direct command to be executed on the shell level, or can be a shell script which then gets executed, or can be an executable. If one has to pass several variables back to the MSL script, one can use the trick to store them into an ODB file (ASCII, XML or JSON format) and then use the <code>ODBLOAD</code> command to put the values into the ODB. Try the <code>ODBSAVE</code> command to see how the ODB format should look like.<p><br />
The PATH environment is inherited from the place when the <code>msequencer</code> program is started. It can be checked with a simple shell script <code>echo $PATH</code> and then displaying the result with the MSL script command <code>message $SCRIPT_RESULT, 1<code>.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3383Sequencer2023-12-04T15:16:33Z<p>Stefan Ritt: /* Sequencer Commands */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. <br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
['test.msl', 'Yes'])<br />
<br />
After a script has been loaded, all '''PARAM''' values are extracted and placed under '''/Sequencer/Param/Defaults'''. Before a script is actually started, the default values may be modified and must be placed under '''/Sequencer/Param/Value''' before the sequencer is started. To start the script, set '''Start script''' to '''yes'''. This can be done in a combined way on a custom page via<br />
<br />
modbset(['/Sequencer/Param/Value/First parameter',<br />
'/Sequencer/Param/Value/Other parameter',<br />
'/Sequencer/Command/Start script'],<br />
['123', '456', 'Yes'])<br />
<br />
The parameter names of course have to be adjusted to the current script.<br />
<br />
The starting of the script via above '''modbset''' can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||EXIT<br />
||Exit script immediately<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script. Any ASCII result from the script is stored as a string in the MSL variable $SCRIPT_RESULT. The ''script'' can be a direct command to be executed on the shell level, or can be a shell script which then gets executed, or can be an executable. If one has to pass several variables back to the MSL script, one can use the trick to store them into an ODB file (ASCII, XML or JSON format) and then use the ''ODBLOAD'' command to put the values into the ODB. Try the ''ODBSAVE'' command to see how the ODB format should look like.<p><br />
The PATH environment is inherited from the place when the '''msequencer''' program is started. It can be checked with a simple shell script '''echo $PATH''' and then displaying the result with the MSL script command '''message $SCRIPT_RESULT, 1'''.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3382Sequencer2023-12-04T15:15:45Z<p>Stefan Ritt: /* Sequencer Commands */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. <br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
['test.msl', 'Yes'])<br />
<br />
After a script has been loaded, all '''PARAM''' values are extracted and placed under '''/Sequencer/Param/Defaults'''. Before a script is actually started, the default values may be modified and must be placed under '''/Sequencer/Param/Value''' before the sequencer is started. To start the script, set '''Start script''' to '''yes'''. This can be done in a combined way on a custom page via<br />
<br />
modbset(['/Sequencer/Param/Value/First parameter',<br />
'/Sequencer/Param/Value/Other parameter',<br />
'/Sequencer/Command/Start script'],<br />
['123', '456', 'Yes'])<br />
<br />
The parameter names of course have to be adjusted to the current script.<br />
<br />
The starting of the script via above '''modbset''' can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||EXIT<br />
||Exit script immediately<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script. Any ASCII result from the script is stored as a string in the MSL variable $SCRIPT_RESULT. The ''script'' can be a direct command to be executed on the shell level, or can be a shell script which then gets executed, or can be an executable. If one has to pass several variables back to the MSL script, one can use the trick to store them into an ODB file (ASCII, XML or JSON format) and then use the ''ODBLOAD'' command to put the values into the ODB. Try the ''ODBSAVE'' command to see how the ODB format should look like.<p><br />
The PATH environment is inherited from the place when the ''msequencer'' program is started. It can be checked with a simple shell script ''echo $PATH'' and then displaying the result with the MSL script command ''message $SCRIPT_RESULT, 1''.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3381Sequencer2023-12-04T15:14:15Z<p>Stefan Ritt: /* Sequencer Commands */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. <br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
['test.msl', 'Yes'])<br />
<br />
After a script has been loaded, all '''PARAM''' values are extracted and placed under '''/Sequencer/Param/Defaults'''. Before a script is actually started, the default values may be modified and must be placed under '''/Sequencer/Param/Value''' before the sequencer is started. To start the script, set '''Start script''' to '''yes'''. This can be done in a combined way on a custom page via<br />
<br />
modbset(['/Sequencer/Param/Value/First parameter',<br />
'/Sequencer/Param/Value/Other parameter',<br />
'/Sequencer/Command/Start script'],<br />
['123', '456', 'Yes'])<br />
<br />
The parameter names of course have to be adjusted to the current script.<br />
<br />
The starting of the script via above '''modbset''' can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||EXIT<br />
||Exit script immediately<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script. Any ASCII result from the script is stored as a string in the MSL variable $SCRIPT_RESULT. The ''script'' can be a direct command to be executed on the shell level, or can be a shell script which then gets executed, or can be an executable. If one has to pass several variables back to the MSL script, one can use the trick to store them into an ODB file (ASCII, XML or JSON format) and then use the ''ODBLOAD'' command to put the values into the ODB. Try the ''ODBSAVE'' command to see how the ODB format should look like.<p><br />
The PATH environment is inherited from the place when the ''msequencer'' program is started.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3380Sequencer2023-12-04T15:10:19Z<p>Stefan Ritt: /* Sequencer Commands */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. <br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
['test.msl', 'Yes'])<br />
<br />
After a script has been loaded, all '''PARAM''' values are extracted and placed under '''/Sequencer/Param/Defaults'''. Before a script is actually started, the default values may be modified and must be placed under '''/Sequencer/Param/Value''' before the sequencer is started. To start the script, set '''Start script''' to '''yes'''. This can be done in a combined way on a custom page via<br />
<br />
modbset(['/Sequencer/Param/Value/First parameter',<br />
'/Sequencer/Param/Value/Other parameter',<br />
'/Sequencer/Command/Start script'],<br />
['123', '456', 'Yes'])<br />
<br />
The parameter names of course have to be adjusted to the current script.<br />
<br />
The starting of the script via above '''modbset''' can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||EXIT<br />
||Exit script immediately<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script. Any ASCII result from the script is stored as a string in the MSL variable $SCRIPT_RESULT. The ''script'' can be a direct command to be executed on the shell level, or can be a shell script which then gets executed, or can be an executable. If one has to pass several variables back to the MSL script, one can use the trick to store them into an ODB file (ASCII, XML or JSON format) and then use the ''ODBLOAD'' command to put the values into the ODB. Try the ''ODBSAVE'' command to see how the ODB format should look like.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3379Sequencer2023-12-04T13:44:50Z<p>Stefan Ritt: /* Sequencer Commands */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. <br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
['test.msl', 'Yes'])<br />
<br />
After a script has been loaded, all '''PARAM''' values are extracted and placed under '''/Sequencer/Param/Defaults'''. Before a script is actually started, the default values may be modified and must be placed under '''/Sequencer/Param/Value''' before the sequencer is started. To start the script, set '''Start script''' to '''yes'''. This can be done in a combined way on a custom page via<br />
<br />
modbset(['/Sequencer/Param/Value/First parameter',<br />
'/Sequencer/Param/Value/Other parameter',<br />
'/Sequencer/Command/Start script'],<br />
['123', '456', 'Yes'])<br />
<br />
The parameter names of course have to be adjusted to the current script.<br />
<br />
The starting of the script via above '''modbset''' can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||EXIT<br />
||Exit script immediately<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script. Any result from the script is stored as a string in the variabel $SCRIPT_RESULT. The ''script'' can be a direct command to be executed on the shell level, or can be a shell script which then gets executed.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3378Sequencer2023-12-04T11:34:40Z<p>Stefan Ritt: /* Sequencer Commands */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. <br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
['test.msl', 'Yes'])<br />
<br />
After a script has been loaded, all '''PARAM''' values are extracted and placed under '''/Sequencer/Param/Defaults'''. Before a script is actually started, the default values may be modified and must be placed under '''/Sequencer/Param/Value''' before the sequencer is started. To start the script, set '''Start script''' to '''yes'''. This can be done in a combined way on a custom page via<br />
<br />
modbset(['/Sequencer/Param/Value/First parameter',<br />
'/Sequencer/Param/Value/Other parameter',<br />
'/Sequencer/Command/Start script'],<br />
['123', '456', 'Yes'])<br />
<br />
The parameter names of course have to be adjusted to the current script.<br />
<br />
The starting of the script via above '''modbset''' can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||EXIT<br />
||Exit script immediately<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script. Any result from the script is stored as a string in the variabel $SCRIPT_RESULT.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3377Sequencer2023-11-17T12:44:18Z<p>Stefan Ritt: /* Sequencer Commands */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. <br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
['test.msl', 'Yes'])<br />
<br />
After a script has been loaded, all '''PARAM''' values are extracted and placed under '''/Sequencer/Param/Defaults'''. Before a script is actually started, the default values may be modified and must be placed under '''/Sequencer/Param/Value''' before the sequencer is started. To start the script, set '''Start script''' to '''yes'''. This can be done in a combined way on a custom page via<br />
<br />
modbset(['/Sequencer/Param/Value/First parameter',<br />
'/Sequencer/Param/Value/Other parameter',<br />
'/Sequencer/Command/Start script'],<br />
['123', '456', 'Yes'])<br />
<br />
The parameter names of course have to be adjusted to the current script.<br />
<br />
The starting of the script via above '''modbset''' can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||EXIT<br />
||Exit script immediately<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&diff=3375Odbxx2023-10-21T15:52:34Z<p>Stefan Ritt: /* Creating new bits of the ODB */</p>
<hr />
<div>A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a "magic" map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.<br />
<br />
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/progs/odbxx_test.cxx odbxx_test.cxx].<br />
<br />
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]] page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.<br />
<br />
== Basic usage ==<br />
<br />
The simplest usage is like:<br />
<br />
<pre>// Grab a bit of the ODB<br />
midas::odb exp("/Experiment");<br />
<br />
// Simple read<br />
std::cout << "The current transition timeout is " << exp["Transition timeout"] << std::endl;<br />
<br />
// Make a change. The new value is automatically sent to the ODB.<br />
// Most C++ operators are supported (++, += etc), or you can do a simple<br />
// re-assignment like `exp["Transition timeout"] = 12345;`.<br />
exp["Transition timeout"] += 100;<br />
<br />
// Read the new value<br />
std::cout << "The transition timeout is now " << exp["Transition timeout"] << std::endl;<br />
</pre><br />
<br />
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:<br />
<br />
<pre>int curr_timeout = exp["Transition timeout"];</pre><br />
<br />
'''Note''': The ODB directory you connect to ("/Experiment" in the above example), has to start with a "/" to tell the midas::odb object<br />
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.<br />
<br />
== Automatic refreshing ==<br />
<br />
You may temporarily disable the automatic updating to/from the ODB<br />
using <code>odb::set_auto_refresh_write(false)</code> and <code>odb::set_auto_refresh_read(false)</code>.<br />
<br />
If auto-refresh is enabled (the default), your new values are sent to<br />
the ODB as soon as you touch the value in the <code>midas::odb</code> object. The ODB<br />
is queried for new values whenever you access the value. In the above<br />
example, the ODB is queried 4 times (during construction of <code>exp</code>, and<br />
each time <code>exp["Transition timeout"]</code> is mentioned), and written to 1<br />
time (when <code>exp["Transition timeout"]</code> is assigned to).<br />
<br />
See the [[#Callback functions]] section below for details on how to have a function<br />
called when a value changes.<br />
<br />
== Arrays/vectors ==<br />
<br />
ODB arrays are represented by std vectors.<br />
<br />
You can access/edit individual elements using []:<br />
<br />
<pre>odb["Example"][1] = 1.2;</pre><br />
<br />
You can completely re-assign content using a std::vector or std::array:<br />
<br />
<pre>std::vector<float> vec = std::vector<float>(10);<br />
odb["Example"] = vec;<br />
</pre><br />
<br />
You can resize arrays using <code>odb::resize()</code>. If the existing array is longer,<br />
it will be truncated; if shorter it will be extended with default values<br />
(0 or an empty string).<br />
<br />
<pre>odb["Example"].resize(5); // Now is 5 elements long</pre><br />
<br />
Note that arithmetic operators are supported for arrays, and will apply<br />
the operation to ALL ELEMENTS IN THE ARRAY:<br />
<br />
<pre>// Create the vector<br />
std::vector<float> vec = std::vector<float>(2);<br />
vec[0] = 3;<br />
vec[1] = 5;<br />
<br />
// Assign in ODB<br />
odb["Example"] = vec;<br />
<br />
// Multiply ALL elements by 2<br />
odb["Example"] *= 2;<br />
<br />
// odb["Example"] now contains {6, 10}.<br />
</pre><br />
<br />
You can directly iterate over arrays/vectors:<br />
<br />
<pre>// Iterating using standard begin/end.<br />
for (auto it = o["Int Array"].begin(); it != o["Int Array"].end(); it++) {<br />
int val = *it;<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (int val : o["Int Array"]) {<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
== Strings ==<br />
<br />
Strings in the ODB are returned as std::string (unlike the midas.h <code>db_get_value()</code><br />
family of functions, where strings are returned as char*). You may have vectors of strings.<br />
<br />
== Creating new bits of the ODB ==<br />
<br />
You can automatically create bits of the ODB by passing a struct to the<br />
<code>midas::odb</code> constructor, then calling <code>odb::connect()</code>, like:<br />
<br />
<pre>// Define the ODB structure<br />
midas::odb new_bit = {<br />
{"Int32 Key", 42},<br />
{"Bool Key", true},<br />
{"Subdir", {<br />
{"Float key", 1.2f}, // floats must be explicitly specified<br />
}},<br />
{"Int Array", {1, 2, 3}},<br />
{"Double Array", {1.2, 2.3, 3.4}},<br />
{"String Array", {"Hello1", "Hello2", "Hello3"}},<br />
{"Large Array", std::array<int, 10>{} }, // array with explicit size<br />
{"Large String", std::string(63, '\0') }, // string with explicit size<br />
};<br />
<br />
// Then sync the structure. This function<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings");<br />
<br />
// If you make the `write_defaults` argument true, then the function<br />
// - overwrites the value of any keys that are in the ODB with the value in your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings", true);<br />
<br />
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
// - deletes any keys that are in the ODB but not your code<br />
// - updates the order of keys in the ODB to match your code<br />
o.connect_and_fix_structure("/Test/Settings");<br />
</pre><br />
<br />
Note that the ODB path in teh odb::connect() call must start with a '/'. The ODB root path like <code>o.connect("/");</code> is not allowed in this call.<br />
<br />
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
existing_key["MyNewSubKey"] = 1.23;<br />
</pre><br />
<br />
You can also create new keys by providing a default value when reading a value. If the key doesn't already exist, the default value will be used.<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
double val = existing_key["MyNewSubKey"](1.23);<br />
</pre><br />
<br />
== Iterating over subkeys ==<br />
<br />
You can iterate over subkeys using normal iterator functions.<br />
<br />
<pre>// Iterating using standard begin/end.<br />
midas::odb exp("/Experiment");<br />
<br />
for (auto it = exp.begin(); it != exp.end(); it++) {<br />
midas::odb& subkey = *it;<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (midas::odb& subkey : exp) {<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
You can check whether a subkey exists using <code>odb::is_subkey()</code>.<br />
<br />
== Callback functions ==<br />
<br />
You may also set up callback functions that are called whenever a value<br />
changes, using the <code>odb::watch()</code> function. Note that you must call<br />
<code>cm_yield()</code> (from midas.h) periodically for this to work - deep down it<br />
is <code>cm_yield()</code> itself that calls your callback function.<br />
<br />
The callback functions can either be a "normal" function or a C++ lambda.<br />
In either case, it should accept one argument - a <code>midas::odb</code> object (passed<br />
by reference) that contains the new state.<br />
<br />
<pre>// Example with a lambda:<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch([](midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
});<br />
</pre><br />
<br />
<pre>// Example with a "normal" function:<br />
void my_function(midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
}<br />
<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch(my_function);<br />
</pre><br />
<br />
== Utility functions ==<br />
<br />
There are various utility functions which can be used:<br />
<br />
==== void odb::create(const char *name, int type) ====<br />
<br />
Simple wrapper around db_create_key() to create a single key in the ODB. <code>type</code> is one of TID_xxx.<br />
<br />
==== void odb::delete_key() ====<br />
<br />
This member function of a midas::odb object deletes that object from the ODB:<br />
<br />
<pre>midas::odb o("/Some/ODB/Path");<br />
o.delete_key();<br />
</pre><br />
<br />
==== int odb::delete_key(const std::string &name) ====<br />
<br />
This function deletes a key or a subtree in the ODB passed by its path in the <code>name</code> argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.<br />
<br />
==== bool odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====<br />
<br />
These functions set and retrieve the debug flag. If the debug flag is <code>true</code> all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.<br />
<br />
== Example code ==<br />
<br />
A full working example exploring most of the features can be found in<br />
<code>odbxx/odbxx_test.cxx</code>. The test executable will be compiled as<br />
<code>build/odbxx/odbxx_test</code> (it is not installed in the `bin` directory).</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&diff=3374Odbxx2023-10-20T14:28:11Z<p>Stefan Ritt: /* Creating new bits of the ODB */</p>
<hr />
<div>A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a "magic" map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.<br />
<br />
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/progs/odbxx_test.cxx odbxx_test.cxx].<br />
<br />
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]] page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.<br />
<br />
== Basic usage ==<br />
<br />
The simplest usage is like:<br />
<br />
<pre>// Grab a bit of the ODB<br />
midas::odb exp("/Experiment");<br />
<br />
// Simple read<br />
std::cout << "The current transition timeout is " << exp["Transition timeout"] << std::endl;<br />
<br />
// Make a change. The new value is automatically sent to the ODB.<br />
// Most C++ operators are supported (++, += etc), or you can do a simple<br />
// re-assignment like `exp["Transition timeout"] = 12345;`.<br />
exp["Transition timeout"] += 100;<br />
<br />
// Read the new value<br />
std::cout << "The transition timeout is now " << exp["Transition timeout"] << std::endl;<br />
</pre><br />
<br />
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:<br />
<br />
<pre>int curr_timeout = exp["Transition timeout"];</pre><br />
<br />
'''Note''': The ODB directory you connect to ("/Experiment" in the above example), has to start with a "/" to tell the midas::odb object<br />
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.<br />
<br />
== Automatic refreshing ==<br />
<br />
You may temporarily disable the automatic updating to/from the ODB<br />
using <code>odb::set_auto_refresh_write(false)</code> and <code>odb::set_auto_refresh_read(false)</code>.<br />
<br />
If auto-refresh is enabled (the default), your new values are sent to<br />
the ODB as soon as you touch the value in the <code>midas::odb</code> object. The ODB<br />
is queried for new values whenever you access the value. In the above<br />
example, the ODB is queried 4 times (during construction of <code>exp</code>, and<br />
each time <code>exp["Transition timeout"]</code> is mentioned), and written to 1<br />
time (when <code>exp["Transition timeout"]</code> is assigned to).<br />
<br />
See the [[#Callback functions]] section below for details on how to have a function<br />
called when a value changes.<br />
<br />
== Arrays/vectors ==<br />
<br />
ODB arrays are represented by std vectors.<br />
<br />
You can access/edit individual elements using []:<br />
<br />
<pre>odb["Example"][1] = 1.2;</pre><br />
<br />
You can completely re-assign content using a std::vector or std::array:<br />
<br />
<pre>std::vector<float> vec = std::vector<float>(10);<br />
odb["Example"] = vec;<br />
</pre><br />
<br />
You can resize arrays using <code>odb::resize()</code>. If the existing array is longer,<br />
it will be truncated; if shorter it will be extended with default values<br />
(0 or an empty string).<br />
<br />
<pre>odb["Example"].resize(5); // Now is 5 elements long</pre><br />
<br />
Note that arithmetic operators are supported for arrays, and will apply<br />
the operation to ALL ELEMENTS IN THE ARRAY:<br />
<br />
<pre>// Create the vector<br />
std::vector<float> vec = std::vector<float>(2);<br />
vec[0] = 3;<br />
vec[1] = 5;<br />
<br />
// Assign in ODB<br />
odb["Example"] = vec;<br />
<br />
// Multiply ALL elements by 2<br />
odb["Example"] *= 2;<br />
<br />
// odb["Example"] now contains {6, 10}.<br />
</pre><br />
<br />
You can directly iterate over arrays/vectors:<br />
<br />
<pre>// Iterating using standard begin/end.<br />
for (auto it = o["Int Array"].begin(); it != o["Int Array"].end(); it++) {<br />
int val = *it;<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (int val : o["Int Array"]) {<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
== Strings ==<br />
<br />
Strings in the ODB are returned as std::string (unlike the midas.h <code>db_get_value()</code><br />
family of functions, where strings are returned as char*). You may have vectors of strings.<br />
<br />
== Creating new bits of the ODB ==<br />
<br />
You can automatically create bits of the ODB by passing a struct to the<br />
<code>midas::odb</code> constructor, then calling <code>odb::connect()</code>, like:<br />
<br />
<pre>// Define the ODB structure<br />
midas::odb new_bit = {<br />
{"Int32 Key", 42},<br />
{"Bool Key", true},<br />
{"Subdir", {<br />
{"Float key", 1.2f}, // floats must be explicitly specified<br />
}},<br />
{"Int Array", {1, 2, 3}},<br />
{"Double Array", {1.2, 2.3, 3.4}},<br />
{"String Array", {"Hello1", "Hello2", "Hello3"}},<br />
{"Large Array", std::array<int, 10>{} }, // array with explicit size<br />
{"Large String", std::string(63, '\0') }, // string with explicit size<br />
};<br />
<br />
// Then sync the structure. This function<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings");<br />
<br />
// If you make the `write_defaults` argument true, then the function<br />
// - overwrites the value of any keys that are in the ODB with the value in your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings", true);<br />
<br />
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
// - deletes any keys that are in the ODB but not your code<br />
// - updates the order of keys in the ODB to match your code<br />
o.connect_and_fix_structure("/Test/Settings");<br />
</pre><br />
<br />
Note that the ODB path in teh odb::connect() call must start and end with a '/'. The ODB root path like <code>o.connect("/");</code> is not allowed in this call.<br />
<br />
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
existing_key["MyNewSubKey"] = 1.23;<br />
</pre><br />
<br />
You can also create new keys by providing a default value when reading a value. If the key doesn't already exist, the default value will be used.<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
double val = existing_key["MyNewSubKey"](1.23);<br />
</pre><br />
<br />
== Iterating over subkeys ==<br />
<br />
You can iterate over subkeys using normal iterator functions.<br />
<br />
<pre>// Iterating using standard begin/end.<br />
midas::odb exp("/Experiment");<br />
<br />
for (auto it = exp.begin(); it != exp.end(); it++) {<br />
midas::odb& subkey = *it;<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (midas::odb& subkey : exp) {<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
You can check whether a subkey exists using <code>odb::is_subkey()</code>.<br />
<br />
== Callback functions ==<br />
<br />
You may also set up callback functions that are called whenever a value<br />
changes, using the <code>odb::watch()</code> function. Note that you must call<br />
<code>cm_yield()</code> (from midas.h) periodically for this to work - deep down it<br />
is <code>cm_yield()</code> itself that calls your callback function.<br />
<br />
The callback functions can either be a "normal" function or a C++ lambda.<br />
In either case, it should accept one argument - a <code>midas::odb</code> object (passed<br />
by reference) that contains the new state.<br />
<br />
<pre>// Example with a lambda:<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch([](midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
});<br />
</pre><br />
<br />
<pre>// Example with a "normal" function:<br />
void my_function(midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
}<br />
<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch(my_function);<br />
</pre><br />
<br />
== Utility functions ==<br />
<br />
There are various utility functions which can be used:<br />
<br />
==== void odb::create(const char *name, int type) ====<br />
<br />
Simple wrapper around db_create_key() to create a single key in the ODB. <code>type</code> is one of TID_xxx.<br />
<br />
==== void odb::delete_key() ====<br />
<br />
This member function of a midas::odb object deletes that object from the ODB:<br />
<br />
<pre>midas::odb o("/Some/ODB/Path");<br />
o.delete_key();<br />
</pre><br />
<br />
==== int odb::delete_key(const std::string &name) ====<br />
<br />
This function deletes a key or a subtree in the ODB passed by its path in the <code>name</code> argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.<br />
<br />
==== bool odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====<br />
<br />
These functions set and retrieve the debug flag. If the debug flag is <code>true</code> all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.<br />
<br />
== Example code ==<br />
<br />
A full working example exploring most of the features can be found in<br />
<code>odbxx/odbxx_test.cxx</code>. The test executable will be compiled as<br />
<code>build/odbxx/odbxx_test</code> (it is not installed in the `bin` directory).</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&diff=3372Custom Page2023-10-15T14:09:13Z<p>Stefan Ritt: /* modbhbar */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}<br />
<br />
= Purpose =<br />
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].<br />
<br />
= Introduction =<br />
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.<br />
<br />
We note that MIDAS has provided a number of different ways of providing custom pages over the years. A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.<br />
<br />
= Examples of Custom Pages =<br />
<br />
Click on the thumbnails to enlarge.<br />
<br />
{|<br />
|-<br />
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || '''Example 1'''<br />
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of "fills" and "labels". Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).<br />
|-<br />
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || '''Example 2'''<br />
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a "virtual" X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.<br />
<br />
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. <br />
<br />
For details using Xvfb server, please contact Ryu Sawada <sawada@icepp.s.u-tokyo.ac.jp>.<br />
|-<br />
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || '''Example 3'''<br />
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value. <br />
|}<br />
<br />
= Access a Custom Page from the Regular MIDAS pages =<br />
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.<br />
<br />
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in<br />
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.<br />
See [[Custom Page Features#Resource files]] for more information.<br />
<br />
If the key {{Odbpath|path=/Custom/myPage&}} (see Note) is created, e.g.<br />
odbedit> ls /Custom<br />
Path /home/expt/online/custom<br />
myPage& mypage.html<br />
<br />
the custom link on the left navigation bar will be <code>myPage</code> and the URL for the resulting custom page will be of the form <code>http://myhost.mydomain:myport/cmd=?Custom&page=myPage</code> (see also [[mhttpd#usage]]). <br />
Clicking on <code>myPage</code> will display the custom page in the same window.<br />
<br />
;Note<br />
: Without the "&" symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.<br />
<br />
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.<br />
<br />
odbedit> ls -r /Custom<br />
Path /home/expt/online/custom<br />
myPage& mypage.html<br />
Calorimeter<br />
HV hv.html<br />
Rates rates.html<br />
Baeam<br />
Beamline beamline.html<br />
Accelerator accel.html<br />
<br />
The pages then include the subdirectory in the URL, like <br />
<br />
http://localhost:8080?cmd=custom&page=beam/Beamline<br />
<br />
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as<br />
<br />
<body class="mcss" onload="mhttpd_init('Calorimeter/HV');"><br />
<br />
in order to keep the submenu open after you select the custom page.<br />
<br />
= How to write a custom page =<br />
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.<br />
<br />
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions. The advantages of using these modb* javascript functions are<br />
<br />
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.<br />
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.<br />
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.<br />
<br />
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient. In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call). It will also require more coding to implement the custom page with only MjsonRPC calls.<br />
<br />
== How to use the standard MIDAS navigation bars on your custom page == <br />
<br />
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax <br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');"><br />
<br />
<!-- header and side navigation will be filled in mhttpd_start --><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
ADD YOUR HTML/JS CODE here...<br />
</div><br />
</pre><br />
<br />
The call <code>mhttpd_init('myPage')</code> is executed when the page is loaded, and <code>myPage</code> is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.<br />
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].<br />
<br />
= modb* Javascript scheme = <br />
<br />
The general scheme of the custom page scheme is to write <code>&lt;div class="modb..."&gt;&lt;/div&gt;</code> or <code>&lt;span class="modb..."&gt;&lt;/span&gt;</code> tags with special class names, most of them starting with "modb..." ("MIDAS-ODB"). Use a <code>&lt;div&gt;</code> tag if you want the element to appear in a separate line, and use the <code>&lt;span&gt;</code> tag if you want to display the element in-line. The following description uses only <code>&lt;div&gt;</code> tags, but all of them can be changed to <code>&lt;span&gt;</code>. All HTML tags with "modb..." names are scanned by the <code>mhttp_init('name')</code> function upon page load, and their inner contents is replaced by the requested ODB value or some graphics. The contents is then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to <code>mhttpd_init('name', interval)</code> where "interval" is in milliseconds.<br />
<br />
== modbset(path, value) ==<br />
<br />
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:<br />
<br />
<code><br />
modbset("odb path", value)<br />
</code><br />
<br />
<code><br />
modbset(["odb path1", "odb path2", ...], [value1, value2, ...])<br />
</code><br />
<br />
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.<br />
<br />
== modb ==<br />
<br />
This special HTML div tag (abbreviation stands for Midas ODB) <code>&lt;div class="modb" data-odb-path="/Some/Path" onchange="func()"&gt;</code> can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &lt;script&gt;...&lt;/script&gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.<br />
<br />
The current value of the ODB entry is available inside the "onchange" function as '''this.value'''. Following tag will call a function which logs the current run number in the JavaScript console window:<br />
<br /><br /><br />
<code>&lt;div class="modb" data-odb-path="/Runinfo/Run number" onchange="func(this.value)"&gt;<br />
<br />
&lt;script&gt;function func(value) {<br />
console.log(value);<br />
}&lt;/script&gt;<br />
</code><br />
<br /><br /><br />
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to '''this.value''' as a JavaSctipt object such as<br />
<br /><br /><br />
<code>&lt;div class="modb" data-odb-path="/Runinfo" onchange="func(this.value)"&gt;<br />
<br />
&lt;script&gt;function func(value) {<br />
console.log(value["run number"]);<br />
}&lt;/script&gt;<br />
<br /><br />
</code><br />
<br /><br /><br />
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket '''value["run number"]''' as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as '''value.state''' for /Runinfo/State for example.<br />
<br />
== modbvalue ==<br />
<br />
This special HTML div tag (abbreviation stands for "Midas ODB VALUE") <br />
<br /><br /><br />
<code>&lt;div class="modbvalue" data-odb-path="/Some/Path"&gt;&lt;/div&gt;</code> <br />
<br /><br /><br />
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:<br />
<br />
{| class="wikitable"<br />
|+ Table 1: List of valid options for modbvalue tag<br />
|-<br />
! Option !! Example !! Meaning<br />
|-<br />
| data-name || class="modbvalue" || Tells the framework to replace this tag with an ODB value<br />
|-<br />
| data-odb-path || data-odb-path = "/Runinfo/Run number" || Path to the value in the ODB<br />
|-<br />
| data-odb-editable || data-odb-editable="1" || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.<br />
|-<br />
| data-format || data-format="f3" || Specify format of data shown. See Table 2 below for options.<br />
|-<br />
| data-size || data-size="8" || Specify size (in chars) of edit box if one modifies the value. Default is 10.<br />
|-<br />
| data-formula || data-formula="2*x+3" || Specify an optional formula to process with the current ODB value stored in x<br />
|-<br />
| data-validate || data-validate="func" || Specify an optional validation function which gets called before submitting data (see below)<br />
|}<br />
<br />
=== Validation ===<br />
<br />
Before a modified value is submitted to the ODB, an optional validation function can be called via the <code>data-validate</code> option. The function<br />
will be called with the current value and a reference to the current modbvalue element. If the function returns <code>false</code>, then the value<br />
is not sent to the ODB.<br />
<br />
Following example shows a function which just rejects the submission of values above 1000:<br />
<br />
&lt;div class="modbvalue" ... data-odb-editable="1" data-validate="my_validate"&gt;&lt;/div&gt;<br />
<br />
&lt;script src="controls.js"&gt;&lt;/script&gt; &lt;!-- needed of dlgAlert() --&gt;<br />
&lt;script&gt;<br />
function my_validate(value, element) {<br />
if (value > 1000) {<br />
dlgAlert("Value cannot be above 1000");<br />
return false;<br />
}<br />
return true;<br />
}<br />
&lt;/script&gt;<br />
<br />
Following function corrects the return value to 1000 if it's above 1000:<br />
<br />
&lt;script&gt;<br />
function my_validate2(value, element) {<br />
if (value > 1000) {<br />
element.childNodes[0].value = 1000;<br />
return true;<br />
}<br />
&lt;/script&gt;<br />
<br />
=== Confirmation ===<br />
<br />
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. <br />
This is done with the option <code>data-confirm=&lt;string&gt;</code>. A dialog box is then shown with the <code>&lt;string&gt;</code> as the main text and two buttons with "OK" and "Cancel".<br />
Hitting "OK" finally writes the value to the ODB, hitting "Cancel" keeps the old value.<br />
<br />
Following example shows an example:<br />
<br />
&lt;div class="modbvalue" ... data-odb-editable="1" data-confirm="Are you sure to change the value?"&gt;&lt;/div&gt;<br />
<br />
Following dialog box is then showed:<br />
<br />
[[File:Confirm.png|frame|left|Optional confirm dialog]]<br />
<br />
<br clear=all><br />
<br />
=== Formatting ===<br />
<br />
Table 2 below lists the format specifiers supported with a modbvalue tag <code>data-format="..."</code>:<br />
<br />
{| class="wikitable"<br />
|+ Table 2: Format specifiers for modbvalue tag <code>data-format="..."</code>.<br />
|-<br />
! Option !! Valid for* !! Example !! Meaning<br />
|-<br />
| %d || int || 1234 || Shows a number in decimal encoding<br />
|-<br />
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator<br />
|-<br />
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding<br />
|-<br />
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like <code>data-format="%d / %x"</code> to produce <code>1234 / 0x4D2</code><br />
|-<br />
| %f&lt;x&gt; || float || 1.234 || Shows a floating point number with &lt;x&gt; digits after the decimal. A value of f0 shows only the integer part.<br />
|-<br />
| %p&lt;x&gt; || float || 1.23 || Shows a floating point number with &lt;x&gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012<br />
|-<br />
| %e&lt;x&gt; || float || 1.23e-05 || Shows a floating point number with &lt;x&gt; digits after the decimal in exponential format. Useful for small number such as pressures.<br />
|-<br />
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.<br />
|-<br />
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats<br />
|}<br />
<br />
* Note that valid for "int" means all integral ODB types (regardless of size or signed/unsigned) and valid for "float" means both float and double.<br />
<br />
== modbbutton ==<br />
<br />
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the "Run number" to 100, one can use the following tag:<br />
<br /><br /><br />
<code>&lt;button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="100" data-validate="my_validate"&gt;[Button Text]&lt;/button&gt;</code><br />
<br />
<br />
The optional <code>data-validate</code> function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.<br />
<br />
== modbbox ==<br />
<br />
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the '''data-color''' is used, otherwise the '''data-background-color''' is used<br />
<br /><br /><br />
<code>&lt;div class="modbbox" data-odb-path="/Logger/Write Data" data-formula="x > 0" style="width: 30px; height: 30px; border: 1px solid black" data-color="lightgreen" data-background-color="red"&gt;&lt;/div&gt;</code><br />
<br />
Optionally, a <code>data-formula</code> can be specified. The formula sees the ODB value in the variable <code>x</code>, and can do any boolean operation. If the result of this is true, then the box gets the <code>data-color</code>, otherwise the <code>data-background-color</code>.<br />
Examples for these formulas are <code>x > 10</code> for a comparison or <code>x & 1</code> which will do a bitwise AND operation and is true only for odd numbers.<br />
<br />
== modbcheckbox ==<br />
<br />
This tag generates a check box which can set a certain ODB entry to true or false. To set the "Write data" flag for the logger true or false, one can use the following tag:<br />
<br /><br /><br />
<code>&lt;input type="checkbox" class="modbcheckbox" data-odb-path="/Logger/Write data" data-validate="my_validate" /&gt;</code><br />
<br /><br /><br />
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional <code>data-validate</code> function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for <code>modbvalue</code> tag.<br />
<br />
== modbselect ==<br />
<br />
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under <code>/Runinfo/Run number</code>.<br />
<br />
&lt;select class="modbselect" data-odb-path="/Runinfo/Run number"&gt;<br />
&lt;option value="1"&gt;1&lt;/option&gt;<br />
&lt;option value="5"&gt;5&lt;/option&gt;<br />
&lt;option value="10"&gt;10&lt;/option&gt;<br />
&lt;/select&gt;<br />
<br />
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify <code>data-auto-options="1"</code> to enable this behaviour. For ODB key <code>x</code>, you will need to create an ODB entry called <code>Options x</code> in the same directory; this "options" key should be a list, with each element in the list being an allowable option.<br />
<br />
<!-- Will read options from "/Equipment/Example/Settings/Options Something" in this case --><br />
&lt;select class="modbselect" data-odb-path="/Equipment/Example/Settings/Something" data-auto-options="1"&gt;<br />
&lt;/select&gt;<br />
<br />
The benefit of the <code>data-auto-options="1"</code> approach is that the same options will be shown on the regular ODB browser webpage. <br />
<br />
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.<br />
<br />
== modbhbar ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbhbar" style="width: 500px; height: 18px; color: lightgreen;" data-odb-path="/Runinfo/Run number" data-max-value="10" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 500px" || Total width of the horizontal bar || Yes<br />
|-<br />
| style="height: 18px" || Height of the horizontal bar || Yes<br />
|-<br />
| style="color: red" || Color of horizontal bar || Transparent if not present<br />
|-<br />
| style="background-color: red" || Background color of horizontal bar || Transparent if not present<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-min-value || Left limit of bar range || 0 if not present<br />
|-<br />
| data-max-value || Right limit of bar range || 1 if not present<br />
|-<br />
| data-log || Logarithmic display || No<br />
|-<br />
| data-print-value || If "1", data value is shown as text overlay || No<br />
|-<br />
| data-format || Specify format of data shown. See Table 2 above for options || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
== mhaxis ==<br />
<br />
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="mhaxis" style="width: 500px; height: 22px;" data-min-value="0" data-max-value="10" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a horizontal axis next to the bar. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 500px" || Total width of the axis, must match the width of the horizontal bar || Yes<br />
|-<br />
| style="height: 18px" || Height of the axis, must be enough to display labels || Yes<br />
|-<br />
| style="vertical-align: top" || Must be "top" if the axis is below the bar || "bottom" if not given<br />
|-<br />
| data-min-value || Left limit of axis range || Yes<br />
|-<br />
| data-max-value || Right limit of axis range || Yes<br />
|-<br />
| data-log || Logarithmic display || No<br />
|}<br />
<br />
== modbvbar ==<br />
<br />
Same as <code>modbhbar</code>, except the bar grows vertically instead of horizontally.<br />
<br />
== mvaxis ==<br />
<br />
Same as <code>mhaxis</code>, except the axis is shown vertically instead of horizontally.<br />
<br />
== modbthermo ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbthermo" style="width: 30px; height: 100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30" data-color="blue" data-print-value="1" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it's good for testing. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 30px" || Width of the thermometer || Yes<br />
|-<br />
| style="height: 100px" || Total height of the thermometer || Yes<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-max-value || Upper range || Yes<br />
|-<br />
| data-min-value || Lower range || 0 if not present<br />
|-<br />
| data-color || Color of thermometer || Black if not present<br />
|-<br />
| data-background-color || Color of thermometer background || Transparent if not present<br />
|-<br />
| data-print-value || If "1", data value is shown below the thermometer || No<br />
|-<br />
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
== modbgauge ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbgauge" style="width: 100px; height: 50px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10" data-color="darkgreen"&gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a circular gauge ranging from 0 to 10. Depending on the ODB value "Run number". Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 100px" || Width of the gauge || Yes<br />
|-<br />
| style="height: 50px" || Total height of the gauge || Yes<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-max-value || Upper range || Yes<br />
|-<br />
| data-min-value || Lower range || 0 if not present<br />
|-<br />
| data-color || Color of gauge || Black if not present<br />
|-<br />
| data-background-color || Color of gauge background || Transparent if not present<br />
|-<br />
| data-print-value || If "1", data value is shown below the gauge || No<br />
|-<br />
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No<br />
|-<br />
| data-scale || If "1", the min and max values of the range are shown below the gauge || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.<br />
<br />
== Changing properties of controls dynamically ==<br />
<br />
All custom controls can be configured to call a user's function when the control is first set up, or when the value changes. This is done by specifying the '''onload''' and/or '''onchange''' functions.<br />
<br />
* '''onload''' is called only once, when the control's value is first read from the ODB.<br />
* '''onchange''' is called each time the value in the ODB changes.<br />
<br />
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:<br />
<br />
<code>onchange="this.dataset.color=this.value > 30?'red':'blue';"</code><br />
<br />
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:<br />
<br />
<pre><br />
<script><br />
function check_therm(elem) {<br />
if (elem.value > 30) {<br />
elem.dataset.color = "red";<br />
} else {<br />
elem.dataset.color = "blue";<br />
}<br />
};<br />
</script><br />
<br />
....<br />
<br />
<div class="modbthermo" style="width: 30px; height: 100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30" data-color="blue" data-print-value="1" onchange="check_therm(this)"></div><br />
<br />
</pre><br />
<br />
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.<br />
<br />
If you want the same function to be called for both onload and onchange, you could set <code>onload="onchange()"</code> and have the "real" function in onchange.<br />
<br />
== Changing values of indicators programmatically ==<br />
<br />
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. <br />
<br />
For such cases, the <code>data-odb-path</code> attribute can be removed and the the display value can be changed via JavaScript code like following:<br />
<br />
<pre><br />
<br />
<div id="mythermo" class="modbthermo" data-min-value="-10" data-max-value="30"></div><br />
<br />
...<br />
let t = document.getElementById("mythermo");<br />
t.setValue(15);<br />
...<br />
<br />
</pre><br />
<br />
= mjshistory =<br />
<br />
Custom pages can contain one or more specific history panels usually shown on the "History" page. This makes it easy to combine current readings of values together with the history of these values.<br />
<br />
To enable interactive history panels, following lines have to be added to your custom page:<br />
<br />
Inisde the <head> tag:<br />
<br />
<script src="mhistory.js"></script><br />
<br />
Inside the <body> tag:<br />
<br />
<body ... onload="mhistory_init();"><br />
<br />
The following tag:<br />
<br />
&lt;div class="mjshistory" data-group="<group>" data-panel="<panel>" style="width: 320px; height: 200px;" &gt;&lt;/div&gt;<br />
<br />
shows a history panel defined in the ODB under /History/Display/&lt;group&gt;/&lt;panel&gt; (replace &lt;group&gt;/&lt;panel&gt; with groups and panels from your experiment).<br />
<br />
Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
| data-group || ODB group of history. Has to match a group under /History/Display || Yes<br />
|-<br />
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&lt;group&gt;/ || Yes<br />
|-<br />
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&lt;group&gt;/&lt;panel&gt;/Timescale is used. || No<br />
|-<br />
| style="width: 320px" || Width of the history panel || No<br />
|-<br />
| style="height: 200px" || Height of the history panel || No<br />
|-<br />
| style="border: 1px solid black" || Border around the history panel || No<br />
|}<br />
<br />
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.<br />
<br />
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users <br />
the possibility to show the history of that variable. Just put a button next to the value and call '''mhistory_dialog(&lt;group&gt;, &lt;panel&gt;)''' from that button like:<br />
<br />
&lt;span class="modbvalue" data-odb-path="/Some/Path"&gt;&lt;/span&gt;<br />
<button onclick="mhistory_dialog('group','panel')"><img src="icons/activity.svg"></button><br />
<br />
The history panel will then be opened when the user clicks the button:<br />
<br />
[[File:History dialog.png|frame|left|Floating history dialog]]<br />
<br />
<br clear=all><br />
<br />
If one wants to avoid the definition of a history panel in the ODB, a "direct variable plot" can be done with the function '''mhistory_dialog_var(&lt;variable&gt;)''' where &lt;variable&gt; has the format like the variable definition in the ODB (e.g. "System:Trigger per sec."). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as '''mhistory_dialog_var("System:Trigger per sec.", {"Timescale": "24h"});'''<br />
<br />
A full example of a custom page with a history panel is shown below.<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css" type="text/css"><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="controls.js"></script><br />
<script src="mhistory.js"></script><br />
</head><br />
<body onload="mhttpd_init('history_example'); mhistory_init();"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div> <br />
<div id="mmain"><br />
<div class="mjshistory" data-group="EPICS" data-panel="Logging" style="width: 500px; height: 300px;" ></div><br />
</div><br />
</body><br />
</html><br />
</pre><br />
<br />
= Complete Example =<br />
<br />
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:<br />
<br />
<pre><br />
/Custom<br />
Path /midas/resources<br />
Test a_example.html<br />
</pre><br />
<br />
== Code ==<br />
The file '''a_example.html''' contains the following code:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html class="mcss"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<title>Example</title><br />
<br />
<style><br />
.mtable td { padding: 10px; }<br />
</style><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('Example');"><br />
<br />
<!-- header and side navigation will be filled in mhttpd_init --><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<table class="mtable"><br />
<tr><br />
<th colspan="2" class="mtableheader">Status</th><br />
</tr><br />
<tr><br />
<td style="width: 200px;"><br />
Run number:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Run number" data-odb-editable="1"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Last run start:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Start time"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Last run stop:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Stop time"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Check box:<br />
</td><br />
<td><br />
<!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --><br />
<input type="checkbox" class="modbcheckbox" data-odb-path="/Logger/Write data"></input><br />
<br />
<div class="modb" data-odb-path="/Logger/Write data" onchange="dlgAlert('Flag has changed');"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Color box:<br />
</td><br />
<td><br />
<!-- box changes color according to /Logger/Write data --><br />
<div class="modbbox" style="width: 30px; height: 30px;" data-odb-path="/Logger/Write data"<br />
data-color="lightgreen" data-background-color="red"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Horizontal bars:<br />
</td><br />
<td><br />
<div class="modbhbar" style="width:300px;height:20px;color:orange;" data-odb-path="/Runinfo/Run number"<br />
data-max-value="10" data-print-value="1"></div><br /><br />
<br />
<div class="mhaxis" style="width:500px;height:22px;" data-min-value="0" data-max-value="10"></div><br />
<div class="modbhbar" style="width: 500px; height: 18px;color:lightblue" data-odb-path="/Runinfo/Run number"<br />
data-max-value="10"></div><br /><br />
<br />
<div class="modbhbar" style="width: 200px; height: 10px;color:lightgreen;background-color:white"<br />
data-odb-path="/Runinfo/Run number" data-min-value="0.1" data-max-value="10" data-log="1"></div><br />
<div class="mhaxis" style="width:200px;height:22px;vertical-align:top;" data-min-value="0.1"<br />
data-max-value="10" data-line="0" data-log="1"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Vertical bars:<br />
</td><br />
<td><br />
<span class="mvaxis" style="width:100px;height:200px;text-align:right;" data-min-value="0" data-max-value="20"></span><span class="modbvbar"<br />
style="width:20px;height:200px;color:yellow;" data-odb-path="/Runinfo/Run number"<br />
data-min-value="0" data-max-value="20"></span><br />
<span class="modbvbar" style="width:10px;height:200px;vertical-align:top;color:red" data-odb-path="/Runinfo/Run number" data-min-value="0.1"<br />
data-max-value="10" data-log="1"></span><span class="mvaxis" style="width:100px;height:200px;text-align:left;" data-min-value="0.1"<br />
data-max-value="10" data-log="1"></span><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td><br />
Thermometer:<br />
</td><br />
<td><br />
<div class="modbthermo" style="width:30px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="darkgreen"></div><br />
<div class="modbthermo" style="width:60px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-scale="1"<br />
onchange="this.dataset.color=this.value > 9?'red':'blue';"></div><br />
<div class="modbthermo" style="width:30px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-background-color="white" data-value="1"></div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td><br />
Gauges:<br />
</td><br />
<td><br />
<div class="modbgauge" style="width:100px;height:50px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10"<br />
data-color="darkgreen" data-background-color="lightgrey" ></div><br />
<div class="modbgauge" style="width:100px;height:65px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10"<br />
data-color="red" data-value="1" data-scale="1"></div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td colspan="2" style="text-align: center;"><br />
<!-- div around image with "relative" position as anchor for labels and bars --><br />
<div style="position:relative;width:300px;margin:auto"><br />
<br />
<img src="tank.gif"> <!-- background image of tank --><br />
<br />
<!-- label next to valve --><br />
<div class="modbvalue" data-odb-path="/Runinfo/Run number" data-odb-editable="1"<br />
style="position:absolute;top:157px;left:288px;"></div><br />
<br />
<!-- vertical bar inside tank, render red if value > 9 --><br />
<div class="modbvbar" style="position:absolute;top:80px;left:10px;width:104px;height:170px;"<br />
data-odb-path="/Runinfo/Run number" data-max-value="11" data-color="green"<br />
onchange="this.firstChild.style.backgroundColor=(this.value > 9)?'red':'green';"></div><br />
<br />
<!-- thermometer inside tank --><br />
<div class="modbthermo" style="position:absolute;top:140px;left:20px;width:20px;height:100px;"<br />
data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-value="1"></div><br />
<br />
</div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td colspan="2" style="text-align: center;"><br />
<!-- three buttons to change an ODB entry (run number in this example) --><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="1">Set run<br />
number to 1<br />
</button><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="5">Set run<br />
number to 5<br />
</button><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="10">Set run<br />
number to 10<br />
</button><br />
</td><br />
</tr><br />
</table><br />
</div><br />
<br />
</body><br />
</html><br />
</pre><br />
<br />
which results in the page shown in Figure 1 below:<br />
<br />
[[File:Custom17.png|frame|left|Figure 1 Example custom page using most features]]<br />
<br />
<div style="clear: both"></div> <!-- clear wraparound after thumbnail --><br />
<br />
= Old custom page feature =<br />
<br />
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].<br />
<br />
<br />
[[Category:mhttpd Pages]] [[Category:Custom]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3356Sequencer2023-10-02T10:01:08Z<p>Stefan Ritt: /* Controlling the sequencer from custom pages */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. <br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
['test.msl', 'Yes'])<br />
<br />
After a script has been loaded, all '''PARAM''' values are extracted and placed under '''/Sequencer/Param/Defaults'''. Before a script is actually started, the default values may be modified and must be placed under '''/Sequencer/Param/Value''' before the sequencer is started. To start the script, set '''Start script''' to '''yes'''. This can be done in a combined way on a custom page via<br />
<br />
modbset(['/Sequencer/Param/Value/First parameter',<br />
'/Sequencer/Param/Value/Other parameter',<br />
'/Sequencer/Command/Start script'],<br />
['123', '456', 'Yes'])<br />
<br />
The parameter names of course have to be adjusted to the current script.<br />
<br />
The starting of the script via above '''modbset''' can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&diff=3327Custom Page2023-08-23T20:26:17Z<p>Stefan Ritt: /* mjshistory */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}<br />
<br />
= Purpose =<br />
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].<br />
<br />
= Introduction =<br />
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.<br />
<br />
We note that MIDAS has provided a number of different ways of providing custom pages over the years. A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.<br />
<br />
= Examples of Custom Pages =<br />
<br />
Click on the thumbnails to enlarge.<br />
<br />
{|<br />
|-<br />
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || '''Example 1'''<br />
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of "fills" and "labels". Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).<br />
|-<br />
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || '''Example 2'''<br />
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a "virtual" X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.<br />
<br />
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. <br />
<br />
For details using Xvfb server, please contact Ryu Sawada <sawada@icepp.s.u-tokyo.ac.jp>.<br />
|-<br />
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || '''Example 3'''<br />
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value. <br />
|}<br />
<br />
= Access a Custom Page from the Regular MIDAS pages =<br />
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.<br />
<br />
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in<br />
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.<br />
See [[Custom Page Features#Resource files]] for more information.<br />
<br />
If the key {{Odbpath|path=/Custom/myPage&}} (see Note) is created, e.g.<br />
odbedit> ls /Custom<br />
Path /home/expt/online/custom<br />
myPage& mypage.html<br />
<br />
the custom link on the left navigation bar will be <code>myPage</code> and the URL for the resulting custom page will be of the form <code>http://myhost.mydomain:myport/cmd=?Custom&page=myPage</code> (see also [[mhttpd#usage]]). <br />
Clicking on <code>myPage</code> will display the custom page in the same window.<br />
<br />
;Note<br />
: Without the "&" symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.<br />
<br />
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.<br />
<br />
odbedit> ls -r /Custom<br />
Path /home/expt/online/custom<br />
myPage& mypage.html<br />
Calorimeter<br />
HV hv.html<br />
Rates rates.html<br />
Baeam<br />
Beamline beamline.html<br />
Accelerator accel.html<br />
<br />
The pages then include the subdirectory in the URL, like <br />
<br />
http://localhost:8080?cmd=custom&page=beam/Beamline<br />
<br />
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as<br />
<br />
<body class="mcss" onload="mhttpd_init('Calorimeter/HV');"><br />
<br />
in order to keep the submenu open after you select the custom page.<br />
<br />
= How to write a custom page =<br />
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.<br />
<br />
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions. The advantages of using these modb* javascript functions are<br />
<br />
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.<br />
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.<br />
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.<br />
<br />
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient. In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call). It will also require more coding to implement the custom page with only MjsonRPC calls.<br />
<br />
== How to use the standard MIDAS navigation bars on your custom page == <br />
<br />
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax <br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');"><br />
<br />
<!-- header and side navigation will be filled in mhttpd_start --><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
ADD YOUR HTML/JS CODE here...<br />
</div><br />
</pre><br />
<br />
The call <code>mhttpd_init('myPage')</code> is executed when the page is loaded, and <code>myPage</code> is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.<br />
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].<br />
<br />
= modb* Javascript scheme = <br />
<br />
The general scheme of the custom page scheme is to write <code>&lt;div class="modb..."&gt;&lt;/div&gt;</code> or <code>&lt;span class="modb..."&gt;&lt;/span&gt;</code> tags with special class names, most of them starting with "modb..." ("MIDAS-ODB"). Use a <code>&lt;div&gt;</code> tag if you want the element to appear in a separate line, and use the <code>&lt;span&gt;</code> tag if you want to display the element in-line. The following description uses only <code>&lt;div&gt;</code> tags, but all of them can be changed to <code>&lt;span&gt;</code>. All HTML tags with "modb..." names are scanned by the <code>mhttp_init('name')</code> function upon page load, and their inner contents is replaced by the requested ODB value or some graphics. The contents is then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to <code>mhttpd_init('name', interval)</code> where "interval" is in milliseconds.<br />
<br />
== modbset(path, value) ==<br />
<br />
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:<br />
<br />
<code><br />
modbset("odb path", value)<br />
</code><br />
<br />
<code><br />
modbset(["odb path1", "odb path2", ...], [value1, value2, ...])<br />
</code><br />
<br />
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.<br />
<br />
== modb ==<br />
<br />
This special HTML div tag (abbreviation stands for Midas ODB) <code>&lt;div class="modb" data-odb-path="/Some/Path" onchange="func()"&gt;</code> can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &lt;script&gt;...&lt;/script&gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.<br />
<br />
The current value of the ODB entry is available inside the "onchange" function as '''this.value'''. Following tag will call a function which logs the current run number in the JavaScript console window:<br />
<br /><br /><br />
<code>&lt;div class="modb" data-odb-path="/Runinfo/Run number" onchange="func(this.value)"&gt;<br />
<br />
&lt;script&gt;function func(value) {<br />
console.log(value);<br />
}&lt;/script&gt;<br />
</code><br />
<br /><br /><br />
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to '''this.value''' as a JavaSctipt object such as<br />
<br /><br /><br />
<code>&lt;div class="modb" data-odb-path="/Runinfo" onchange="func(this.value)"&gt;<br />
<br />
&lt;script&gt;function func(value) {<br />
console.log(value["run number"]);<br />
}&lt;/script&gt;<br />
<br /><br />
</code><br />
<br /><br /><br />
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket '''value["run number"]''' as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as '''value.state''' for /Runinfo/State for example.<br />
<br />
== modbvalue ==<br />
<br />
This special HTML div tag (abbreviation stands for "Midas ODB VALUE") <br />
<br /><br /><br />
<code>&lt;div class="modbvalue" data-odb-path="/Some/Path"&gt;&lt;/div&gt;</code> <br />
<br /><br /><br />
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:<br />
<br />
{| class="wikitable"<br />
|+ Table 1: List of valid options for modbvalue tag<br />
|-<br />
! Option !! Example !! Meaning<br />
|-<br />
| data-name || class="modbvalue" || Tells the framework to replace this tag with an ODB value<br />
|-<br />
| data-odb-path || data-odb-path = "/Runinfo/Run number" || Path to the value in the ODB<br />
|-<br />
| data-odb-editable || data-odb-editable="1" || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.<br />
|-<br />
| data-format || data-format="f3" || Specify format of data shown. See Table 2 below for options.<br />
|-<br />
| data-size || data-size="8" || Specify size (in chars) of edit box if one modifies the value. Default is 10.<br />
|-<br />
| data-formula || data-formula="2*x+3" || Specify an optional formula to process with the current ODB value stored in x<br />
|-<br />
| data-validate || data-validate="func" || Specify an optional validation function which gets called before submitting data (see below)<br />
|}<br />
<br />
=== Validation ===<br />
<br />
Before a modified value is submitted to the ODB, an optional validation function can be called via the <code>data-validate</code> option. The function<br />
will be called with the current value and a reference to the current modbvalue element. If the function returns <code>false</code>, then the value<br />
is not sent to the ODB.<br />
<br />
Following example shows a function which just rejects the submission of values above 1000:<br />
<br />
&lt;div class="modbvalue" ... data-odb-editable="1" data-validate="my_validate"&gt;&lt;/div&gt;<br />
<br />
&lt;script src="controls.js"&gt;&lt;/script&gt; &lt;!-- needed of dlgAlert() --&gt;<br />
&lt;script&gt;<br />
function my_validate(value, element) {<br />
if (value > 1000) {<br />
dlgAlert("Value cannot be above 1000");<br />
return false;<br />
}<br />
return true;<br />
}<br />
&lt;/script&gt;<br />
<br />
Following function corrects the return value to 1000 if it's above 1000:<br />
<br />
&lt;script&gt;<br />
function my_validate2(value, element) {<br />
if (value > 1000) {<br />
element.childNodes[0].value = 1000;<br />
return true;<br />
}<br />
&lt;/script&gt;<br />
<br />
=== Confirmation ===<br />
<br />
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. <br />
This is done with the option <code>data-confirm=&lt;string&gt;</code>. A dialog box is then shown with the <code>&lt;string&gt;</code> as the main text and two buttons with "OK" and "Cancel".<br />
Hitting "OK" finally writes the value to the ODB, hitting "Cancel" keeps the old value.<br />
<br />
Following example shows an example:<br />
<br />
&lt;div class="modbvalue" ... data-odb-editable="1" data-confirm="Are you sure to change the value?"&gt;&lt;/div&gt;<br />
<br />
Following dialog box is then showed:<br />
<br />
[[File:Confirm.png|frame|left|Optional confirm dialog]]<br />
<br />
<br clear=all><br />
<br />
=== Formatting ===<br />
<br />
Table 2 below lists the format specifiers supported with a modbvalue tag <code>data-format="..."</code>:<br />
<br />
{| class="wikitable"<br />
|+ Table 2: Format specifiers for modbvalue tag <code>data-format="..."</code>.<br />
|-<br />
! Option !! Valid for* !! Example !! Meaning<br />
|-<br />
| %d || int || 1234 || Shows a number in decimal encoding<br />
|-<br />
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator<br />
|-<br />
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding<br />
|-<br />
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like <code>data-format="%d / %x"</code> to produce <code>1234 / 0x4D2</code><br />
|-<br />
| %f&lt;x&gt; || float || 1.234 || Shows a floating point number with &lt;x&gt; digits after the decimal. A value of f0 shows only the integer part.<br />
|-<br />
| %p&lt;x&gt; || float || 1.23 || Shows a floating point number with &lt;x&gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012<br />
|-<br />
| %e&lt;x&gt; || float || 1.23e-05 || Shows a floating point number with &lt;x&gt; digits after the decimal in exponential format. Useful for small number such as pressures.<br />
|-<br />
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.<br />
|-<br />
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats<br />
|}<br />
<br />
* Note that valid for "int" means all integral ODB types (regardless of size or signed/unsigned) and valid for "float" means both float and double.<br />
<br />
== modbbutton ==<br />
<br />
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the "Run number" to 100, one can use the following tag:<br />
<br /><br /><br />
<code>&lt;button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="100" data-validate="my_validate"&gt;[Button Text]&lt;/button&gt;</code><br />
<br />
<br />
The optional <code>data-validate</code> function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.<br />
<br />
== modbbox ==<br />
<br />
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the '''data-color''' is used, otherwise the '''data-background-color''' is used<br />
<br /><br /><br />
<code>&lt;div class="modbbox" data-odb-path="/Logger/Write Data" data-formula="x > 0" style="width: 30px; height: 30px; border: 1px solid black" data-color="lightgreen" data-background-color="red"&gt;&lt;/div&gt;</code><br />
<br />
Optionally, a <code>data-formula</code> can be specified. The formula sees the ODB value in the variable <code>x</code>, and can do any boolean operation. If the result of this is true, then the box gets the <code>data-color</code>, otherwise the <code>data-background-color</code>.<br />
Examples for these formulas are <code>x > 10</code> for a comparison or <code>x & 1</code> which will do a bitwise AND operation and is true only for odd numbers.<br />
<br />
== modbcheckbox ==<br />
<br />
This tag generates a check box which can set a certain ODB entry to true or false. To set the "Write data" flag for the logger true or false, one can use the following tag:<br />
<br /><br /><br />
<code>&lt;input type="checkbox" class="modbcheckbox" data-odb-path="/Logger/Write data" data-validate="my_validate" /&gt;</code><br />
<br /><br /><br />
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional <code>data-validate</code> function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for <code>modbvalue</code> tag.<br />
<br />
== modbselect ==<br />
<br />
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under <code>/Runinfo/Run number</code>.<br />
<br />
&lt;select class="modbselect" data-odb-path="/Runinfo/Run number"&gt;<br />
&lt;option value="1"&gt;1&lt;/option&gt;<br />
&lt;option value="5"&gt;5&lt;/option&gt;<br />
&lt;option value="10"&gt;10&lt;/option&gt;<br />
&lt;/select&gt;<br />
<br />
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify <code>data-auto-options="1"</code> to enable this behaviour. For ODB key <code>x</code>, you will need to create an ODB entry called <code>Options x</code> in the same directory; this "options" key should be a list, with each element in the list being an allowable option.<br />
<br />
<!-- Will read options from "/Equipment/Example/Settings/Options Something" in this case --><br />
&lt;select class="modbselect" data-odb-path="/Equipment/Example/Settings/Something" data-auto-options="1"&gt;<br />
&lt;/select&gt;<br />
<br />
The benefit of the <code>data-auto-options="1"</code> approach is that the same options will be shown on the regular ODB browser webpage. <br />
<br />
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.<br />
<br />
== modbhbar ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbhbar" style="width: 500px; height: 18px;" data-odb-path="/Runinfo/Run number" data-max-value="10" data-color="lightgreen" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 500px" || Total width of the horizontal bar || Yes<br />
|-<br />
| style="height: 18px" || Height of the horizontal bar || Yes<br />
|-<br />
| style="color: red" || Color of horizontal bar || Transparent if not present<br />
|-<br />
| style="background-color: red" || Background color of horizontal bar || Transparent if not present<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-min-value || Left limit of bar range || 0 if not present<br />
|-<br />
| data-max-value || Right limit of bar range || 1 if not present<br />
|-<br />
| data-log || Logarithmic display || No<br />
|-<br />
| data-print-value || If "1", data value is shown as text overlay || No<br />
|-<br />
| data-format || Specify format of data shown. See Table 2 above for options || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
== mhaxis ==<br />
<br />
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="mhaxis" style="width: 500px; height: 22px;" data-min-value="0" data-max-value="10" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a horizontal axis next to the bar. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 500px" || Total width of the axis, must match the width of the horizontal bar || Yes<br />
|-<br />
| style="height: 18px" || Height of the axis, must be enough to display labels || Yes<br />
|-<br />
| style="vertical-align: top" || Must be "top" if the axis is below the bar || "bottom" if not given<br />
|-<br />
| data-min-value || Left limit of axis range || Yes<br />
|-<br />
| data-max-value || Right limit of axis range || Yes<br />
|-<br />
| data-log || Logarithmic display || No<br />
|}<br />
<br />
== modbvbar ==<br />
<br />
Same as <code>modbhbar</code>, except the bar grows vertically instead of horizontally.<br />
<br />
== mvaxis ==<br />
<br />
Same as <code>mhaxis</code>, except the axis is shown vertically instead of horizontally.<br />
<br />
== modbthermo ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbthermo" style="width: 30px; height: 100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30" data-color="blue" data-print-value="1" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it's good for testing. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 30px" || Width of the thermometer || Yes<br />
|-<br />
| style="height: 100px" || Total height of the thermometer || Yes<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-max-value || Upper range || Yes<br />
|-<br />
| data-min-value || Lower range || 0 if not present<br />
|-<br />
| data-color || Color of thermometer || Black if not present<br />
|-<br />
| data-background-color || Color of thermometer background || Transparent if not present<br />
|-<br />
| data-print-value || If "1", data value is shown below the thermometer || No<br />
|-<br />
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
== modbgauge ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbgauge" style="width: 100px; height: 50px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10" data-color="darkgreen"&gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a circular gauge ranging from 0 to 10. Depending on the ODB value "Run number". Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 100px" || Width of the gauge || Yes<br />
|-<br />
| style="height: 50px" || Total height of the gauge || Yes<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-max-value || Upper range || Yes<br />
|-<br />
| data-min-value || Lower range || 0 if not present<br />
|-<br />
| data-color || Color of gauge || Black if not present<br />
|-<br />
| data-background-color || Color of gauge background || Transparent if not present<br />
|-<br />
| data-print-value || If "1", data value is shown below the gauge || No<br />
|-<br />
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No<br />
|-<br />
| data-scale || If "1", the min and max values of the range are shown below the gauge || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.<br />
<br />
== Changing properties of controls dynamically ==<br />
<br />
All custom controls can be configured to call a user's function when the control is first set up, or when the value changes. This is done by specifying the '''onload''' and/or '''onchange''' functions.<br />
<br />
* '''onload''' is called only once, when the control's value is first read from the ODB.<br />
* '''onchange''' is called each time the value in the ODB changes.<br />
<br />
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:<br />
<br />
<code>onchange="this.dataset.color=this.value > 30?'red':'blue';"</code><br />
<br />
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:<br />
<br />
<pre><br />
<script><br />
function check_therm(elem) {<br />
if (elem.value > 30) {<br />
elem.dataset.color = "red";<br />
} else {<br />
elem.dataset.color = "blue";<br />
}<br />
};<br />
</script><br />
<br />
....<br />
<br />
<div class="modbthermo" style="width: 30px; height: 100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30" data-color="blue" data-print-value="1" onchange="check_therm(this)"></div><br />
<br />
</pre><br />
<br />
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.<br />
<br />
If you want the same function to be called for both onload and onchange, you could set <code>onload="onchange()"</code> and have the "real" function in onchange.<br />
<br />
== Changing values of indicators programmatically ==<br />
<br />
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. <br />
<br />
For such cases, the <code>data-odb-path</code> attribute can be removed and the the display value can be changed via JavaScript code like following:<br />
<br />
<pre><br />
<br />
<div id="mythermo" class="modbthermo" data-min-value="-10" data-max-value="30"></div><br />
<br />
...<br />
let t = document.getElementById("mythermo");<br />
t.setValue(15);<br />
...<br />
<br />
</pre><br />
<br />
= mjshistory =<br />
<br />
Custom pages can contain one or more specific history panels usually shown on the "History" page. This makes it easy to combine current readings of values together with the history of these values.<br />
<br />
To enable interactive history panels, following lines have to be added to your custom page:<br />
<br />
Inisde the <head> tag:<br />
<br />
<script src="mhistory.js"></script><br />
<br />
Inside the <body> tag:<br />
<br />
<body ... onload="mhistory_init();"><br />
<br />
The following tag:<br />
<br />
&lt;div class="mjshistory" data-group="<group>" data-panel="<panel>" style="width: 320px; height: 200px;" &gt;&lt;/div&gt;<br />
<br />
shows a history panel defined in the ODB under /History/Display/&lt;group&gt;/&lt;panel&gt; (replace &lt;group&gt;/&lt;panel&gt; with groups and panels from your experiment).<br />
<br />
Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
| data-group || ODB group of history. Has to match a group under /History/Display || Yes<br />
|-<br />
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&lt;group&gt;/ || Yes<br />
|-<br />
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&lt;group&gt;/&lt;panel&gt;/Timescale is used. || No<br />
|-<br />
| style="width: 320px" || Width of the history panel || No<br />
|-<br />
| style="height: 200px" || Height of the history panel || No<br />
|-<br />
| style="border: 1px solid black" || Border around the history panel || No<br />
|}<br />
<br />
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.<br />
<br />
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users <br />
the possibility to show the history of that variable. Just put a button next to the value and call '''mhistory_dialog(&lt;group&gt;, &lt;panel&gt;)''' from that button like:<br />
<br />
&lt;span class="modbvalue" data-odb-path="/Some/Path"&gt;&lt;/span&gt;<br />
<button onclick="mhistory_dialog('group','panel')"><img src="icons/activity.svg"></button><br />
<br />
The history panel will then be opened when the user clicks the button:<br />
<br />
[[File:History dialog.png|frame|left|Floating history dialog]]<br />
<br />
<br clear=all><br />
<br />
If one wants to avoid the definition of a history panel in the ODB, a "direct variable plot" can be done with the function '''mhistory_dialog_var(&lt;variable&gt;)''' where &lt;variable&gt; has the format like the variable definition in the ODB (e.g. "System:Trigger per sec."). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as '''mhistory_dialog_var("System:Trigger per sec.", {"Timescale": "24h"});'''<br />
<br />
A full example of a custom page with a history panel is shown below.<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css" type="text/css"><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="controls.js"></script><br />
<script src="mhistory.js"></script><br />
</head><br />
<body onload="mhttpd_init('history_example'); mhistory_init();"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div> <br />
<div id="mmain"><br />
<div class="mjshistory" data-group="EPICS" data-panel="Logging" style="width: 500px; height: 300px;" ></div><br />
</div><br />
</body><br />
</html><br />
</pre><br />
<br />
= Complete Example =<br />
<br />
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:<br />
<br />
<pre><br />
/Custom<br />
Path /midas/resources<br />
Test a_example.html<br />
</pre><br />
<br />
== Code ==<br />
The file '''a_example.html''' contains the following code:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html class="mcss"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<title>Example</title><br />
<br />
<style><br />
.mtable td { padding: 10px; }<br />
</style><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('Example');"><br />
<br />
<!-- header and side navigation will be filled in mhttpd_init --><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<table class="mtable"><br />
<tr><br />
<th colspan="2" class="mtableheader">Status</th><br />
</tr><br />
<tr><br />
<td style="width: 200px;"><br />
Run number:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Run number" data-odb-editable="1"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Last run start:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Start time"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Last run stop:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Stop time"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Check box:<br />
</td><br />
<td><br />
<!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --><br />
<input type="checkbox" class="modbcheckbox" data-odb-path="/Logger/Write data"></input><br />
<br />
<div class="modb" data-odb-path="/Logger/Write data" onchange="dlgAlert('Flag has changed');"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Color box:<br />
</td><br />
<td><br />
<!-- box changes color according to /Logger/Write data --><br />
<div class="modbbox" style="width: 30px; height: 30px;" data-odb-path="/Logger/Write data"<br />
data-color="lightgreen" data-background-color="red"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Horizontal bars:<br />
</td><br />
<td><br />
<div class="modbhbar" style="width:300px;height:20px;color:orange;" data-odb-path="/Runinfo/Run number"<br />
data-max-value="10" data-print-value="1"></div><br /><br />
<br />
<div class="mhaxis" style="width:500px;height:22px;" data-min-value="0" data-max-value="10"></div><br />
<div class="modbhbar" style="width: 500px; height: 18px;color:lightblue" data-odb-path="/Runinfo/Run number"<br />
data-max-value="10"></div><br /><br />
<br />
<div class="modbhbar" style="width: 200px; height: 10px;color:lightgreen;background-color:white"<br />
data-odb-path="/Runinfo/Run number" data-min-value="0.1" data-max-value="10" data-log="1"></div><br />
<div class="mhaxis" style="width:200px;height:22px;vertical-align:top;" data-min-value="0.1"<br />
data-max-value="10" data-line="0" data-log="1"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Vertical bars:<br />
</td><br />
<td><br />
<span class="mvaxis" style="width:100px;height:200px;text-align:right;" data-min-value="0" data-max-value="20"></span><span class="modbvbar"<br />
style="width:20px;height:200px;color:yellow;" data-odb-path="/Runinfo/Run number"<br />
data-min-value="0" data-max-value="20"></span><br />
<span class="modbvbar" style="width:10px;height:200px;vertical-align:top;color:red" data-odb-path="/Runinfo/Run number" data-min-value="0.1"<br />
data-max-value="10" data-log="1"></span><span class="mvaxis" style="width:100px;height:200px;text-align:left;" data-min-value="0.1"<br />
data-max-value="10" data-log="1"></span><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td><br />
Thermometer:<br />
</td><br />
<td><br />
<div class="modbthermo" style="width:30px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="darkgreen"></div><br />
<div class="modbthermo" style="width:60px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-scale="1"<br />
onchange="this.dataset.color=this.value > 9?'red':'blue';"></div><br />
<div class="modbthermo" style="width:30px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-background-color="white" data-value="1"></div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td><br />
Gauges:<br />
</td><br />
<td><br />
<div class="modbgauge" style="width:100px;height:50px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10"<br />
data-color="darkgreen" data-background-color="lightgrey" ></div><br />
<div class="modbgauge" style="width:100px;height:65px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10"<br />
data-color="red" data-value="1" data-scale="1"></div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td colspan="2" style="text-align: center;"><br />
<!-- div around image with "relative" position as anchor for labels and bars --><br />
<div style="position:relative;width:300px;margin:auto"><br />
<br />
<img src="tank.gif"> <!-- background image of tank --><br />
<br />
<!-- label next to valve --><br />
<div class="modbvalue" data-odb-path="/Runinfo/Run number" data-odb-editable="1"<br />
style="position:absolute;top:157px;left:288px;"></div><br />
<br />
<!-- vertical bar inside tank, render red if value > 9 --><br />
<div class="modbvbar" style="position:absolute;top:80px;left:10px;width:104px;height:170px;"<br />
data-odb-path="/Runinfo/Run number" data-max-value="11" data-color="green"<br />
onchange="this.firstChild.style.backgroundColor=(this.value > 9)?'red':'green';"></div><br />
<br />
<!-- thermometer inside tank --><br />
<div class="modbthermo" style="position:absolute;top:140px;left:20px;width:20px;height:100px;"<br />
data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-value="1"></div><br />
<br />
</div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td colspan="2" style="text-align: center;"><br />
<!-- three buttons to change an ODB entry (run number in this example) --><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="1">Set run<br />
number to 1<br />
</button><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="5">Set run<br />
number to 5<br />
</button><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="10">Set run<br />
number to 10<br />
</button><br />
</td><br />
</tr><br />
</table><br />
</div><br />
<br />
</body><br />
</html><br />
</pre><br />
<br />
which results in the page shown in Figure 1 below:<br />
<br />
[[File:Custom17.png|frame|left|Figure 1 Example custom page using most features]]<br />
<br />
<div style="clear: both"></div> <!-- clear wraparound after thumbnail --><br />
<br />
= Old custom page feature =<br />
<br />
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].<br />
<br />
<br />
[[Category:mhttpd Pages]] [[Category:Custom]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&diff=3325Custom Page2023-08-23T19:28:26Z<p>Stefan Ritt: /* mjshistory */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}<br />
<br />
= Purpose =<br />
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].<br />
<br />
= Introduction =<br />
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.<br />
<br />
We note that MIDAS has provided a number of different ways of providing custom pages over the years. A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.<br />
<br />
= Examples of Custom Pages =<br />
<br />
Click on the thumbnails to enlarge.<br />
<br />
{|<br />
|-<br />
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || '''Example 1'''<br />
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of "fills" and "labels". Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).<br />
|-<br />
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || '''Example 2'''<br />
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a "virtual" X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.<br />
<br />
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. <br />
<br />
For details using Xvfb server, please contact Ryu Sawada <sawada@icepp.s.u-tokyo.ac.jp>.<br />
|-<br />
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || '''Example 3'''<br />
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value. <br />
|}<br />
<br />
= Access a Custom Page from the Regular MIDAS pages =<br />
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.<br />
<br />
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in<br />
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.<br />
See [[Custom Page Features#Resource files]] for more information.<br />
<br />
If the key {{Odbpath|path=/Custom/myPage&}} (see Note) is created, e.g.<br />
odbedit> ls /Custom<br />
Path /home/expt/online/custom<br />
myPage& mypage.html<br />
<br />
the custom link on the left navigation bar will be <code>myPage</code> and the URL for the resulting custom page will be of the form <code>http://myhost.mydomain:myport/cmd=?Custom&page=myPage</code> (see also [[mhttpd#usage]]). <br />
Clicking on <code>myPage</code> will display the custom page in the same window.<br />
<br />
;Note<br />
: Without the "&" symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.<br />
<br />
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.<br />
<br />
odbedit> ls -r /Custom<br />
Path /home/expt/online/custom<br />
myPage& mypage.html<br />
Calorimeter<br />
HV hv.html<br />
Rates rates.html<br />
Baeam<br />
Beamline beamline.html<br />
Accelerator accel.html<br />
<br />
The pages then include the subdirectory in the URL, like <br />
<br />
http://localhost:8080?cmd=custom&page=beam/Beamline<br />
<br />
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as<br />
<br />
<body class="mcss" onload="mhttpd_init('Calorimeter/HV');"><br />
<br />
in order to keep the submenu open after you select the custom page.<br />
<br />
= How to write a custom page =<br />
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.<br />
<br />
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions. The advantages of using these modb* javascript functions are<br />
<br />
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.<br />
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.<br />
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.<br />
<br />
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient. In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call). It will also require more coding to implement the custom page with only MjsonRPC calls.<br />
<br />
== How to use the standard MIDAS navigation bars on your custom page == <br />
<br />
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax <br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<title>myPage</title><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('myPage');"><br />
<br />
<!-- header and side navigation will be filled in mhttpd_start --><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
ADD YOUR HTML/JS CODE here...<br />
</div><br />
</pre><br />
<br />
The call <code>mhttpd_init('myPage')</code> is executed when the page is loaded, and <code>myPage</code> is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.<br />
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].<br />
<br />
= modb* Javascript scheme = <br />
<br />
The general scheme of the custom page scheme is to write <code>&lt;div class="modb..."&gt;&lt;/div&gt;</code> or <code>&lt;span class="modb..."&gt;&lt;/span&gt;</code> tags with special class names, most of them starting with "modb..." ("MIDAS-ODB"). Use a <code>&lt;div&gt;</code> tag if you want the element to appear in a separate line, and use the <code>&lt;span&gt;</code> tag if you want to display the element in-line. The following description uses only <code>&lt;div&gt;</code> tags, but all of them can be changed to <code>&lt;span&gt;</code>. All HTML tags with "modb..." names are scanned by the <code>mhttp_init('name')</code> function upon page load, and their inner contents is replaced by the requested ODB value or some graphics. The contents is then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to <code>mhttpd_init('name', interval)</code> where "interval" is in milliseconds.<br />
<br />
== modbset(path, value) ==<br />
<br />
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:<br />
<br />
<code><br />
modbset("odb path", value)<br />
</code><br />
<br />
<code><br />
modbset(["odb path1", "odb path2", ...], [value1, value2, ...])<br />
</code><br />
<br />
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.<br />
<br />
== modb ==<br />
<br />
This special HTML div tag (abbreviation stands for Midas ODB) <code>&lt;div class="modb" data-odb-path="/Some/Path" onchange="func()"&gt;</code> can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &lt;script&gt;...&lt;/script&gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.<br />
<br />
The current value of the ODB entry is available inside the "onchange" function as '''this.value'''. Following tag will call a function which logs the current run number in the JavaScript console window:<br />
<br /><br /><br />
<code>&lt;div class="modb" data-odb-path="/Runinfo/Run number" onchange="func(this.value)"&gt;<br />
<br />
&lt;script&gt;function func(value) {<br />
console.log(value);<br />
}&lt;/script&gt;<br />
</code><br />
<br /><br /><br />
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to '''this.value''' as a JavaSctipt object such as<br />
<br /><br /><br />
<code>&lt;div class="modb" data-odb-path="/Runinfo" onchange="func(this.value)"&gt;<br />
<br />
&lt;script&gt;function func(value) {<br />
console.log(value["run number"]);<br />
}&lt;/script&gt;<br />
<br /><br />
</code><br />
<br /><br /><br />
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket '''value["run number"]''' as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as '''value.state''' for /Runinfo/State for example.<br />
<br />
== modbvalue ==<br />
<br />
This special HTML div tag (abbreviation stands for "Midas ODB VALUE") <br />
<br /><br /><br />
<code>&lt;div class="modbvalue" data-odb-path="/Some/Path"&gt;&lt;/div&gt;</code> <br />
<br /><br /><br />
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:<br />
<br />
{| class="wikitable"<br />
|+ Table 1: List of valid options for modbvalue tag<br />
|-<br />
! Option !! Example !! Meaning<br />
|-<br />
| data-name || class="modbvalue" || Tells the framework to replace this tag with an ODB value<br />
|-<br />
| data-odb-path || data-odb-path = "/Runinfo/Run number" || Path to the value in the ODB<br />
|-<br />
| data-odb-editable || data-odb-editable="1" || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.<br />
|-<br />
| data-format || data-format="f3" || Specify format of data shown. See Table 2 below for options.<br />
|-<br />
| data-size || data-size="8" || Specify size (in chars) of edit box if one modifies the value. Default is 10.<br />
|-<br />
| data-formula || data-formula="2*x+3" || Specify an optional formula to process with the current ODB value stored in x<br />
|-<br />
| data-validate || data-validate="func" || Specify an optional validation function which gets called before submitting data (see below)<br />
|}<br />
<br />
=== Validation ===<br />
<br />
Before a modified value is submitted to the ODB, an optional validation function can be called via the <code>data-validate</code> option. The function<br />
will be called with the current value and a reference to the current modbvalue element. If the function returns <code>false</code>, then the value<br />
is not sent to the ODB.<br />
<br />
Following example shows a function which just rejects the submission of values above 1000:<br />
<br />
&lt;div class="modbvalue" ... data-odb-editable="1" data-validate="my_validate"&gt;&lt;/div&gt;<br />
<br />
&lt;script src="controls.js"&gt;&lt;/script&gt; &lt;!-- needed of dlgAlert() --&gt;<br />
&lt;script&gt;<br />
function my_validate(value, element) {<br />
if (value > 1000) {<br />
dlgAlert("Value cannot be above 1000");<br />
return false;<br />
}<br />
return true;<br />
}<br />
&lt;/script&gt;<br />
<br />
Following function corrects the return value to 1000 if it's above 1000:<br />
<br />
&lt;script&gt;<br />
function my_validate2(value, element) {<br />
if (value > 1000) {<br />
element.childNodes[0].value = 1000;<br />
return true;<br />
}<br />
&lt;/script&gt;<br />
<br />
=== Confirmation ===<br />
<br />
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. <br />
This is done with the option <code>data-confirm=&lt;string&gt;</code>. A dialog box is then shown with the <code>&lt;string&gt;</code> as the main text and two buttons with "OK" and "Cancel".<br />
Hitting "OK" finally writes the value to the ODB, hitting "Cancel" keeps the old value.<br />
<br />
Following example shows an example:<br />
<br />
&lt;div class="modbvalue" ... data-odb-editable="1" data-confirm="Are you sure to change the value?"&gt;&lt;/div&gt;<br />
<br />
Following dialog box is then showed:<br />
<br />
[[File:Confirm.png|frame|left|Optional confirm dialog]]<br />
<br />
<br clear=all><br />
<br />
=== Formatting ===<br />
<br />
Table 2 below lists the format specifiers supported with a modbvalue tag <code>data-format="..."</code>:<br />
<br />
{| class="wikitable"<br />
|+ Table 2: Format specifiers for modbvalue tag <code>data-format="..."</code>.<br />
|-<br />
! Option !! Valid for* !! Example !! Meaning<br />
|-<br />
| %d || int || 1234 || Shows a number in decimal encoding<br />
|-<br />
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator<br />
|-<br />
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding<br />
|-<br />
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like <code>data-format="%d / %x"</code> to produce <code>1234 / 0x4D2</code><br />
|-<br />
| %f&lt;x&gt; || float || 1.234 || Shows a floating point number with &lt;x&gt; digits after the decimal. A value of f0 shows only the integer part.<br />
|-<br />
| %p&lt;x&gt; || float || 1.23 || Shows a floating point number with &lt;x&gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012<br />
|-<br />
| %e&lt;x&gt; || float || 1.23e-05 || Shows a floating point number with &lt;x&gt; digits after the decimal in exponential format. Useful for small number such as pressures.<br />
|-<br />
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.<br />
|-<br />
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats<br />
|}<br />
<br />
* Note that valid for "int" means all integral ODB types (regardless of size or signed/unsigned) and valid for "float" means both float and double.<br />
<br />
== modbbutton ==<br />
<br />
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the "Run number" to 100, one can use the following tag:<br />
<br /><br /><br />
<code>&lt;button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="100" data-validate="my_validate"&gt;[Button Text]&lt;/button&gt;</code><br />
<br />
<br />
The optional <code>data-validate</code> function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.<br />
<br />
== modbbox ==<br />
<br />
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the '''data-color''' is used, otherwise the '''data-background-color''' is used<br />
<br /><br /><br />
<code>&lt;div class="modbbox" data-odb-path="/Logger/Write Data" data-formula="x > 0" style="width: 30px; height: 30px; border: 1px solid black" data-color="lightgreen" data-background-color="red"&gt;&lt;/div&gt;</code><br />
<br />
Optionally, a <code>data-formula</code> can be specified. The formula sees the ODB value in the variable <code>x</code>, and can do any boolean operation. If the result of this is true, then the box gets the <code>data-color</code>, otherwise the <code>data-background-color</code>.<br />
Examples for these formulas are <code>x > 10</code> for a comparison or <code>x & 1</code> which will do a bitwise AND operation and is true only for odd numbers.<br />
<br />
== modbcheckbox ==<br />
<br />
This tag generates a check box which can set a certain ODB entry to true or false. To set the "Write data" flag for the logger true or false, one can use the following tag:<br />
<br /><br /><br />
<code>&lt;input type="checkbox" class="modbcheckbox" data-odb-path="/Logger/Write data" data-validate="my_validate" /&gt;</code><br />
<br /><br /><br />
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional <code>data-validate</code> function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for <code>modbvalue</code> tag.<br />
<br />
== modbselect ==<br />
<br />
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under <code>/Runinfo/Run number</code>.<br />
<br />
&lt;select class="modbselect" data-odb-path="/Runinfo/Run number"&gt;<br />
&lt;option value="1"&gt;1&lt;/option&gt;<br />
&lt;option value="5"&gt;5&lt;/option&gt;<br />
&lt;option value="10"&gt;10&lt;/option&gt;<br />
&lt;/select&gt;<br />
<br />
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify <code>data-auto-options="1"</code> to enable this behaviour. For ODB key <code>x</code>, you will need to create an ODB entry called <code>Options x</code> in the same directory; this "options" key should be a list, with each element in the list being an allowable option.<br />
<br />
<!-- Will read options from "/Equipment/Example/Settings/Options Something" in this case --><br />
&lt;select class="modbselect" data-odb-path="/Equipment/Example/Settings/Something" data-auto-options="1"&gt;<br />
&lt;/select&gt;<br />
<br />
The benefit of the <code>data-auto-options="1"</code> approach is that the same options will be shown on the regular ODB browser webpage. <br />
<br />
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.<br />
<br />
== modbhbar ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbhbar" style="width: 500px; height: 18px;" data-odb-path="/Runinfo/Run number" data-max-value="10" data-color="lightgreen" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 500px" || Total width of the horizontal bar || Yes<br />
|-<br />
| style="height: 18px" || Height of the horizontal bar || Yes<br />
|-<br />
| style="color: red" || Color of horizontal bar || Transparent if not present<br />
|-<br />
| style="background-color: red" || Background color of horizontal bar || Transparent if not present<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-min-value || Left limit of bar range || 0 if not present<br />
|-<br />
| data-max-value || Right limit of bar range || 1 if not present<br />
|-<br />
| data-log || Logarithmic display || No<br />
|-<br />
| data-print-value || If "1", data value is shown as text overlay || No<br />
|-<br />
| data-format || Specify format of data shown. See Table 2 above for options || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
== mhaxis ==<br />
<br />
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="mhaxis" style="width: 500px; height: 22px;" data-min-value="0" data-max-value="10" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a horizontal axis next to the bar. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 500px" || Total width of the axis, must match the width of the horizontal bar || Yes<br />
|-<br />
| style="height: 18px" || Height of the axis, must be enough to display labels || Yes<br />
|-<br />
| style="vertical-align: top" || Must be "top" if the axis is below the bar || "bottom" if not given<br />
|-<br />
| data-min-value || Left limit of axis range || Yes<br />
|-<br />
| data-max-value || Right limit of axis range || Yes<br />
|-<br />
| data-log || Logarithmic display || No<br />
|}<br />
<br />
== modbvbar ==<br />
<br />
Same as <code>modbhbar</code>, except the bar grows vertically instead of horizontally.<br />
<br />
== mvaxis ==<br />
<br />
Same as <code>mhaxis</code>, except the axis is shown vertically instead of horizontally.<br />
<br />
== modbthermo ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbthermo" style="width: 30px; height: 100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30" data-color="blue" data-print-value="1" &gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it's good for testing. Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 30px" || Width of the thermometer || Yes<br />
|-<br />
| style="height: 100px" || Total height of the thermometer || Yes<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-max-value || Upper range || Yes<br />
|-<br />
| data-min-value || Lower range || 0 if not present<br />
|-<br />
| data-color || Color of thermometer || Black if not present<br />
|-<br />
| data-background-color || Color of thermometer background || Transparent if not present<br />
|-<br />
| data-print-value || If "1", data value is shown below the thermometer || No<br />
|-<br />
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
== modbgauge ==<br />
<br />
The following tag:<br />
<br /><br /><br />
<code>&lt;div class="modbgauge" style="width: 100px; height: 50px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10" data-color="darkgreen"&gt;&lt;/div&gt;</code><br />
<br /><br /><br />
shows a circular gauge ranging from 0 to 10. Depending on the ODB value "Run number". Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Setting !! Meaning !! Required<br />
|-<br />
| style="width: 100px" || Width of the gauge || Yes<br />
|-<br />
| style="height: 50px" || Total height of the gauge || Yes<br />
|-<br />
| data-odb-path || ODB path of value being displayed || Yes<br />
|-<br />
| data-max-value || Upper range || Yes<br />
|-<br />
| data-min-value || Lower range || 0 if not present<br />
|-<br />
| data-color || Color of gauge || Black if not present<br />
|-<br />
| data-background-color || Color of gauge background || Transparent if not present<br />
|-<br />
| data-print-value || If "1", data value is shown below the gauge || No<br />
|-<br />
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No<br />
|-<br />
| data-scale || If "1", the min and max values of the range are shown below the gauge || No<br />
|-<br />
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No<br />
|}<br />
<br />
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.<br />
<br />
== Changing properties of controls dynamically ==<br />
<br />
All custom controls can be configured to call a user's function when the control is first set up, or when the value changes. This is done by specifying the '''onload''' and/or '''onchange''' functions.<br />
<br />
* '''onload''' is called only once, when the control's value is first read from the ODB.<br />
* '''onchange''' is called each time the value in the ODB changes.<br />
<br />
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:<br />
<br />
<code>onchange="this.dataset.color=this.value > 30?'red':'blue';"</code><br />
<br />
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:<br />
<br />
<pre><br />
<script><br />
function check_therm(elem) {<br />
if (elem.value > 30) {<br />
elem.dataset.color = "red";<br />
} else {<br />
elem.dataset.color = "blue";<br />
}<br />
};<br />
</script><br />
<br />
....<br />
<br />
<div class="modbthermo" style="width: 30px; height: 100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30" data-color="blue" data-print-value="1" onchange="check_therm(this)"></div><br />
<br />
</pre><br />
<br />
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.<br />
<br />
If you want the same function to be called for both onload and onchange, you could set <code>onload="onchange()"</code> and have the "real" function in onchange.<br />
<br />
== Changing values of indicators programmatically ==<br />
<br />
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. <br />
<br />
For such cases, the <code>data-odb-path</code> attribute can be removed and the the display value can be changed via JavaScript code like following:<br />
<br />
<pre><br />
<br />
<div id="mythermo" class="modbthermo" data-min-value="-10" data-max-value="30"></div><br />
<br />
...<br />
let t = document.getElementById("mythermo");<br />
t.setValue(15);<br />
...<br />
<br />
</pre><br />
<br />
= mjshistory =<br />
<br />
Custom pages can contain one or more specific history panels usually shown on the "History" page. This makes it easy to combine current readings of values together with the history of these values.<br />
<br />
To enable interactive history panels, following lines have to be added to your custom page:<br />
<br />
Inisde the <head> tag:<br />
<br />
<script src="mhistory.js"></script><br />
<br />
Inside the <body> tag:<br />
<br />
<body ... onload="mhistory_init();"><br />
<br />
The following tag:<br />
<br />
&lt;div class="mjshistory" data-group="<group>" data-panel="<panel>" style="width: 320px; height: 200px;" &gt;&lt;/div&gt;<br />
<br />
shows a history panel defined in the ODB under /History/Display/&lt;group&gt;/&lt;panel&gt; (replace &lt;group&gt;/&lt;panel&gt; with groups and panels from your experiment).<br />
<br />
Following options are possible:<br />
<br />
{| class="wikitable"<br />
|-<br />
| data-group || ODB group of history. Has to match a group under /History/Display || Yes<br />
|-<br />
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&lt;group&gt;/ || Yes<br />
|-<br />
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&lt;group&gt;/&lt;panel&gt;/Timescale is used. || No<br />
|-<br />
| style="width: 320px" || Width of the history panel || No<br />
|-<br />
| style="height: 200px" || Height of the history panel || No<br />
|-<br />
| style="border: 1px solid black" || Border around the history panel || No<br />
|}<br />
<br />
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.<br />
<br />
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users <br />
the possibility to show the history of that variable. Just put a button next to the value and call '''mhistory_dialog(&lt;group&gt;, &ltp;panel&gt;)''' from that button like:<br />
<br />
&lt;span class="modbvalue" data-odb-path="/Some/Path"&gt;&lt;/span&gt;<br />
<button onclick="mhistory_display('group','panel')"><img src="icons/activity.svg"></button><br />
<br />
The history panel will then be opened when the user clicks the button:<br />
<br />
[[File:History dialog.png|frame|left|Floating history dialog]]<br />
<br />
<br clear=all><br />
<br />
If one wants to avoid the definition of a history panel in the ODB, a "direct variable plot" can be done with the function '''mhistory_dialog_var(&lt;variable&gt;)''' where &lt;variable&gt; has the format like the variable definition in the ODB (e.g. "System:Trigger per sec."). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as '''mhistory_dialog_var("System:Trigger per sec.", {"Timescale": "24h"});'''<br />
<br />
A full example of a custom page with a history panel is shown below.<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html lang="en"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css" type="text/css"><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<script src="controls.js"></script><br />
<script src="mhistory.js"></script><br />
</head><br />
<body onload="mhttpd_init('history_example'); mhistory_init();"><br />
<div id="mheader"></div><br />
<div id="msidenav"></div> <br />
<div id="mmain"><br />
<div class="mjshistory" data-group="EPICS" data-panel="Logging" style="width: 500px; height: 300px;" ></div><br />
</div><br />
</body><br />
</html><br />
</pre><br />
<br />
= Complete Example =<br />
<br />
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:<br />
<br />
<pre><br />
/Custom<br />
Path /midas/resources<br />
Test a_example.html<br />
</pre><br />
<br />
== Code ==<br />
The file '''a_example.html''' contains the following code:<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html class="mcss"><br />
<head><br />
<meta charset="UTF-8"><br />
<link rel="stylesheet" href="midas.css"><br />
<script src="controls.js"></script><br />
<script src="midas.js"></script><br />
<script src="mhttpd.js"></script><br />
<title>Example</title><br />
<br />
<style><br />
.mtable td { padding: 10px; }<br />
</style><br />
</head><br />
<br />
<body class="mcss" onload="mhttpd_init('Example');"><br />
<br />
<!-- header and side navigation will be filled in mhttpd_init --><br />
<div id="mheader"></div><br />
<div id="msidenav"></div><br />
<br />
<div id="mmain"><br />
<table class="mtable"><br />
<tr><br />
<th colspan="2" class="mtableheader">Status</th><br />
</tr><br />
<tr><br />
<td style="width: 200px;"><br />
Run number:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Run number" data-odb-editable="1"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Last run start:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Start time"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Last run stop:<br />
</td><br />
<td><br />
<div class="modbvalue" data-odb-path="/Runinfo/Stop time"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Check box:<br />
</td><br />
<td><br />
<!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --><br />
<input type="checkbox" class="modbcheckbox" data-odb-path="/Logger/Write data"></input><br />
<br />
<div class="modb" data-odb-path="/Logger/Write data" onchange="dlgAlert('Flag has changed');"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Color box:<br />
</td><br />
<td><br />
<!-- box changes color according to /Logger/Write data --><br />
<div class="modbbox" style="width: 30px; height: 30px;" data-odb-path="/Logger/Write data"<br />
data-color="lightgreen" data-background-color="red"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Horizontal bars:<br />
</td><br />
<td><br />
<div class="modbhbar" style="width:300px;height:20px;color:orange;" data-odb-path="/Runinfo/Run number"<br />
data-max-value="10" data-print-value="1"></div><br /><br />
<br />
<div class="mhaxis" style="width:500px;height:22px;" data-min-value="0" data-max-value="10"></div><br />
<div class="modbhbar" style="width: 500px; height: 18px;color:lightblue" data-odb-path="/Runinfo/Run number"<br />
data-max-value="10"></div><br /><br />
<br />
<div class="modbhbar" style="width: 200px; height: 10px;color:lightgreen;background-color:white"<br />
data-odb-path="/Runinfo/Run number" data-min-value="0.1" data-max-value="10" data-log="1"></div><br />
<div class="mhaxis" style="width:200px;height:22px;vertical-align:top;" data-min-value="0.1"<br />
data-max-value="10" data-line="0" data-log="1"></div><br />
</td><br />
</tr><br />
<tr><br />
<td><br />
Vertical bars:<br />
</td><br />
<td><br />
<span class="mvaxis" style="width:100px;height:200px;text-align:right;" data-min-value="0" data-max-value="20"></span><span class="modbvbar"<br />
style="width:20px;height:200px;color:yellow;" data-odb-path="/Runinfo/Run number"<br />
data-min-value="0" data-max-value="20"></span><br />
<span class="modbvbar" style="width:10px;height:200px;vertical-align:top;color:red" data-odb-path="/Runinfo/Run number" data-min-value="0.1"<br />
data-max-value="10" data-log="1"></span><span class="mvaxis" style="width:100px;height:200px;text-align:left;" data-min-value="0.1"<br />
data-max-value="10" data-log="1"></span><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td><br />
Thermometer:<br />
</td><br />
<td><br />
<div class="modbthermo" style="width:30px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="darkgreen"></div><br />
<div class="modbthermo" style="width:60px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-scale="1"<br />
onchange="this.dataset.color=this.value > 9?'red':'blue';"></div><br />
<div class="modbthermo" style="width:30px;height:100px;" data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-background-color="white" data-value="1"></div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td><br />
Gauges:<br />
</td><br />
<td><br />
<div class="modbgauge" style="width:100px;height:50px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10"<br />
data-color="darkgreen" data-background-color="lightgrey" ></div><br />
<div class="modbgauge" style="width:100px;height:65px;" data-odb-path="/Runinfo/Run number" data-min-value="0" data-max-value="10"<br />
data-color="red" data-value="1" data-scale="1"></div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td colspan="2" style="text-align: center;"><br />
<!-- div around image with "relative" position as anchor for labels and bars --><br />
<div style="position:relative;width:300px;margin:auto"><br />
<br />
<img src="tank.gif"> <!-- background image of tank --><br />
<br />
<!-- label next to valve --><br />
<div class="modbvalue" data-odb-path="/Runinfo/Run number" data-odb-editable="1"<br />
style="position:absolute;top:157px;left:288px;"></div><br />
<br />
<!-- vertical bar inside tank, render red if value > 9 --><br />
<div class="modbvbar" style="position:absolute;top:80px;left:10px;width:104px;height:170px;"<br />
data-odb-path="/Runinfo/Run number" data-max-value="11" data-color="green"<br />
onchange="this.firstChild.style.backgroundColor=(this.value > 9)?'red':'green';"></div><br />
<br />
<!-- thermometer inside tank --><br />
<div class="modbthermo" style="position:absolute;top:140px;left:20px;width:20px;height:100px;"<br />
data-odb-path="/Runinfo/Run number" data-min-value="-10" data-max-value="30"<br />
data-color="blue" data-value="1"></div><br />
<br />
</div><br />
</td><br />
</tr><br />
<br />
<tr><br />
<td colspan="2" style="text-align: center;"><br />
<!-- three buttons to change an ODB entry (run number in this example) --><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="1">Set run<br />
number to 1<br />
</button><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="5">Set run<br />
number to 5<br />
</button><br />
<button class="modbbutton" class="mbutton" data-odb-path="/Runinfo/Run number" data-odb-value="10">Set run<br />
number to 10<br />
</button><br />
</td><br />
</tr><br />
</table><br />
</div><br />
<br />
</body><br />
</html><br />
</pre><br />
<br />
which results in the page shown in Figure 1 below:<br />
<br />
[[File:Custom17.png|frame|left|Figure 1 Example custom page using most features]]<br />
<br />
<div style="clear: both"></div> <!-- clear wraparound after thumbnail --><br />
<br />
= Old custom page feature =<br />
<br />
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].<br />
<br />
<br />
[[Category:mhttpd Pages]] [[Category:Custom]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&diff=3321Odbxx2023-08-22T18:53:05Z<p>Stefan Ritt: /* Basic usage */</p>
<hr />
<div>A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a "magic" map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.<br />
<br />
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/progs/odbxx_test.cxx odbxx_test.cxx].<br />
<br />
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]] page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.<br />
<br />
== Basic usage ==<br />
<br />
The simplest usage is like:<br />
<br />
<pre>// Grab a bit of the ODB<br />
midas::odb exp("/Experiment");<br />
<br />
// Simple read<br />
std::cout << "The current transition timeout is " << exp["Transition timeout"] << std::endl;<br />
<br />
// Make a change. The new value is automatically sent to the ODB.<br />
// Most C++ operators are supported (++, += etc), or you can do a simple<br />
// re-assignment like `exp["Transition timeout"] = 12345;`.<br />
exp["Transition timeout"] += 100;<br />
<br />
// Read the new value<br />
std::cout << "The transition timeout is now " << exp["Transition timeout"] << std::endl;<br />
</pre><br />
<br />
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:<br />
<br />
<pre>int curr_timeout = exp["Transition timeout"];</pre><br />
<br />
'''Note''': The ODB directory you connect to ("/Experiment" in the above example), has to start with a "/" to tell the midas::odb object<br />
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.<br />
<br />
== Automatic refreshing ==<br />
<br />
You may temporarily disable the automatic updating to/from the ODB<br />
using <code>odb::set_auto_refresh_write(false)</code> and <code>odb::set_auto_refresh_read(false)</code>.<br />
<br />
If auto-refresh is enabled (the default), your new values are sent to<br />
the ODB as soon as you touch the value in the <code>midas::odb</code> object. The ODB<br />
is queried for new values whenever you access the value. In the above<br />
example, the ODB is queried 4 times (during construction of <code>exp</code>, and<br />
each time <code>exp["Transition timeout"]</code> is mentioned), and written to 1<br />
time (when <code>exp["Transition timeout"]</code> is assigned to).<br />
<br />
See the [[#Callback functions]] section below for details on how to have a function<br />
called when a value changes.<br />
<br />
== Arrays/vectors ==<br />
<br />
ODB arrays are represented by std vectors.<br />
<br />
You can access/edit individual elements using []:<br />
<br />
<pre>odb["Example"][1] = 1.2;</pre><br />
<br />
You can completely re-assign content using a std::vector or std::array:<br />
<br />
<pre>std::vector<float> vec = std::vector<float>(10);<br />
odb["Example"] = vec;<br />
</pre><br />
<br />
You can resize arrays using <code>odb::resize()</code>. If the existing array is longer,<br />
it will be truncated; if shorter it will be extended with default values<br />
(0 or an empty string).<br />
<br />
<pre>odb["Example"].resize(5); // Now is 5 elements long</pre><br />
<br />
Note that arithmetic operators are supported for arrays, and will apply<br />
the operation to ALL ELEMENTS IN THE ARRAY:<br />
<br />
<pre>// Create the vector<br />
std::vector<float> vec = std::vector<float>(2);<br />
vec[0] = 3;<br />
vec[1] = 5;<br />
<br />
// Assign in ODB<br />
odb["Example"] = vec;<br />
<br />
// Multiply ALL elements by 2<br />
odb["Example"] *= 2;<br />
<br />
// odb["Example"] now contains {6, 10}.<br />
</pre><br />
<br />
You can directly iterate over arrays/vectors:<br />
<br />
<pre>// Iterating using standard begin/end.<br />
for (auto it = o["Int Array"].begin(); it != o["Int Array"].end(); it++) {<br />
int val = *it;<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (int val : o["Int Array"]) {<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
== Strings ==<br />
<br />
Strings in the ODB are returned as std::string (unlike the midas.h <code>db_get_value()</code><br />
family of functions, where strings are returned as char*). You may have vectors of strings.<br />
<br />
== Creating new bits of the ODB ==<br />
<br />
You can automatically create bits of the ODB by passing a struct to the<br />
<code>midas::odb</code> constructor, then calling <code>odb::connect()</code>, like:<br />
<br />
<pre>// Define the ODB structure<br />
midas::odb new_bit = {<br />
{"Int32 Key", 42},<br />
{"Bool Key", true},<br />
{"Subdir", {<br />
{"Float key", 1.2f}, // floats must be explicitly specified<br />
}},<br />
{"Int Array", {1, 2, 3}},<br />
{"Double Array", {1.2, 2.3, 3.4}},<br />
{"String Array", {"Hello1", "Hello2", "Hello3"}},<br />
{"Large Array", std::array<int, 10>{} }, // array with explicit size<br />
{"Large String", std::string(63, '\0') }, // string with explicit size<br />
};<br />
<br />
// Then sync the structure. This function<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings");<br />
<br />
// If you make the `write_defaults` argument true, then the function<br />
// - overwrites the value of any keys that are in the ODB with the value in your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings", true);<br />
<br />
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
// - deletes any keys that are in the ODB but not your code<br />
// - updates the order of keys in the ODB to match your code<br />
o.connect_and_fix_structure("/Test/Settings");<br />
</pre><br />
<br />
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
existing_key["MyNewSubKey"] = 1.23;<br />
</pre><br />
<br />
You can also create new keys by providing a default value when reading a value. If the key doesn't already exist, the default value will be used.<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
double val = existing_key["MyNewSubKey"](1.23);<br />
</pre><br />
<br />
== Iterating over subkeys ==<br />
<br />
You can iterate over subkeys using normal iterator functions.<br />
<br />
<pre>// Iterating using standard begin/end.<br />
midas::odb exp("/Experiment");<br />
<br />
for (auto it = exp.begin(); it != exp.end(); it++) {<br />
midas::odb& subkey = *it;<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (midas::odb& subkey : exp) {<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
You can check whether a subkey exists using <code>odb::is_subkey()</code>.<br />
<br />
== Callback functions ==<br />
<br />
You may also set up callback functions that are called whenever a value<br />
changes, using the <code>odb::watch()</code> function. Note that you must call<br />
<code>cm_yield()</code> (from midas.h) periodically for this to work - deep down it<br />
is <code>cm_yield()</code> itself that calls your callback function.<br />
<br />
The callback functions can either be a "normal" function or a C++ lambda.<br />
In either case, it should accept one argument - a <code>midas::odb</code> object (passed<br />
by reference) that contains the new state.<br />
<br />
<pre>// Example with a lambda:<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch([](midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
});<br />
</pre><br />
<br />
<pre>// Example with a "normal" function:<br />
void my_function(midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
}<br />
<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch(my_function);<br />
</pre><br />
<br />
== Utility functions ==<br />
<br />
There are various utility functions which can be used:<br />
<br />
==== void odb::create(const char *name, int type) ====<br />
<br />
Simple wrapper around db_create_key() to create a single key in the ODB. <code>type</code> is one of TID_xxx.<br />
<br />
==== void odb::delete_key() ====<br />
<br />
This member function of a midas::odb object deletes that object from the ODB:<br />
<br />
<pre>midas::odb o("/Some/ODB/Path");<br />
o.delete_key();<br />
</pre><br />
<br />
==== int odb::delete_key(const std::string &name) ====<br />
<br />
This function deletes a key or a subtree in the ODB passed by its path in the <code>name</code> argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.<br />
<br />
==== bool odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====<br />
<br />
These functions set and retrieve the debug flag. If the debug flag is <code>true</code> all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.<br />
<br />
== Example code ==<br />
<br />
A full working example exploring most of the features can be found in<br />
<code>odbxx/odbxx_test.cxx</code>. The test executable will be compiled as<br />
<code>build/odbxx/odbxx_test</code> (it is not installed in the `bin` directory).</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Main_Page&diff=3319Main Page2023-08-17T12:03:33Z<p>Stefan Ritt: /* News and Support */</p>
<hr />
<div>= Introduction =<br />
<br />
MIDAS is a modern data acquisition system developed at [http://www.psi.ch PSI] and [http://www.triumf.ca TRIUMF]. Supported hardware includes VME, Fastbus, CAMAC, RS232, GPIB, USB, ethernet, fiber optic and MSCB attached data acquisition devices. MIDAS is written in C/C++ and runs on Linux, MacOS and MS Windows. It is licensed under the [http://www.gnu.org/copyleft/gpl.html GNU Public License].<br />
[[Introduction|'''more...''']]<br />
<br />
= Documentation =<br />
<br />
* [[Midas documentation| Main Documentation]]<br />
* [[Installation | Installation Instructions]]<br />
* [[Changelog | Changelog / Upgrade Instructions]]<br />
* [https://daq.triumf.ca/~daqweb/doc/midas-develop/html/modules.html <i>Doxygen</i>] Code structure, functions, etc...<br />
* [https://bitbucket.com/tmidas/midas git repository on bitbucket] Source code, bug tracking, etc<br />
* [https://midas.triumf.ca/forum MIDAS forum] Discussion, announcements, problem reports, requests for improvements<br />
<br />
= News and Support=<br />
<br />
For news about bug fixes and new releases, go to the<br />
[https://midas.triumf.ca/forum MIDAS forum] hosted by TRIUMF using<br />
the [http://elog.psi.ch/elog ELOG] system. Users can register in this system to be notified automatically via <br />
E-mail when new entries are submitted.<br />
<br />
Another source of information is the [https://bitbucket.com/tmidas/midas Bitbucket Repository] of MIDAS, where one can see the latest changes to the software.<br />
<br />
Finally, you can read presentations from<br />
* [https://indico.psi.ch/conferenceDisplay.py?ovw=True&confId=3793 2015 MIDAS Workshop]<br />
* [https://meetings.triumf.ca/event/428/ 2017 MIDAS Workshop]<br />
* [https://meetings.triumf.ca/event/429/ 2019 MIDAS Workshop]<br />
* [https://indico.psi.ch/e/midas23 2023 MIDAS Workshop]<br />
<br />
= Related packages =<br />
<br />
* [http://elog.psi.ch/elog ELOG] - Electronics Logbook from PSI<br />
* [https://bitbucket.org/tmidas/manalyzer/src/master/ manalyzer] - data analysis from TRIUMF<br />
* [[ROOT]] - data analysis package from CERN<br />
* [[ROOTANA]] - ROOT-based analyzer for MIDAS<br />
* [[ROODY]] - viewer for online histograms<br />
* http://elog.psi.ch/rome/ - MEG/PSI data analysis package</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&diff=3278Odbxx2023-06-29T13:41:25Z<p>Stefan Ritt: </p>
<hr />
<div>A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a "magic" map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.<br />
<br />
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/progs/odbxx_test.cxx odbxx_test.cxx].<br />
<br />
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]] page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.<br />
<br />
== Basic usage ==<br />
<br />
The simplest usage is like:<br />
<br />
<pre>// Grab a bit of the ODB<br />
midas::odb exp("/Experiment");<br />
<br />
// Simple read<br />
std::cout << "The current transition timeout is " << exp["Transition timeout"] << std::endl;<br />
<br />
// Make a change. The new value is automatically sent to the ODB.<br />
// Most C++ operators are supported (++, += etc), or you can do a simple<br />
// re-assignment like `exp["Transition timeout"] = 12345;`.<br />
exp["Transition timeout"] += 100;<br />
<br />
// Read the new value<br />
std::cout << "The transition timeout is now " << exp["Transition timeout"] << std::endl;<br />
</pre><br />
<br />
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:<br />
<br />
<pre>int curr_timeout = exp["Transition timeout"];</pre><br />
<br />
== Automatic refreshing ==<br />
<br />
You may temporarily disable the automatic updating to/from the ODB<br />
using <code>odb::set_auto_refresh_write(false)</code> and <code>odb::set_auto_refresh_read(false)</code>.<br />
<br />
If auto-refresh is enabled (the default), your new values are sent to<br />
the ODB as soon as you touch the value in the <code>midas::odb</code> object. The ODB<br />
is queried for new values whenever you access the value. In the above<br />
example, the ODB is queried 4 times (during construction of <code>exp</code>, and<br />
each time <code>exp["Transition timeout"]</code> is mentioned), and written to 1<br />
time (when <code>exp["Transition timeout"]</code> is assigned to).<br />
<br />
See the [[#Callback functions]] section below for details on how to have a function<br />
called when a value changes.<br />
<br />
== Arrays/vectors ==<br />
<br />
ODB arrays are represented by std vectors.<br />
<br />
You can access/edit individual elements using []:<br />
<br />
<pre>odb["Example"][1] = 1.2;</pre><br />
<br />
You can completely re-assign content using a std::vector or std::array:<br />
<br />
<pre>std::vector<float> vec = std::vector<float>(10);<br />
odb["Example"] = vec;<br />
</pre><br />
<br />
You can resize arrays using <code>odb::resize()</code>. If the existing array is longer,<br />
it will be truncated; if shorter it will be extended with default values<br />
(0 or an empty string).<br />
<br />
<pre>odb["Example"].resize(5); // Now is 5 elements long</pre><br />
<br />
Note that arithmetic operators are supported for arrays, and will apply<br />
the operation to ALL ELEMENTS IN THE ARRAY:<br />
<br />
<pre>// Create the vector<br />
std::vector<float> vec = std::vector<float>(2);<br />
vec[0] = 3;<br />
vec[1] = 5;<br />
<br />
// Assign in ODB<br />
odb["Example"] = vec;<br />
<br />
// Multiply ALL elements by 2<br />
odb["Example"] *= 2;<br />
<br />
// odb["Example"] now contains {6, 10}.<br />
</pre><br />
<br />
You can directly iterate over arrays/vectors:<br />
<br />
<pre>// Iterating using standard begin/end.<br />
for (auto it = o["Int Array"].begin(); it != o["Int Array"].end(); it++) {<br />
int val = *it;<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (int val : o["Int Array"]) {<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
== Strings ==<br />
<br />
Strings in the ODB are returned as std::string (unlike the midas.h <code>db_get_value()</code><br />
family of functions, where strings are returned as char*). You may have vectors of strings.<br />
<br />
== Creating new bits of the ODB ==<br />
<br />
You can automatically create bits of the ODB by passing a struct to the<br />
<code>midas::odb</code> constructor, then calling <code>odb::connect()</code>, like:<br />
<br />
<pre>// Define the ODB structure<br />
midas::odb new_bit = {<br />
{"Int32 Key", 42},<br />
{"Bool Key", true},<br />
{"Subdir", {<br />
{"Float key", 1.2f}, // floats must be explicitly specified<br />
}},<br />
{"Int Array", {1, 2, 3}},<br />
{"Double Array", {1.2, 2.3, 3.4}},<br />
{"String Array", {"Hello1", "Hello2", "Hello3"}},<br />
{"Large Array", std::array<int, 10>{} }, // array with explicit size<br />
{"Large String", std::string(63, '\0') }, // string with explicit size<br />
};<br />
<br />
// Then sync the structure. This function<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings");<br />
<br />
// If you make the `write_defaults` argument true, then the function<br />
// - overwrites the value of any keys that are in the ODB with the value in your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings", true);<br />
<br />
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
// - deletes any keys that are in the ODB but not your code<br />
// - updates the order of keys in the ODB to match your code<br />
o.connect_and_fix_structure("/Test/Settings");<br />
</pre><br />
<br />
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
existing_key["MyNewSubKey"] = 1.23;<br />
</pre><br />
<br />
You can also create new keys by providing a default value when reading a value. If the key doesn't already exist, the default value will be used.<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
double val = existing_key["MyNewSubKey"](1.23);<br />
</pre><br />
<br />
== Iterating over subkeys ==<br />
<br />
You can iterate over subkeys using normal iterator functions.<br />
<br />
<pre>// Iterating using standard begin/end.<br />
midas::odb exp("/Experiment");<br />
<br />
for (auto it = exp.begin(); it != exp.end(); it++) {<br />
midas::odb& subkey = *it;<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (midas::odb& subkey : exp) {<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
You can check whether a subkey exists using <code>odb::is_subkey()</code>.<br />
<br />
== Callback functions ==<br />
<br />
You may also set up callback functions that are called whenever a value<br />
changes, using the <code>odb::watch()</code> function. Note that you must call<br />
<code>cm_yield()</code> (from midas.h) periodically for this to work - deep down it<br />
is <code>cm_yield()</code> itself that calls your callback function.<br />
<br />
The callback functions can either be a "normal" function or a C++ lambda.<br />
In either case, it should accept one argument - a <code>midas::odb</code> object (passed<br />
by reference) that contains the new state.<br />
<br />
<pre>// Example with a lambda:<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch([](midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
});<br />
</pre><br />
<br />
<pre>// Example with a "normal" function:<br />
void my_function(midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
}<br />
<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch(my_function);<br />
</pre><br />
<br />
== Utility functions ==<br />
<br />
There are various utility functions which can be used:<br />
<br />
==== void odb::create(const char *name, int type) ====<br />
<br />
Simple wrapper around db_create_key() to create a single key in the ODB. <code>type</code> is one of TID_xxx.<br />
<br />
==== void odb::delete_key() ====<br />
<br />
This member function of a midas::odb object deletes that object from the ODB:<br />
<br />
<pre>midas::odb o("/Some/ODB/Path");<br />
o.delete_key();<br />
</pre><br />
<br />
==== int odb::delete_key(const std::string &name) ====<br />
<br />
This function deletes a key or a subtree in the ODB passed by its path in the <code>name</code> argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.<br />
<br />
==== bool odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====<br />
<br />
These functions set and retrieve the debug flag. If the debug flag is <code>true</code> all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.<br />
<br />
== Example code ==<br />
<br />
A full working example exploring most of the features can be found in<br />
<code>odbxx/odbxx_test.cxx</code>. The test executable will be compiled as<br />
<code>build/odbxx/odbxx_test</code> (it is not installed in the `bin` directory).</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&diff=3277Odbxx2023-06-29T13:40:06Z<p>Stefan Ritt: Added utility functions</p>
<hr />
<div>A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a "magic" map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.<br />
<br />
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/progs/odbxx_test.cxx odbxx_test.cxx].<br />
<br />
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]] page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.<br />
<br />
== Basic usage ==<br />
<br />
The simplest usage is like:<br />
<br />
<pre>// Grab a bit of the ODB<br />
midas::odb exp("/Experiment");<br />
<br />
// Simple read<br />
std::cout << "The current transition timeout is " << exp["Transition timeout"] << std::endl;<br />
<br />
// Make a change. The new value is automatically sent to the ODB.<br />
// Most C++ operators are supported (++, += etc), or you can do a simple<br />
// re-assignment like `exp["Transition timeout"] = 12345;`.<br />
exp["Transition timeout"] += 100;<br />
<br />
// Read the new value<br />
std::cout << "The transition timeout is now " << exp["Transition timeout"] << std::endl;<br />
</pre><br />
<br />
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:<br />
<br />
<pre>int curr_timeout = exp["Transition timeout"];</pre><br />
<br />
== Automatic refreshing ==<br />
<br />
You may temporarily disable the automatic updating to/from the ODB<br />
using <code>odb::set_auto_refresh_write(false)</code> and <code>odb::set_auto_refresh_read(false)</code>.<br />
<br />
If auto-refresh is enabled (the default), your new values are sent to<br />
the ODB as soon as you touch the value in the <code>midas::odb</code> object. The ODB<br />
is queried for new values whenever you access the value. In the above<br />
example, the ODB is queried 4 times (during construction of <code>exp</code>, and<br />
each time <code>exp["Transition timeout"]</code> is mentioned), and written to 1<br />
time (when <code>exp["Transition timeout"]</code> is assigned to).<br />
<br />
See the [[#Callback functions]] section below for details on how to have a function<br />
called when a value changes.<br />
<br />
== Arrays/vectors ==<br />
<br />
ODB arrays are represented by std vectors.<br />
<br />
You can access/edit individual elements using []:<br />
<br />
<pre>odb["Example"][1] = 1.2;</pre><br />
<br />
You can completely re-assign content using a std::vector or std::array:<br />
<br />
<pre>std::vector<float> vec = std::vector<float>(10);<br />
odb["Example"] = vec;<br />
</pre><br />
<br />
You can resize arrays using <code>odb::resize()</code>. If the existing array is longer,<br />
it will be truncated; if shorter it will be extended with default values<br />
(0 or an empty string).<br />
<br />
<pre>odb["Example"].resize(5); // Now is 5 elements long</pre><br />
<br />
Note that arithmetic operators are supported for arrays, and will apply<br />
the operation to ALL ELEMENTS IN THE ARRAY:<br />
<br />
<pre>// Create the vector<br />
std::vector<float> vec = std::vector<float>(2);<br />
vec[0] = 3;<br />
vec[1] = 5;<br />
<br />
// Assign in ODB<br />
odb["Example"] = vec;<br />
<br />
// Multiply ALL elements by 2<br />
odb["Example"] *= 2;<br />
<br />
// odb["Example"] now contains {6, 10}.<br />
</pre><br />
<br />
You can directly iterate over arrays/vectors:<br />
<br />
<pre>// Iterating using standard begin/end.<br />
for (auto it = o["Int Array"].begin(); it != o["Int Array"].end(); it++) {<br />
int val = *it;<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (int val : o["Int Array"]) {<br />
std::cout << val << std::endl;<br />
}<br />
</pre><br />
<br />
== Strings ==<br />
<br />
Strings in the ODB are returned as std::string (unlike the midas.h <code>db_get_value()</code><br />
family of functions, where strings are returned as char*). You may have vectors of strings.<br />
<br />
== Creating new bits of the ODB ==<br />
<br />
You can automatically create bits of the ODB by passing a struct to the<br />
<code>midas::odb</code> constructor, then calling <code>odb::connect()</code>, like:<br />
<br />
<pre>// Define the ODB structure<br />
midas::odb new_bit = {<br />
{"Int32 Key", 42},<br />
{"Bool Key", true},<br />
{"Subdir", {<br />
{"Float key", 1.2f}, // floats must be explicitly specified<br />
}},<br />
{"Int Array", {1, 2, 3}},<br />
{"Double Array", {1.2, 2.3, 3.4}},<br />
{"String Array", {"Hello1", "Hello2", "Hello3"}},<br />
{"Large Array", std::array<int, 10>{} }, // array with explicit size<br />
{"Large String", std::string(63, '\0') }, // string with explicit size<br />
};<br />
<br />
// Then sync the structure. This function<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings");<br />
<br />
// If you make the `write_defaults` argument true, then the function<br />
// - overwrites the value of any keys that are in the ODB with the value in your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
o.connect("/Test/Settings", true);<br />
<br />
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and<br />
// - keeps the existing value of any keys that are in the ODB and your code<br />
// - creates any keys that are in your code but not yet in the ODB<br />
// - deletes any keys that are in the ODB but not your code<br />
// - updates the order of keys in the ODB to match your code<br />
o.connect_and_fix_structure("/Test/Settings");<br />
</pre><br />
<br />
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
existing_key["MyNewSubKey"] = 1.23;<br />
</pre><br />
<br />
You can also create new keys by providing a default value when reading a value. If the key doesn't already exist, the default value will be used.<br />
<br />
<pre>midas::odb existing_key("/MyExistingKey");<br />
double val = existing_key["MyNewSubKey"](1.23);<br />
</pre><br />
<br />
== Iterating over subkeys ==<br />
<br />
You can iterate over subkeys using normal iterator functions.<br />
<br />
<pre>// Iterating using standard begin/end.<br />
midas::odb exp("/Experiment");<br />
<br />
for (auto it = exp.begin(); it != exp.end(); it++) {<br />
midas::odb& subkey = *it;<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
<pre>// Iterating using C++11 range-based for loop.<br />
for (midas::odb& subkey : exp) {<br />
std::cout << subkey.get_name() << " = " << subkey << std::endl;<br />
}<br />
</pre><br />
<br />
You can check whether a subkey exists using <code>odb::is_subkey()</code>.<br />
<br />
== Utility functions ==<br />
<br />
There are various utility functions which can be used:<br />
<br />
==== void odb::create(const char *name, int type) ====<br />
<br />
Simple wrapper around db_create_key() to create a single key in the ODB. <code>type</code> is one of TID_xxx.<br />
<br />
==== void odb::delete_key() ====<br />
<br />
This member function of a midas::odb object deletes that object from the ODB:<br />
<br />
<pre>midas::odb o("/Some/ODB/Path");<br />
o.delete_key();<br />
</pre><br />
<br />
==== int odb::delete_key(const std::string &name) ====<br />
<br />
This function deletes a key or a subtree in the ODB passed by its path in the <code>name</code> argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.<br />
<br />
==== bool odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::exists(const std::string *name) ====<br />
<br />
This boolean function checks if a key given by its name exists in the ODB.<br />
<br />
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====<br />
<br />
These functions set and retrieve the debug flag. If the debug flag is <code>true</code> all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.<br />
<br />
== Callback functions ==<br />
<br />
You may also set up callback functions that are called whenever a value<br />
changes, using the <code>odb::watch()</code> function. Note that you must call<br />
<code>cm_yield()</code> (from midas.h) periodically for this to work - deep down it<br />
is <code>cm_yield()</code> itself that calls your callback function.<br />
<br />
The callback functions can either be a "normal" function or a C++ lambda.<br />
In either case, it should accept one argument - a <code>midas::odb</code> object (passed<br />
by reference) that contains the new state.<br />
<br />
<pre>// Example with a lambda:<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch([](midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
});<br />
</pre><br />
<br />
<pre>// Example with a "normal" function:<br />
void my_function(midas::odb &arg) {<br />
std::cout << "Value of key \"" + arg.get_full_path() + "\" changed to " << arg << std::endl;<br />
}<br />
<br />
midas::odb to_watch("/Experiment");<br />
to_watch.watch(my_function);<br />
</pre><br />
<br />
== Example code ==<br />
<br />
A full working example exploring most of the features can be found in<br />
<code>odbxx/odbxx_test.cxx</code>. The test executable will be compiled as<br />
<code>build/odbxx/odbxx_test</code> (it is not installed in the `bin` directory).</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=MIDAS_environment_variables&diff=3234MIDAS environment variables2023-06-20T07:29:47Z<p>Stefan Ritt: /* MIDAS_EXPNAME */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduction =<br />
MIDAS uses a several environment variables to facilitate the startup of the different applications. Only [[#MIDASSYS|MIDASSYS]] is required. <br />
<br />
The MIDAS environment variables are also discussed in [[Compilation & Build]] and [[QuickStart]].<br />
<br />
;NOTE<br />
:Except where noted, the following environment variables are defined on the '''MIDAS experiment host''', i.e. the host on which the MIDAS experiment shared memories are residing. <br />
<br><br />
---------<br />
<br><br />
= MIDASSYS =<br />
This environmental variable is required. It should point to the main path of the installed MIDAS package on the experiment host e.g.<br />
;csh : setenv MIDASSYS $HOME/packages/midas <br />
;bash : export MIDASSYS=$HOME/packages/midas <br />
The application [[odbedit]] will generate a warning message unless this variable is defined.<br />
<br />
<br />
<br><br />
--------<br />
<br><br />
<br />
= MIDAS_EXPTAB =<br />
This variable specifies the location of the optional [[exptab (Experiment Table)|exptab]] file containing the predefined MIDAS experiment(s), e.g. <br />
<br />
;csh : setenv MIDAS_EXPTAB $HOME/online/exptab<br />
;bash : export MIDAS_EXPTAB = $HOME/online/exptab<br />
<br />
If '''MIDAS_EXPTAB''' is not defined, the default location will be used (i.e. for OS_UNIX: ''/etc'', / and for OS_WINNT: ''\system32, \system '').<br />
<br />
<br />
<br />
<br><br />
-----------<br />
<br><br />
<br />
= MIDAS_SERVER_HOST =<br />
<br />
If all the MIDAS clients run on the '''experiment host''', this variable is NOT defined.<br />
<br />
It is needed when a connection to a remote experiment is requested. In that case, this variable is '''defined on the remote host(s)''', and predefines the name of the [[#Introduction|experiment host]]. In this case, the [[mserver]] client must also be started on the experiment host. See [[Quickstart Linux|Quickstart]] for more information.<br />
<br />
Defining MIDAS_SERVER_HOST makes adding the "''-h <hostname>''" argument to the application command unnecessary (see [[Common Parameters to MIDAS Utilities]]). <br />
<br />
'''MIDAS_SERVER_HOST''' will be superseded by [[#MIDAS_DIR |MIDAS_DIR]] if defined. This variable is valid for Unix as well as Windows OS.<br />
<br />
<br><br />
--------<br />
<br><br />
<br />
= MIDAS_EXPT_NAME =<br />
<br />
<br />
Where there are multiple experiments available on the MIDAS experiment host, this variable predefines the name of the experiment to which the user wishes to connect. It prevents the requested application from asking for the experiment name. Defining MIDAS_EXPT_NAME makes adding the ''"-e <exptname>"'' argument to the application command unnecessary (see [[Common Parameters to MIDAS Utilities]]). <br />
<br />
In addition, when the MIDAS_DIR environment variable is used, then MIDAS_EXPT_NAME must be set to the experiment name. This is equivalent to the exptab file where we also have name and directory. If you use the exptab file, you don't need MIDAS_EXPT_NAME for that.<br />
<br />
<br><br />
--------<br />
<br><br />
<br />
= MIDAS_DIR =<br />
This variable predefines the LOCAL directory path where the shared memories for the experiment are located. Defining this variable results in a single experiment called "Default". Since a given directory path can only refer to a single experiment, '''MIDAS_DIR''' supersedes the ''"-e <exptname>"'' and "''-h <hostname>''" arguments as well as the [[#MIDAS_SERVER_HOST |MIDAS_SERVER_HOST]] and [[#MIDAS_EXPT_NAME|MIDAS_EXPT_NAME]] environment variables.<br />
<br />
;Note : If you wish to use the [[exptab]] file to define multiple experiments on a single host, do not define '''MIDAS_DIR'''. <br />
<br />
<br><br />
------------<br />
<br><br />
<br />
= MIDAS_INVALID_STRING_IS_OK =<br />
;Note<br />
: Environment variable MIDAS_INVALID_STRING_IS_OK and UTF-8 key check added Jan 2017.<br />
<br />
By default, the ODB keys names are checked to ensure they are UTF-8 compliant (see [[ODB Access and Use#ODB Keys|ODB Keys]] for more information). <br />
<br />
Defining environment value '''MIDAS_INVALID_STRING_IS_OK''' (set to any value) disables this key check. The existence of this environment value is only checked for once, when a program starts.<br />
<br />
<br><br />
------------<br />
<br><br />
<br />
<br />
= ROOTSYS =<br />
This variable must point to the ROOT package if generating a complete MIDAS/ROOT analyzer application. If not using ROOT, '''ROOTSYS''' should be undefined.<br />
<br />
<br><br />
-----------<br />
<br><br />
<br />
= CERNLIB_PACK =<br />
In case of HBOOK/PAW analyzer application, this variable should be pointing to a valid cernpacklib.a library. See [[#HAVE_HBOOK |HAVE_HBOOK]].<br />
<br />
<br><br />
----------<br />
<br><br />
<br />
= MIDAS_FRONTEND_INDEX =<br />
This variable predefines the index assigned to the equipment using the event builder option. Useful if the [[Frontend Application|Frontend Applications]] are started from different hosts. Refer to [[Event Builder#Event Builder and related frontend fragment|Event Builder and related frontend fragment]] for more information.<br />
<br><br />
---------<br />
<br><br />
<br />
= MCHART_DIR =<br />
This variable is used for the old [[mchart]] utility.<br />
<br />
<br />
[[Category:Environment variable]] [[Category:Appendices]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=MIDAS_environment_variables&diff=3233MIDAS environment variables2023-06-20T07:29:37Z<p>Stefan Ritt: /* MIDAS_EXPT_NAME */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduction =<br />
MIDAS uses a several environment variables to facilitate the startup of the different applications. Only [[#MIDASSYS|MIDASSYS]] is required. <br />
<br />
The MIDAS environment variables are also discussed in [[Compilation & Build]] and [[QuickStart]].<br />
<br />
;NOTE<br />
:Except where noted, the following environment variables are defined on the '''MIDAS experiment host''', i.e. the host on which the MIDAS experiment shared memories are residing. <br />
<br><br />
---------<br />
<br><br />
= MIDASSYS =<br />
This environmental variable is required. It should point to the main path of the installed MIDAS package on the experiment host e.g.<br />
;csh : setenv MIDASSYS $HOME/packages/midas <br />
;bash : export MIDASSYS=$HOME/packages/midas <br />
The application [[odbedit]] will generate a warning message unless this variable is defined.<br />
<br />
<br />
<br><br />
--------<br />
<br><br />
<br />
= MIDAS_EXPTAB =<br />
This variable specifies the location of the optional [[exptab (Experiment Table)|exptab]] file containing the predefined MIDAS experiment(s), e.g. <br />
<br />
;csh : setenv MIDAS_EXPTAB $HOME/online/exptab<br />
;bash : export MIDAS_EXPTAB = $HOME/online/exptab<br />
<br />
If '''MIDAS_EXPTAB''' is not defined, the default location will be used (i.e. for OS_UNIX: ''/etc'', / and for OS_WINNT: ''\system32, \system '').<br />
<br />
<br />
<br />
<br><br />
-----------<br />
<br><br />
<br />
= MIDAS_SERVER_HOST =<br />
<br />
If all the MIDAS clients run on the '''experiment host''', this variable is NOT defined.<br />
<br />
It is needed when a connection to a remote experiment is requested. In that case, this variable is '''defined on the remote host(s)''', and predefines the name of the [[#Introduction|experiment host]]. In this case, the [[mserver]] client must also be started on the experiment host. See [[Quickstart Linux|Quickstart]] for more information.<br />
<br />
Defining MIDAS_SERVER_HOST makes adding the "''-h <hostname>''" argument to the application command unnecessary (see [[Common Parameters to MIDAS Utilities]]). <br />
<br />
'''MIDAS_SERVER_HOST''' will be superseded by [[#MIDAS_DIR |MIDAS_DIR]] if defined. This variable is valid for Unix as well as Windows OS.<br />
<br />
<br><br />
--------<br />
<br><br />
<br />
= MIDAS_EXPT_NAME =<br />
<br />
<br />
Where there are multiple experiments available on the MIDAS experiment host, this variable predefines the name of the experiment to which the user wishes to connect. It prevents the requested application from asking for the experiment name. Defining MIDAS_EXPT_NAME makes adding the ''"-e <exptname>"'' argument to the application command unnecessary (see [[Common Parameters to MIDAS Utilities]]). <br />
<br />
In addition, when the MIDAS_DIR environment variable is used, then MIDAS_EXPT_NAME must be set to the experiment name. This is equivalent to the exptab file where we also have name and directory. If you use the exptab file, you don't need MIDAS_EXPT_NAME for that.<br />
<br />
<br><br />
--------<br />
<br><br />
<br />
= MIDAS_DIR =<br />
This variable predefines the LOCAL directory path where the shared memories for the experiment are located. Defining this variable results in a single experiment called "Default". Since a given directory path can only refer to a single experiment, '''MIDAS_DIR''' supersedes the ''"-e <exptname>"'' and "''-h <hostname>''" arguments as well as the [[#MIDAS_SERVER_HOST |MIDAS_SERVER_HOST]] and [[#MIDAS_EXPT_NAME|MIDAS_EXPT_NAME]] environment variables.<br />
<br />
;Note : If you wish to use the [[exptab]] file to define multiple experiments on a single host, do not define '''MIDAS_DIR'''. <br />
<br />
<br><br />
------------<br />
<br><br />
<br />
= MIDAS_EXPNAME =<br />
<br />
When the '''MIDAS_DIR''' environment variable is used, then '''MIDAS_EXPNAME''' must be set to the experiment name. This is equivalent to the '''exptab''' file where we also have name and directory. If you use the [[exptab]] file, you don't need '''MIDAS_EXPNAME'''<br />
<br />
<br><br />
------------<br />
<br><br />
<br />
= MIDAS_INVALID_STRING_IS_OK =<br />
;Note<br />
: Environment variable MIDAS_INVALID_STRING_IS_OK and UTF-8 key check added Jan 2017.<br />
<br />
By default, the ODB keys names are checked to ensure they are UTF-8 compliant (see [[ODB Access and Use#ODB Keys|ODB Keys]] for more information). <br />
<br />
Defining environment value '''MIDAS_INVALID_STRING_IS_OK''' (set to any value) disables this key check. The existence of this environment value is only checked for once, when a program starts.<br />
<br />
<br><br />
------------<br />
<br><br />
<br />
<br />
= ROOTSYS =<br />
This variable must point to the ROOT package if generating a complete MIDAS/ROOT analyzer application. If not using ROOT, '''ROOTSYS''' should be undefined.<br />
<br />
<br><br />
-----------<br />
<br><br />
<br />
= CERNLIB_PACK =<br />
In case of HBOOK/PAW analyzer application, this variable should be pointing to a valid cernpacklib.a library. See [[#HAVE_HBOOK |HAVE_HBOOK]].<br />
<br />
<br><br />
----------<br />
<br><br />
<br />
= MIDAS_FRONTEND_INDEX =<br />
This variable predefines the index assigned to the equipment using the event builder option. Useful if the [[Frontend Application|Frontend Applications]] are started from different hosts. Refer to [[Event Builder#Event Builder and related frontend fragment|Event Builder and related frontend fragment]] for more information.<br />
<br><br />
---------<br />
<br><br />
<br />
= MCHART_DIR =<br />
This variable is used for the old [[mchart]] utility.<br />
<br />
<br />
[[Category:Environment variable]] [[Category:Appendices]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=MIDAS_environment_variables&diff=3217MIDAS environment variables2023-05-12T07:03:35Z<p>Stefan Ritt: Added MIDAS_EXPNAME</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Introduction =<br />
MIDAS uses a several environment variables to facilitate the startup of the different applications. Only [[#MIDASSYS|MIDASSYS]] is required. <br />
<br />
The MIDAS environment variables are also discussed in [[Compilation & Build]] and [[QuickStart]].<br />
<br />
;NOTE<br />
:Except where noted, the following environment variables are defined on the '''MIDAS experiment host''', i.e. the host on which the MIDAS experiment shared memories are residing. <br />
<br><br />
---------<br />
<br><br />
= MIDASSYS =<br />
This environmental variable is required. It should point to the main path of the installed MIDAS package on the experiment host e.g.<br />
;csh : setenv MIDASSYS $HOME/packages/midas <br />
;bash : export MIDASSYS=$HOME/packages/midas <br />
The application [[odbedit]] will generate a warning message unless this variable is defined.<br />
<br />
<br />
<br><br />
--------<br />
<br><br />
<br />
= MIDAS_EXPTAB =<br />
This variable specifies the location of the optional [[exptab (Experiment Table)|exptab]] file containing the predefined MIDAS experiment(s), e.g. <br />
<br />
;csh : setenv MIDAS_EXPTAB $HOME/online/exptab<br />
;bash : export MIDAS_EXPTAB = $HOME/online/exptab<br />
<br />
If '''MIDAS_EXPTAB''' is not defined, the default location will be used (i.e. for OS_UNIX: ''/etc'', / and for OS_WINNT: ''\system32, \system '').<br />
<br />
<br />
<br />
<br><br />
-----------<br />
<br><br />
<br />
= MIDAS_SERVER_HOST =<br />
<br />
If all the MIDAS clients run on the '''experiment host''', this variable is NOT defined.<br />
<br />
It is needed when a connection to a remote experiment is requested. In that case, this variable is '''defined on the remote host(s)''', and predefines the name of the [[#Introduction|experiment host]]. In this case, the [[mserver]] client must also be started on the experiment host. See [[Quickstart Linux|Quickstart]] for more information.<br />
<br />
Defining MIDAS_SERVER_HOST makes adding the "''-h <hostname>''" argument to the application command unnecessary (see [[Common Parameters to MIDAS Utilities]]). <br />
<br />
'''MIDAS_SERVER_HOST''' will be superseded by [[#MIDAS_DIR |MIDAS_DIR]] if defined. This variable is valid for Unix as well as Windows OS.<br />
<br />
<br><br />
--------<br />
<br><br />
<br />
= MIDAS_EXPT_NAME =<br />
<br />
<br />
Where there are multiple experiments available on the MIDAS experiment host, this variable predefines the name of the experiment to which the user wishes to connect. It prevents the requested application from asking for the experiment name. Defining MIDAS_EXPT_NAME makes adding the ''"-e <exptname>"'' argument to the application command unnecessary (see [[Common Parameters to MIDAS Utilities]]). <br />
<br />
'''MIDAS_EXPT_NAME''' will be superseded by [[#MIDAS_DIR |MIDAS_DIR]] if defined. This variable is valid for Unix as well as Windows OS.<br />
<br />
<br><br />
--------<br />
<br><br />
<br />
= MIDAS_DIR =<br />
This variable predefines the LOCAL directory path where the shared memories for the experiment are located. Defining this variable results in a single experiment called "Default". Since a given directory path can only refer to a single experiment, '''MIDAS_DIR''' supersedes the ''"-e <exptname>"'' and "''-h <hostname>''" arguments as well as the [[#MIDAS_SERVER_HOST |MIDAS_SERVER_HOST]] and [[#MIDAS_EXPT_NAME|MIDAS_EXPT_NAME]] environment variables.<br />
<br />
;Note : If you wish to use the [[exptab]] file to define multiple experiments on a single host, do not define '''MIDAS_DIR'''. <br />
<br />
<br><br />
------------<br />
<br><br />
<br />
= MIDAS_EXPNAME =<br />
<br />
When the '''MIDAS_DIR''' environment variable is used, then '''MIDAS_EXPNAME''' must be set to the experiment name. This is equivalent to the '''exptab''' file where we also have name and directory. If you use the [[exptab]] file, you don't need '''MIDAS_EXPNAME'''<br />
<br />
<br><br />
------------<br />
<br><br />
<br />
= MIDAS_INVALID_STRING_IS_OK =<br />
;Note<br />
: Environment variable MIDAS_INVALID_STRING_IS_OK and UTF-8 key check added Jan 2017.<br />
<br />
By default, the ODB keys names are checked to ensure they are UTF-8 compliant (see [[ODB Access and Use#ODB Keys|ODB Keys]] for more information). <br />
<br />
Defining environment value '''MIDAS_INVALID_STRING_IS_OK''' (set to any value) disables this key check. The existence of this environment value is only checked for once, when a program starts.<br />
<br />
<br><br />
------------<br />
<br><br />
<br />
<br />
= ROOTSYS =<br />
This variable must point to the ROOT package if generating a complete MIDAS/ROOT analyzer application. If not using ROOT, '''ROOTSYS''' should be undefined.<br />
<br />
<br><br />
-----------<br />
<br><br />
<br />
= CERNLIB_PACK =<br />
In case of HBOOK/PAW analyzer application, this variable should be pointing to a valid cernpacklib.a library. See [[#HAVE_HBOOK |HAVE_HBOOK]].<br />
<br />
<br><br />
----------<br />
<br><br />
<br />
= MIDAS_FRONTEND_INDEX =<br />
This variable predefines the index assigned to the equipment using the event builder option. Useful if the [[Frontend Application|Frontend Applications]] are started from different hosts. Refer to [[Event Builder#Event Builder and related frontend fragment|Event Builder and related frontend fragment]] for more information.<br />
<br><br />
---------<br />
<br><br />
<br />
= MCHART_DIR =<br />
This variable is used for the old [[mchart]] utility.<br />
<br />
<br />
[[Category:Environment variable]] [[Category:Appendices]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Sequencer&diff=3216Sequencer2023-05-10T09:03:59Z<p>Stefan Ritt: /* Introduction */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Sequencer ODB tree]]<br />
* [[Sequencer Page]]<br />
* [[mhttpd]]<br />
</div><br />
<br />
= Introduction =<br />
A Sequencer for starting and stopping runs is part of the MIDAS distribution. It uses a simple Midas Script Language (MSL) for commands.<br />
<br />
The sequencer runs as a separate process and must be started before running a sequence via the command '''msequencer'''. The first time the {{Button|name=Sequencer}} button on the [[Status Page]] is pressed, the ODB [[/Sequencer ODB tree|Sequencer Tree]] is created. If the Sequencer button is not present on the [[Status Page]], it may have been [[Status Page#page-switch-buttons|suppressed]].<br />
<br />
The sequencer runs scripts in MSL (Midas Script Language) format, which reside on the server (where {{Utility|name=mhttpd}} is running). The sequencer state is completely stored in the ODB, meaning that even if {{Utility|name=mhttpd}} or the sequencer is stopped and restarted, the active sequence operation continues exactly where it has been stopped.<br />
<br />
Refer to the [[/Sequencer ODB tree]] for more details on its organization. <br />
<br />
For instructions on '''getting started with the sequencer''', see [[Sequencer Page]]<br />
<br />
= Controlling the sequencer from custom pages =<br />
<br />
The normal interface is the "Sequencer" status page in MIDAS. In addition, the sequencer can be controlled by other means. The ODB contains a directory '''/Sequencer/Command''', which contains the currently loaded filename and three switches. To load new file and compile it, set the '''Load filename''' and the '''Load new file''' to '''yes'''. To start the script, set '''Start script''' to '''yes'''. This can be done on a custom page via<br />
<br />
modbset(['/Sequencer/Command/Load filename',<br />
'/Sequencer/Command/Load new file',<br />
'/Sequencer/Command/Start script'],<br />
['test.msl', 'Yes', 'Yes'])<br />
<br />
which can be put in the '''onclick=...''' method of a button, or even into a side menu via an alias by putting an ODB key into '''/Alias/xxx''' and setting it to '''javascript:modbset(...)'''.<br />
<br />
= Sequencer Commands =<br />
The following commands are implemented in the MIDAS Sequencer. The left syntax is for the XML file, the right for the MSL (Midas Script Language).<br />
<br />
Variable names are indicated in italic, options are enclosed in [brackets].<br />
<br />
{| class="wikitable" style="text-align: left; color: black; tr:nth-child(even) {background-color: #F0F0F0};" <br />
!style="width: 20%;background-color:#C0C0C0;"|MSL format<br />
!style="width: 60%;background-color:#C0C0C0;"|Description<br />
|-<br />
||INCLUDE ''name''<br />
||Include another MSL file ''name''.msl<br />
|-<br />
||BREAK<br />
||Break (finish prematurely) a loop. This is usually used together with an IF statement to abort a loop like<br />
<pre><br />
LOOP 10<br />
IF ($x > 0)<br />
BREAK<br />
ENDIF<br />
ENDLOOP<br />
</pre><br />
|-<br />
||CALL ''name'', ''a'', ''b'', ''c'', ...<br />
||Call a subroutine. Optional parameters ''a'',''b'',''c''... are passed to the subroutine, where they can be referenced via $1, $2, $3, etc. The subroutine can either reside in the current file, or in a library file which is included.<br />
|-<br />
||CAT ''name'', ''a'', ''b'', ''c'', ...<br />
||Concatenates the strings ''a'',''b'',''c'',... into a single variable name . Can be referenced with $''name''. If a string must contain a comma, it can be enclosed in quotes such as in '''CAT title $run, ",", $n events'''<br />
|-<br />
||COMMENT ''comment''<br />
||A comment for this XML file, for information only. This comment is shown in the title bar next to the file name if one runs a script. This can be helpful if one has many XML files and the file name only is not enough to supply enough information.<br />
|-<br />
||GOTO ''n''<br />
||Jump to line ''n'' in script<br />
|-<br />
||IF ''con'' <br>or<br>IF (''con'')<br><br />
... <br><br />
ELSE <br><br />
... <br><br />
ENDIF<br />
||Statements between "IF" and "ENDIF" are only executed if condition "con" is true, otherwise the statements between the optional "ELSE" and "ENDIF" are executed. The condition can use any math expression using variables via $name together with operators "<", "<=", ">", ">=", "==", "!=", "&" (bitwise AND). Up to four nested IF statements are possible. An example would be "IF ($x+3 > $y*4+$z^2)".<br />
|- <br />
||LIBRARY ''name''<br />
||Indicates that the current file is a library (which can be included by other files). A library usually consists of a set of subroutines.<br />
<br />
|-<br />
||LOOP [''name'' ,] ''n'' ... ENDLOOP<br />
||To execute a loop ''n'' times. For infinite loops, "infinite" can be specified as ''n''. Optionally, the loop variable running from 1...''n'' can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||LOOP ''name'', ''a'', ''b'', ''c'', ... ... ENDLOOP<br />
||Loop though a list of values. The loop is executed once for each value, which is stored into the variable ''name'' so it can be accessed inside the loop via $''name''.<br />
<br />
|-<br />
||MESSAGE ''message'' [,1]<br />
||Opens a message box in the browser containing the text message. If wait="1", then the sequencer waits until the box is closed by the user before continuing.<br />
|-<br />
||MSG ''message'' [,<type>]<br />
||Produces a midas message going into the message buffer. The optional <type> can be any of ERROR, INFO, DEBUG, LOG, TALK. If no type is given, INFO is used.<br />
|-<br />
||ODBCREATE ''path'', ''type'' [, ''size'']<br />
||Create an ODB key of type ''type'' and optional size ''size'' (for arrays). ''type'' can be ''UNIT8'', ''INT8'', ''UNIT16'', ''INT16'', ''UNIT32'', ''INT32'', ''BOOL'', ''FLOAT'', ''DOUBLE'', ''STRING''<br />
|-<br />
||ODBDELETE ''path''<br />
||Delete an ODB key. If ''path'' points to a ODB subdirectory, delete the whole subdirectory.<br />
|-<br />
||ODBGET ''path'', ''name''<br />
||To get a value from a variable ''name''. The variable can then be referenced with $''name''. If the ODB value is an array, the index can be specified like via a constant or expression:<br><br />
<pre><br />
ODBGET "/Path/key[15]", v<br />
ODBGET "/Path/key[$i + 15]", v<br />
</pre><br />
|-<br />
||ODBINC ''path'' [, ''delta'']<br />
||To increment a value in the ODB. ''delta'' can be positive or negative. If no "delta" is given, 1 is used.<br />
|-<br />
||ODBSET ''path'', ''value'' [, 0|1]<br />
||To set a value in the ODB. ''value'' can be any math expression containing variables (preceded by a '$'). ''path'' can also contain variables (preceded by a '$'), but no math expressions.<br><br />
If the ODB key referenced by ''path'' is an array, the index can be specified such as<br><br />
<pre><br />
ODBSET "/Path/value[3]", 1 # individual value<br />
ODBSET "/Path/value[3-5]", 2 # index range 3,4,5<br />
ODBSET "/Path/value[*]", 0 # all values of array<br />
</pre><br />
The path value can match multiple keys using the '?' and '*' wildcard (one or any number of characters respectively). This is useful to set the same sub-path on multiple folders:<br> <br />
<pre><br />
ODBSET "/Path/to/somewhere/folder0/value", "1"<br />
ODBSET "/Path/to/somewhere/folder1/value", "1" <br />
ODBSET "/Path/to/somewhere/folder2/value", "1"<br />
</pre><br />
can be shortened as:<br><br />
<pre><br />
ODBSET "/Path/to/somewhere/folder*/value", "1"<br />
</pre><br />
The notify flag specifies if possible hot-links to this ODB value are notified. This can be useful if a front-end program has many parameters, which must be set for a specific run. The front-end usually has a hot-link to its parameters, so on each modification a callback function in the front-end is called which usually modifies some hardware. If there are many ODBSet statements for all parameters, the callback would be executed on each ODBSet, so the hardware would be reconfigured many times slowing down the startup of a run. In order to prevent this, notify="0" can be specified for all ODBSet statements except the last one which contains notify="1", so the callback function is called only once at the end. <br />
|-<br />
||ODBLOAD ''file'' [, ''path'']<br />
||Load an external file into the ODB.<br><br />
JSON, XML and ODB file format are supported with the value inferred by the content of the file.<br />
The optional path marks the position where to load the given file into the ODB, if no position is given the file is loaded from ODB root.<br><br />
For the input file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "$" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file.<br />
|-<br />
||ODBSAVE ''path'', ''file''<br />
||Save part of the ODB to an external file.<br><br />
The file extension is used to determine the format. Possible values are ".json", ".xml" and ".odb".<br />
The path can point to an individual value in the ODB or to a whole subtree.<br><br />
For the output file several options are supported: if the filename starts with "/" is is assumed to be an absolute path, if it starts with a "%" the path starts from the Sequencer root folder stored in the ODB at /Sequencer/Path. In all other cases it is intended as a relative path to the local MSL file. If the file name contains a variable (starting with '$') it gets evaluated before saving.<br />
|-<br />
||ODBSUBDIR ''path'' ...ENDODBSUBDIR<br />
||If one wants to se several ODB values in the same directory, the ODBSet commands can be rather long if the path is long. Using this command, the subdir can be specified for all commands between the opening and ending tags. So instead of writing <br><br />
<pre><br />
ODBSET /Very/long/path/into/the/odb/value1, 1<br />
ODBSET /Very/long/path/into/the/odb/value2, 1<br />
ODBSET /Very/long/path/into/the/odb/value3, 1<br />
ODBSET /Very/long/path/into/the/odb/value4, 1<br />
</pre><br />
one can write <br><br />
<pre><br />
ODBSUBDIR /Very/long/path/into/the/odb<br />
ODBSET value1, 1<br />
ODBSET value2, 1<br />
ODBSET value3, 1<br />
ODBSET value4, 1<br />
ENDODBSUBDIR<br />
</pre><br />
|-<br />
||PARAM ''name'', ''comment'', [''default''], [''a'',''b'',''c'' | bool]<br />
||When starting a script, a start page is shown where one can enter additional parameters for the script. Parameters can be defined either centrally for all scripts in the ODB under /Experiment/Edit on sequence. This subdirectory in the ODB can contain links to ODB values, which are queried at the start page. In addition, each script can define additional parameters appended to this list. They will be stored under /Sequencer/Variables and can be referenced inside the script via $name, where name can be any variable name specified in the parameter statement. The optional "comment" is shown on the start page below the parameter name. An optional "default" value can be specified for each parameter. If only certain options are possible for the parameter, they can be defined via the options list. The web page will then contain a drop-down list showing the options. In case of type="bool", a checkbox will be shown.<br />
|-<br />
||RUNDESCRIPTION ''description''<br />
||a run description which is stored under /Experiment/Run Parameters/Run Description .<br />
|-<br />
||SCRIPT ''script'' [, a, b, c, ...]<br />
||To call a script on the server side. Optionally, pass parameters to the script.<br />
|-<br />
||SET ''name'', ''value''<br><br />
or<br><br />
''name'' = ''value''<br />
||Sets the variable ''name'' to "value". The variable can then be referenced later in the script by putting a "$" in front of the name like $"name". ''value'' can be a simple number or a complex expression containing other variables (preceded by a ''$'') and calculations such as ''$v + 3*$x * sin($t)''. Following functions are available: ''abs, acos, asin, atan, atan2, ceil, cos. cosh, e, exp, fac, floor, ln, log, pi, pow, sin, sinh, sqrt, tan, tanh''. A variable can be an array by using square brackets, like "a[10] = 5" or "x = $a[10]".<br />
|-<br />
||SUBROUTINE ''name'' ... ENDSUBROUTINE<br />
||Declares a subroutine which can be called via CALL.<br />
|-<br />
||TRANSITION start | stop | pause | resume<br />
||To start, stop, pause or resume a run<br />
|-<br />
||WAIT events | ODBvalue | seconds, [''ODB path''], [''op''], [''value'']<br />
||Wait until a number of events is acquired (testing /Equipment/Trigger/Statistics/Events sent), or until a value in the ODB exceeds value, or wait for ''value'' seconds. If the operand ''op'' is given, the ODB value is compared with value using this operand. So one could wait until an ODB value is equal to value or becomes smaller than value. Here is an example of such a statement which waits until some high voltage is below 100 V. <br />
<pre><br />
WAIT ODBvalue, /Equipment/HV/Variables/Measured[3], <, 100<br />
</pre><br />
|}<br />
<br />
= MSL Examples =<br />
The following example is a simple script, which writes a run description to the ODB, asks for a number of runs, then executes the runs, each running for 60 seconds.<br />
<br />
<pre><br />
COMMENT "This is a MSL test file"<br />
RUNDESCRIPTION "Test run"<br />
PARAM runs<br />
<br />
LOOP $runs<br />
TRANSITION START<br />
WAIT Seconds, 60<br />
TRANSITION STOP<br />
ENDLOOP<br />
</pre><br />
<br />
The following script is a more complex example, which measures a I-V curve (e.g. of a SiPM detector) and stores it in the ODB. It assumes a power supply operated by a front-end and linked to /Equipment/KEYTHLEY/. The I-V curve in the ODB can be plotted with a midas custom page.<br />
<br />
<pre><br />
#<br />
# I-V-Curve test with parameter specification at startup<br />
#<br />
<br />
PARAM start_voltage, "Starting voltage"<br />
PARAM stop_voltage, "Stop voltage"<br />
PARAM num_steps, "Number of steps"<br />
<br />
# Calculate step size<br />
num_steps = $num_steps + 1 # add one step for stop_voltage<br />
step_size = ($stop_voltage-$start_voltage) / ($num_steps-1)<br />
<br />
# Initialize measurement arrays at startup<br />
ODBCREATE /Equipment/Test/Variables/Voltage, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/Current, FLOAT, $num_steps<br />
ODBCREATE /Equipment/Test/Variables/V, FLOAT<br />
<br />
# Erase any previously stored array<br />
ODBSET /Equipment/Test/Variables/Voltage[*], 0<br />
ODBSET /Equipment/Test/Variables/Current[*], 0<br />
<br />
v = $start_voltage<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
current = 0<br />
<br />
# Turn on Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 1<br />
# Wait to turn on<br />
WAIT SECONDS, 2<br />
<br />
<br />
# Looping starts at 1<br />
LOOP i, $num_steps<br />
# Store voltage in array and in variable<br />
ODBSET /Equipment/Test/Variables/Voltage[$i-1], $v<br />
ODBSET /Equipment/Test/Variables/V, $v<br />
<br />
# Set voltage and measure<br />
ODBSET /Equipment/KEITHLEY/Variables/Demand Voltage, $v<br />
# Wait for measurement to be stored in Current<br />
WAIT SECONDS, 10<br />
ODBGET /Equipment/KEITHLEY/Variables/Current, current<br />
<br />
# Outputting current to ODB array<br />
ODBSET /Equipment/Test/Variables/Current[$i-1], $current<br />
<br />
# increment voltage<br />
v = $v + $step_size<br />
ENDLOOP<br />
<br />
# Turn off Keithley<br />
ODBSET /Equipment/KEITHLEY/Variables/Set State, 0<br />
<br />
# Wait to turn off<br />
WAIT SECONDS, 1<br />
</pre><br />
<br />
[[Category:Sequencer]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Alarm_System&diff=3215Alarm System2023-05-05T14:32:12Z<p>Stefan Ritt: /* Alarm triggering Telegram notifications */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Alarms ODB tree]]<br />
* [[Alarms Page]]<br />
* [[Mhttpd|mhttpd MIDAS web server]]<br />
</div><br />
<br />
= Introduction =<br />
MIDAS provides an alarm system, which by default is turned off. When the alarm system is [[/Alarms ODB tree#Alarm system active|activated]] and an alarm condition is detected, alarm messages are sent by the system which appear as an alarm banner on the [[Status Page|mhttpd status page]], and as a message on any windows running [[odbedit]] clients. The alarm system is flexible and can be extensively customized for each experiment<br />
using the [[Alarms Page|mhttpd Alarms Page]] or [[odbedit]]. <br />
<br />
The alarm system is built-in and part of the main experiment scheduler. This means no separate task is necessary to benefit from the alarm system. Its setup and activation is done through the '''[[/Alarms ODB tree]]'''. The alarm system includes several other features such as sequencing and control of the experiment. The alarm capabilities are:<br />
* Alarm setting on any ODB variable against a threshold parameter.<br />
* Alarm triggered by evaluated condition<br />
* Selection of Alarm check frequency<br />
* Selection of Alarm trigger frequency<br />
* Customization alarm scheme; under this scheme multiple choices of alarm type can be selected<br />
* Selection of alarm message destination (to system message log or to elog)<br />
* email or SMS alerts can be sent<br />
* Alarm triggered when a Program is not running <br />
<br />
<br />
= Implementation of the MIDAS Alarm System =<br />
<br />
The alarm system source code is [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/alarm_8c_source.html alarm.c].<br />
Alarms are checked inside [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html alarm.c::al_check()]. This function is called by cm_yield() every 10 seconds and by rpc_server_thread(), also every 10 seconds. For remote MIDAS clients, their al_check() issues an RPC_AL_CHECK RPC call into the MIDAS server utility [[mserver]], where rpc_server_dispatch() calls the local al_check(). As result, all alarm checks run inside a process directly attached to the local MIDAS shared memory (inside a local client or inside an mserver process for a remote client). Each and every MIDAS client runs the alarm checks. To prevent race conditions between different MIDAS clients, access to al_check() is serialized using the ALARM semaphore. Inside al_check(), alarms are triggered using <br />
[http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_trigger_alarm()], which in turn calls al_trigger_class(). Inside al_trigger_class(), the alarm is recorded into an elog or into [[Message System|midas.log]] using cm_msg(MTALK).<br />
<br />
Special note should be made of the ODB setting [[/Alarms ODB tree#System message interval|system message interval]], which has a surprising effect - after an alarm is recorded into system messages (using cm_msg(MTALK)), no record is made of any subsequent alarms until the time interval set by this variable elapses. With default value of 60 seconds, after one alarm, no more alarms are recorded for 60 seconds. Also, because all the alarms are checked at the same time, '''only the first''' triggered alarm will be recorded.<br />
<br />
As of alarm.c rev 4683, {{Odbpath|path=/Alarms/System message interval}} is set to 0 ensures that every alarm is recorded into the [[Message System#MIDAS Log file|MIDAS log file]]. (In previous revisions, this setting may still miss some alarms).<br />
<br />
<br />
<br />
<br />
<div id="Alarm class"></div><br />
= Alarms structure =<br />
The [[/Alarms ODB tree]] structure is split into 2 sections:<br />
*"Alarms" which define the condition to be tested. The user can create as many [[/Alarms ODB tree#Alarms subtree|Alarms]] as desired, but each must be one of the four defined [[#Alarm Types|Alarm types]] .<br />
*"Classes" which define the action to be taken when the alarm occurs. Two Classes (Alarm and Warning) are defined by default. The user can add more [[/Alarms ODB tree#Classes subtree|Classes]] as desired.<br />
<br><br><br />
<br />
In order to make the system flexible, each alarm class may perform different actions when an alarm is given. For example, it may<br />
* write a system message (see [[/Alarms ODB tree#Write System Message|Write System Message]])<br />
* write to the elog (see [[/Alarms ODB tree#Write Elog Message|Write Elog Message]])<br />
* stop the run (see [[/Alarms ODB tree#Stop run|Stop run]])<br />
* spawn a detached script listed in the ODB variable [[/Alarms ODB tree#Execute command|Execute command]]. This feature is used when an Alarm triggers Email or SMS alerts (see [[#Alarm triggering Email or SMS alerts|example]]).<br />
<br><br />
<br />
= Alarm Types =<br />
<br />
The four available Alarm Types are shown in Table 1. They are defined in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/midas_8h.html&nbsp midas.h]. The alarm type is entered into the [[/Alarms ODB tree#Type|Type]] key.<br />
<br />
<br />
{| style="text-align: left; width: 100%; background-color: white;" border="3" cellpadding="2" cellspacing="2"<br />
|+ Table 1 : Defined Alarm Types<br />
|-<br />
| colspan="2" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Alarm Type<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | INT value<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Explanation<br />
<br />
|- <br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: bold;" |Internal alarms<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: normal;" | AT_INTERNAL<br />
|1<br />
|Trigger on internal (program) alarm setting through the use of the al_...() functions. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Program alarms<br />
|AT_PROGRAM<br />
|2<br />
|Triggered on condition of the state of the defined task (i.e. program not running)<br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Evaluated alarms<br />
|AT_EVALUATED<br />
|3<br />
|Triggered by ODB value on given arithmetical condition. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Periodic alarms<br />
|AT_PERIODIC<br />
|4<br />
|Triggered by timeout condition defined in the alarm setting. <br />
<br />
|}<br />
<br />
<br />
== Program Alarm ==<br />
<br />
Program (or rather "Program not running") alarms, when enabled, warn the user when a program is not running.<br />
<br />
Program alarms are enabled by setting the ODB key [[/Programs ODB tree#Alarm class|/Programs/<client-name>/Alarm class]] to a valid Alarm class specified in the [[/Alarms ODB tree]]. The first time the alarm is triggered, an <span style="color: purple; font-style:italic;">/Alarms/Alarms/<client-name></span> subtree will be created automatically. The program alarm will not be visible in the [[Alarms Page]] until the alarm has triggered, and the subtree created.<br />
<br />
<br />
The alarm system periodically calls [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_check()]. This causes every client listed in the {{Odbpath|path=/Programs}} ODB tree to be tested using [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__cmfunctionc.html cm_exist()] to see if it is running. If the client is not running, the time of first failure is recorded in the ODB key [[/Programs ODB tree#First failed|/Programs/<client-name>/First failed]].<br />
<br />
If the client has not been running for longer than the time set in ODB key [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]], a "Program not running" alarm is triggered (if enabled by [[/Programs ODB tree#Alarm class|Alarm class]]) and the program is restarted (if enabled by [[/Programs ODB tree#Auto restart|/Programs/<client-name>/Auto restart]] and a valid [[/Programs ODB tree#Start command|Start command]] is supplied).<br />
<br />
The "not running" condition is tested every 10 seconds (each time al_check() is called), but the frequency of ''Program not running'' alarms can be reduced by increasing the value of the ODB key<br />
[[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]<br />
(default value 60 seconds). This can be useful if [[/Alarms ODB tree#System message interval|System message interval]] in the specified alarm class subtree is set to zero.<br />
<br />
<br />
== Periodic Alarm ==<br />
The periodic alarm is activated periodically according to the time in [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]. An example of a periodic alarm is "Demo Periodic" in the [[/Alarms ODB tree#Example|example]].<br />
<br />
== Evaluated Alarm ==<br />
<br />
Evaluated alarms require an ''alarm condition'' which is entered into the ODB key [[/Alarms ODB tree#Condition|Condition]] in the [[/Alarms ODB tree#<alarm_name> subtree|<alarm_name> subtree]].<br />
The condition may be simply a '''comparison''' between any ODB variable and a threshold parameter, e.g.<br />
<br />
/Runinfo/Run number > 100<br />
or it may be an '''evaluated condition'''. One can write conditions like<br />
<br />
/Equipment/HV/Variables/Input[*] < 100<br />
or<br />
<br />
/Equipment/HV/Variables/Input[2-3] < 100<br />
to check all values from an array or a certain range. If one array element fulfills the alarm condition, the alarm is triggered. In addition, bit-wise alarm conditions are possible, e.g.<br />
<br />
/Equipment/Environment/Variables/Input[0] & 8<br />
The alarm is triggered if bit #3 is set in Input[0].<br />
<br />
The value of an evaluated alarm is computed using al_evaluate_condition() in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/alarm_8c_source.html alarm.c].<br />
<br />
== Internal Alarm ==<br />
These are triggered in a program using a call to <br />
[https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__alfunctioncode.html al_trigger_alarm()]. See also description of al_trigger_alarm() sequence [[#Implementation of the MIDAS Alarm System|above]].<br />
<br />
<br />
There is nothing surprising in these alarms. Each alarm is checked with a time period set by ODB key [[ /Alarms ODB tree#Check interval|Check interval]] in the [[/Alarms ODB tree]].<br />
<br />
= Alarm triggering Email or SMS alerts =<br />
<br />
It is possible to have the MIDAS alarm system send email or SMS alerts to cell phones when alarms are triggered. This can be configured by defining an ODB alarm on a critical ODB parameter, e.g.<br />
<br />
/Alarms/Alarms/Liquid Level<br />
Active y<br />
Triggered 0 (0x0)<br />
Type 3 (0x3)<br />
Check interval 60 (0x3C)<br />
Checked last 1227690148 (0x492D10A4)<br />
Time triggered first (empty)<br />
Time triggered last (empty)<br />
Condition /Equipment/Environment/Variables/Input[0] < 10<br />
Alarm Class Level Alarm<br />
Alarm Message Liquid Level is only %s<br />
<br />
In this example, the alarm triggers an alarm of class "Level Alarm". This alarm class is defined as follows:<br />
<br />
/Alarms/Classes/Level Alarm<br />
Write system message y<br />
Write Elog message n<br />
System message interval 600 (0x258)<br />
System message last 0 (0x0)<br />
Execute command /home/midas/level_alarm '%s'<br />
Execute interval 1800 (0x708)<br />
Execute last 0 (0x0)<br />
Stop run n<br />
Display BGColor red<br />
Display FGColor black<br />
<br />
The key here is to call a script "level_alarm", which can send emails. Use something like:<br />
<br />
#/bin/csh<br />
echo $1 | mail -s \"Level Alarm\" your.name@domain.edu<br />
odbedit -c 'msg 2 level_alarm \"Alarm was sent to your.name@domain.edu\"'<br />
<br />
The second command just generates a MIDAS system message for confirmation. Most cell phones (depends on the provider) have an email address. If you send an email there, it will be translated into a SMS message.<br />
<br />
The script file above can of course be more complicated. A perl script could be used that parses an address list, so other interested parties can register by adding his/her email address to that list. The script may also collects some other slow control variables (like pressure, temperature) and combine them into the SMS message.<br />
<br />
For very sensitive systems, having an alarm via SMS may not be sufficient, since the alarm system could be down (e.g. computer crash, network failure). In this case 'negative alarms' can be used. For example, every 30 minutes the system may send an SMS with the current parameter values. If the expected message is not received, it may indicate that something in the MIDAS system is wrong.<br />
<br />
= Alarm triggering Slack notifications =<br />
<br />
A more modern way for notification is to use messengers apps such as Slack. To send alarms to Slack, do the following:<br />
<br />
* Go to https://api.slack.com/apps<br />
* Create an App "MIDAS alarms", select "From scratch"<br />
* Name it "MIDAS alarm" or similar, select your workspace<br />
* Click on "Add features and functionality"<br />
* Select "Incoming Webhooks"<br />
* Activate Incoming Webhooks<br />
* Click on "Add New Webhook to Workspace"<br />
* Select channel where alarms get posted, allow access<br />
* Copy sample curl request and replace "Hello World" by "$1", such as: <br />
<br />
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$1\"}' https://hooks.slack.com/services/[xxx]/[yyy]/[zzz]<br />
<br />
(leave they keys xxx, yyy, zzz as shown on the web page).<br />
<br />
* Call the curl command from the alarm system by putting the above command under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
<br />
<br />
= Alarm triggering Telegram notifications =<br />
<br />
A telegram bot needs to be created to post Midas alarms into telegram channels.<br />
<br />
* Go to https://web.telegram.org and login by scanning the QR-code with the telegram app on your phone<br />
* Go to https://t.me/botfather<br />
* Click on "open in web"<br />
* A chat window will open. <br />
* Send "/newbot" into the chat and assign name and username to your bot<br />
* At the end of the bot creation a access token will be displayed. Save that access token.<br />
* Post "/mybots" into the chat<br />
* Select the bot that was just created<br />
* Click on "Bot settings"<br />
* Click on "Allow Groups"<br />
* Enable groups for the bot<br />
* Click on "back to settings"<br />
* Click on Channel Admin Rights<br />
* Click on "Manage channel" (a checkmark should appear next to the text)<br />
* Close this chat.<br />
<br />
* Add the created bot to a telegram group chat via searching for the bot name in "add members" (same as adding new People to the group)<br />
* Now you need the chat_id of the same group chat. When you open the group chat in a webbrowser the URL should look like this https://web.telegram.org/a/#-1700000000 (where 1700000000 will be a different number for you)<br />
* The group chat_id is then -1001700000000 (add -100 in front of the number in your URL)<br />
* With the chat_id from above and the access token from the bot creation process you can post to the telegram group like this: (replace <chat_id> and <accessToken> with your numbers)<br />
<br />
curl -s -X POST "https://api.telegram.org/bot<accessToken>/sendMessage" -d chat_id=<chat_id> -d text="🚨 Midas alarm triggered with message = $1"<br />
<br />
* Create a shell script with this command<br />
* Call the shell script from the alarm system by putting <code>path/to/script.sh '%1$s'</code> under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
* Should you want to post the same alarm to multiple systems you can just separate scripts in <code>/Alarms/Classes/All/Execute command</code> with a semicolon <code>path/to/script.sh '%1$s';path/to/script2.sh '%1$s'</code><br />
<br />
= Alarm triggering Mattermost notifications =<br />
<br />
First a mattermost bot needs to be created. To do this you need to be an Admin of the Team that the bot should post to.<br />
<br />
* In the drop down menu on the top left corner of the mattermost page click on "Integrations" (note: this field will be missing if you are not an Admin of the selected mattermost team) <br />
* Select "Incoming Webhooks"<br />
* Click on "Add Incoming Webhook"<br />
* Give the bot a title and username (e.g. midasalarms) and select the channel to post to, click "save"<br />
* Now a URL for the incoming webhook should be displayed (https://mattermost.gitlab.rlp.net/hooks/<token>). Copy that URL into the curl command in the following script:<br />
<br />
<br />
#!/bin/bash<br />
alarm_message=$1<br />
generate_post_data()<br />
{<br />
cat <<EOF<br />
{<br />
"text": "@all Midas alarm triggered with message = $alarm_message", <br />
"icon_emoji":":rotating_light:", <br />
"username":"Midas"}<br />
EOF<br />
}<br />
curl -i -X POST -H 'Content-Type: application/jsoni' --data "$(generate_post_data)" https://mattermost.gitlab.rlp.net/hooks/<token><br />
<br />
- Call the shell script from the alarm system by putting <code>path/to/script.sh '%1$s'</code> under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
<br />
[[Category:Alarms]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Alarm_System&diff=3214Alarm System2023-05-05T14:31:33Z<p>Stefan Ritt: /* Alarm triggering Mattermost notifications */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Alarms ODB tree]]<br />
* [[Alarms Page]]<br />
* [[Mhttpd|mhttpd MIDAS web server]]<br />
</div><br />
<br />
= Introduction =<br />
MIDAS provides an alarm system, which by default is turned off. When the alarm system is [[/Alarms ODB tree#Alarm system active|activated]] and an alarm condition is detected, alarm messages are sent by the system which appear as an alarm banner on the [[Status Page|mhttpd status page]], and as a message on any windows running [[odbedit]] clients. The alarm system is flexible and can be extensively customized for each experiment<br />
using the [[Alarms Page|mhttpd Alarms Page]] or [[odbedit]]. <br />
<br />
The alarm system is built-in and part of the main experiment scheduler. This means no separate task is necessary to benefit from the alarm system. Its setup and activation is done through the '''[[/Alarms ODB tree]]'''. The alarm system includes several other features such as sequencing and control of the experiment. The alarm capabilities are:<br />
* Alarm setting on any ODB variable against a threshold parameter.<br />
* Alarm triggered by evaluated condition<br />
* Selection of Alarm check frequency<br />
* Selection of Alarm trigger frequency<br />
* Customization alarm scheme; under this scheme multiple choices of alarm type can be selected<br />
* Selection of alarm message destination (to system message log or to elog)<br />
* email or SMS alerts can be sent<br />
* Alarm triggered when a Program is not running <br />
<br />
<br />
= Implementation of the MIDAS Alarm System =<br />
<br />
The alarm system source code is [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/alarm_8c_source.html alarm.c].<br />
Alarms are checked inside [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html alarm.c::al_check()]. This function is called by cm_yield() every 10 seconds and by rpc_server_thread(), also every 10 seconds. For remote MIDAS clients, their al_check() issues an RPC_AL_CHECK RPC call into the MIDAS server utility [[mserver]], where rpc_server_dispatch() calls the local al_check(). As result, all alarm checks run inside a process directly attached to the local MIDAS shared memory (inside a local client or inside an mserver process for a remote client). Each and every MIDAS client runs the alarm checks. To prevent race conditions between different MIDAS clients, access to al_check() is serialized using the ALARM semaphore. Inside al_check(), alarms are triggered using <br />
[http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_trigger_alarm()], which in turn calls al_trigger_class(). Inside al_trigger_class(), the alarm is recorded into an elog or into [[Message System|midas.log]] using cm_msg(MTALK).<br />
<br />
Special note should be made of the ODB setting [[/Alarms ODB tree#System message interval|system message interval]], which has a surprising effect - after an alarm is recorded into system messages (using cm_msg(MTALK)), no record is made of any subsequent alarms until the time interval set by this variable elapses. With default value of 60 seconds, after one alarm, no more alarms are recorded for 60 seconds. Also, because all the alarms are checked at the same time, '''only the first''' triggered alarm will be recorded.<br />
<br />
As of alarm.c rev 4683, {{Odbpath|path=/Alarms/System message interval}} is set to 0 ensures that every alarm is recorded into the [[Message System#MIDAS Log file|MIDAS log file]]. (In previous revisions, this setting may still miss some alarms).<br />
<br />
<br />
<br />
<br />
<div id="Alarm class"></div><br />
= Alarms structure =<br />
The [[/Alarms ODB tree]] structure is split into 2 sections:<br />
*"Alarms" which define the condition to be tested. The user can create as many [[/Alarms ODB tree#Alarms subtree|Alarms]] as desired, but each must be one of the four defined [[#Alarm Types|Alarm types]] .<br />
*"Classes" which define the action to be taken when the alarm occurs. Two Classes (Alarm and Warning) are defined by default. The user can add more [[/Alarms ODB tree#Classes subtree|Classes]] as desired.<br />
<br><br><br />
<br />
In order to make the system flexible, each alarm class may perform different actions when an alarm is given. For example, it may<br />
* write a system message (see [[/Alarms ODB tree#Write System Message|Write System Message]])<br />
* write to the elog (see [[/Alarms ODB tree#Write Elog Message|Write Elog Message]])<br />
* stop the run (see [[/Alarms ODB tree#Stop run|Stop run]])<br />
* spawn a detached script listed in the ODB variable [[/Alarms ODB tree#Execute command|Execute command]]. This feature is used when an Alarm triggers Email or SMS alerts (see [[#Alarm triggering Email or SMS alerts|example]]).<br />
<br><br />
<br />
= Alarm Types =<br />
<br />
The four available Alarm Types are shown in Table 1. They are defined in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/midas_8h.html&nbsp midas.h]. The alarm type is entered into the [[/Alarms ODB tree#Type|Type]] key.<br />
<br />
<br />
{| style="text-align: left; width: 100%; background-color: white;" border="3" cellpadding="2" cellspacing="2"<br />
|+ Table 1 : Defined Alarm Types<br />
|-<br />
| colspan="2" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Alarm Type<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | INT value<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Explanation<br />
<br />
|- <br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: bold;" |Internal alarms<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: normal;" | AT_INTERNAL<br />
|1<br />
|Trigger on internal (program) alarm setting through the use of the al_...() functions. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Program alarms<br />
|AT_PROGRAM<br />
|2<br />
|Triggered on condition of the state of the defined task (i.e. program not running)<br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Evaluated alarms<br />
|AT_EVALUATED<br />
|3<br />
|Triggered by ODB value on given arithmetical condition. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Periodic alarms<br />
|AT_PERIODIC<br />
|4<br />
|Triggered by timeout condition defined in the alarm setting. <br />
<br />
|}<br />
<br />
<br />
== Program Alarm ==<br />
<br />
Program (or rather "Program not running") alarms, when enabled, warn the user when a program is not running.<br />
<br />
Program alarms are enabled by setting the ODB key [[/Programs ODB tree#Alarm class|/Programs/<client-name>/Alarm class]] to a valid Alarm class specified in the [[/Alarms ODB tree]]. The first time the alarm is triggered, an <span style="color: purple; font-style:italic;">/Alarms/Alarms/<client-name></span> subtree will be created automatically. The program alarm will not be visible in the [[Alarms Page]] until the alarm has triggered, and the subtree created.<br />
<br />
<br />
The alarm system periodically calls [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_check()]. This causes every client listed in the {{Odbpath|path=/Programs}} ODB tree to be tested using [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__cmfunctionc.html cm_exist()] to see if it is running. If the client is not running, the time of first failure is recorded in the ODB key [[/Programs ODB tree#First failed|/Programs/<client-name>/First failed]].<br />
<br />
If the client has not been running for longer than the time set in ODB key [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]], a "Program not running" alarm is triggered (if enabled by [[/Programs ODB tree#Alarm class|Alarm class]]) and the program is restarted (if enabled by [[/Programs ODB tree#Auto restart|/Programs/<client-name>/Auto restart]] and a valid [[/Programs ODB tree#Start command|Start command]] is supplied).<br />
<br />
The "not running" condition is tested every 10 seconds (each time al_check() is called), but the frequency of ''Program not running'' alarms can be reduced by increasing the value of the ODB key<br />
[[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]<br />
(default value 60 seconds). This can be useful if [[/Alarms ODB tree#System message interval|System message interval]] in the specified alarm class subtree is set to zero.<br />
<br />
<br />
== Periodic Alarm ==<br />
The periodic alarm is activated periodically according to the time in [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]. An example of a periodic alarm is "Demo Periodic" in the [[/Alarms ODB tree#Example|example]].<br />
<br />
== Evaluated Alarm ==<br />
<br />
Evaluated alarms require an ''alarm condition'' which is entered into the ODB key [[/Alarms ODB tree#Condition|Condition]] in the [[/Alarms ODB tree#<alarm_name> subtree|<alarm_name> subtree]].<br />
The condition may be simply a '''comparison''' between any ODB variable and a threshold parameter, e.g.<br />
<br />
/Runinfo/Run number > 100<br />
or it may be an '''evaluated condition'''. One can write conditions like<br />
<br />
/Equipment/HV/Variables/Input[*] < 100<br />
or<br />
<br />
/Equipment/HV/Variables/Input[2-3] < 100<br />
to check all values from an array or a certain range. If one array element fulfills the alarm condition, the alarm is triggered. In addition, bit-wise alarm conditions are possible, e.g.<br />
<br />
/Equipment/Environment/Variables/Input[0] & 8<br />
The alarm is triggered if bit #3 is set in Input[0].<br />
<br />
The value of an evaluated alarm is computed using al_evaluate_condition() in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/alarm_8c_source.html alarm.c].<br />
<br />
== Internal Alarm ==<br />
These are triggered in a program using a call to <br />
[https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__alfunctioncode.html al_trigger_alarm()]. See also description of al_trigger_alarm() sequence [[#Implementation of the MIDAS Alarm System|above]].<br />
<br />
<br />
There is nothing surprising in these alarms. Each alarm is checked with a time period set by ODB key [[ /Alarms ODB tree#Check interval|Check interval]] in the [[/Alarms ODB tree]].<br />
<br />
= Alarm triggering Email or SMS alerts =<br />
<br />
It is possible to have the MIDAS alarm system send email or SMS alerts to cell phones when alarms are triggered. This can be configured by defining an ODB alarm on a critical ODB parameter, e.g.<br />
<br />
/Alarms/Alarms/Liquid Level<br />
Active y<br />
Triggered 0 (0x0)<br />
Type 3 (0x3)<br />
Check interval 60 (0x3C)<br />
Checked last 1227690148 (0x492D10A4)<br />
Time triggered first (empty)<br />
Time triggered last (empty)<br />
Condition /Equipment/Environment/Variables/Input[0] < 10<br />
Alarm Class Level Alarm<br />
Alarm Message Liquid Level is only %s<br />
<br />
In this example, the alarm triggers an alarm of class "Level Alarm". This alarm class is defined as follows:<br />
<br />
/Alarms/Classes/Level Alarm<br />
Write system message y<br />
Write Elog message n<br />
System message interval 600 (0x258)<br />
System message last 0 (0x0)<br />
Execute command /home/midas/level_alarm '%s'<br />
Execute interval 1800 (0x708)<br />
Execute last 0 (0x0)<br />
Stop run n<br />
Display BGColor red<br />
Display FGColor black<br />
<br />
The key here is to call a script "level_alarm", which can send emails. Use something like:<br />
<br />
#/bin/csh<br />
echo $1 | mail -s \"Level Alarm\" your.name@domain.edu<br />
odbedit -c 'msg 2 level_alarm \"Alarm was sent to your.name@domain.edu\"'<br />
<br />
The second command just generates a MIDAS system message for confirmation. Most cell phones (depends on the provider) have an email address. If you send an email there, it will be translated into a SMS message.<br />
<br />
The script file above can of course be more complicated. A perl script could be used that parses an address list, so other interested parties can register by adding his/her email address to that list. The script may also collects some other slow control variables (like pressure, temperature) and combine them into the SMS message.<br />
<br />
For very sensitive systems, having an alarm via SMS may not be sufficient, since the alarm system could be down (e.g. computer crash, network failure). In this case 'negative alarms' can be used. For example, every 30 minutes the system may send an SMS with the current parameter values. If the expected message is not received, it may indicate that something in the MIDAS system is wrong.<br />
<br />
= Alarm triggering Slack notifications =<br />
<br />
A more modern way for notification is to use messengers apps such as Slack. To send alarms to Slack, do the following:<br />
<br />
* Go to https://api.slack.com/apps<br />
* Create an App "MIDAS alarms", select "From scratch"<br />
* Name it "MIDAS alarm" or similar, select your workspace<br />
* Click on "Add features and functionality"<br />
* Select "Incoming Webhooks"<br />
* Activate Incoming Webhooks<br />
* Click on "Add New Webhook to Workspace"<br />
* Select channel where alarms get posted, allow access<br />
* Copy sample curl request and replace "Hello World" by "$1", such as: <br />
<br />
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$1\"}' https://hooks.slack.com/services/[xxx]/[yyy]/[zzz]<br />
<br />
(leave they keys xxx, yyy, zzz as shown on the web page).<br />
<br />
* Call the curl command from the alarm system by putting the above command under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
<br />
<br />
= Alarm triggering Telegram notifications =<br />
<br />
A telegram bot needs to be created to post Midas alarms into telegram channels.<br />
<br />
* Go to https://web.telegram.org and login by scanning the QR-code with the telegram app on your phone<br />
* Go to https://t.me/botfather<br />
* Click on "open in web"<br />
* A chat window will open. <br />
* Send "/newbot" into the chat and assign name and username to your bot<br />
* At the end of the bot creation a access token will be displayed. Save that access token.<br />
* Post "/mybots" into the chat<br />
* Select the bot that was just created<br />
* Click on "Bot settings"<br />
* Click on "Allow Groups"<br />
* Enable groups for the bot<br />
* Click on "back to settings"<br />
* Click on Channel Admin Rights<br />
* Click on "Manage channel" (a checkmark should appear next to the text)<br />
* Close this chat.<br />
<br />
* Add the created bot to a telegram group chat via searching for the bot name in "add members" (same as adding new People to the group)<br />
* Now you need the chat_id of the same group chat. When you open the group chat in a webbrowser the URL should look like this https://web.telegram.org/a/#-1700000000 (where 1700000000 will be a different number for you)<br />
* The group chat_id is then -1001700000000 (add -100 in front of the number in your URL)<br />
* With the chat_id from above and the access token from the bot creation process you can post to the telegram group like this: (replace <chat_id> and <accessToken> with your numbers)<br />
<br />
curl -s -X POST "https://api.telegram.org/bot<accessToken>/sendMessage" -d chat_id=<chat_id> -d text="🚨 Midas alarm triggered with message = $1"<br />
<br />
* Create a shell script with this command<br />
* Call the shell script from the alarm system by putting "path/to/script.sh '%1$s'" under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
* Should you want to post the same alarm to multiple systems you can just separate scripts in <code>/Alarms/Classes/All/Execute command</code> with a semicolon ("path/to/script.sh '%1$s';path/to/script2.sh '%1$s'")<br />
<br />
= Alarm triggering Mattermost notifications =<br />
<br />
First a mattermost bot needs to be created. To do this you need to be an Admin of the Team that the bot should post to.<br />
<br />
* In the drop down menu on the top left corner of the mattermost page click on "Integrations" (note: this field will be missing if you are not an Admin of the selected mattermost team) <br />
* Select "Incoming Webhooks"<br />
* Click on "Add Incoming Webhook"<br />
* Give the bot a title and username (e.g. midasalarms) and select the channel to post to, click "save"<br />
* Now a URL for the incoming webhook should be displayed (https://mattermost.gitlab.rlp.net/hooks/<token>). Copy that URL into the curl command in the following script:<br />
<br />
<br />
#!/bin/bash<br />
alarm_message=$1<br />
generate_post_data()<br />
{<br />
cat <<EOF<br />
{<br />
"text": "@all Midas alarm triggered with message = $alarm_message", <br />
"icon_emoji":":rotating_light:", <br />
"username":"Midas"}<br />
EOF<br />
}<br />
curl -i -X POST -H 'Content-Type: application/jsoni' --data "$(generate_post_data)" https://mattermost.gitlab.rlp.net/hooks/<token><br />
<br />
- Call the shell script from the alarm system by putting <code>path/to/script.sh '%1$s'</code> under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
<br />
[[Category:Alarms]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Alarm_System&diff=3213Alarm System2023-05-05T14:30:38Z<p>Stefan Ritt: /* Alarm triggering Mattermost notifications */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Alarms ODB tree]]<br />
* [[Alarms Page]]<br />
* [[Mhttpd|mhttpd MIDAS web server]]<br />
</div><br />
<br />
= Introduction =<br />
MIDAS provides an alarm system, which by default is turned off. When the alarm system is [[/Alarms ODB tree#Alarm system active|activated]] and an alarm condition is detected, alarm messages are sent by the system which appear as an alarm banner on the [[Status Page|mhttpd status page]], and as a message on any windows running [[odbedit]] clients. The alarm system is flexible and can be extensively customized for each experiment<br />
using the [[Alarms Page|mhttpd Alarms Page]] or [[odbedit]]. <br />
<br />
The alarm system is built-in and part of the main experiment scheduler. This means no separate task is necessary to benefit from the alarm system. Its setup and activation is done through the '''[[/Alarms ODB tree]]'''. The alarm system includes several other features such as sequencing and control of the experiment. The alarm capabilities are:<br />
* Alarm setting on any ODB variable against a threshold parameter.<br />
* Alarm triggered by evaluated condition<br />
* Selection of Alarm check frequency<br />
* Selection of Alarm trigger frequency<br />
* Customization alarm scheme; under this scheme multiple choices of alarm type can be selected<br />
* Selection of alarm message destination (to system message log or to elog)<br />
* email or SMS alerts can be sent<br />
* Alarm triggered when a Program is not running <br />
<br />
<br />
= Implementation of the MIDAS Alarm System =<br />
<br />
The alarm system source code is [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/alarm_8c_source.html alarm.c].<br />
Alarms are checked inside [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html alarm.c::al_check()]. This function is called by cm_yield() every 10 seconds and by rpc_server_thread(), also every 10 seconds. For remote MIDAS clients, their al_check() issues an RPC_AL_CHECK RPC call into the MIDAS server utility [[mserver]], where rpc_server_dispatch() calls the local al_check(). As result, all alarm checks run inside a process directly attached to the local MIDAS shared memory (inside a local client or inside an mserver process for a remote client). Each and every MIDAS client runs the alarm checks. To prevent race conditions between different MIDAS clients, access to al_check() is serialized using the ALARM semaphore. Inside al_check(), alarms are triggered using <br />
[http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_trigger_alarm()], which in turn calls al_trigger_class(). Inside al_trigger_class(), the alarm is recorded into an elog or into [[Message System|midas.log]] using cm_msg(MTALK).<br />
<br />
Special note should be made of the ODB setting [[/Alarms ODB tree#System message interval|system message interval]], which has a surprising effect - after an alarm is recorded into system messages (using cm_msg(MTALK)), no record is made of any subsequent alarms until the time interval set by this variable elapses. With default value of 60 seconds, after one alarm, no more alarms are recorded for 60 seconds. Also, because all the alarms are checked at the same time, '''only the first''' triggered alarm will be recorded.<br />
<br />
As of alarm.c rev 4683, {{Odbpath|path=/Alarms/System message interval}} is set to 0 ensures that every alarm is recorded into the [[Message System#MIDAS Log file|MIDAS log file]]. (In previous revisions, this setting may still miss some alarms).<br />
<br />
<br />
<br />
<br />
<div id="Alarm class"></div><br />
= Alarms structure =<br />
The [[/Alarms ODB tree]] structure is split into 2 sections:<br />
*"Alarms" which define the condition to be tested. The user can create as many [[/Alarms ODB tree#Alarms subtree|Alarms]] as desired, but each must be one of the four defined [[#Alarm Types|Alarm types]] .<br />
*"Classes" which define the action to be taken when the alarm occurs. Two Classes (Alarm and Warning) are defined by default. The user can add more [[/Alarms ODB tree#Classes subtree|Classes]] as desired.<br />
<br><br><br />
<br />
In order to make the system flexible, each alarm class may perform different actions when an alarm is given. For example, it may<br />
* write a system message (see [[/Alarms ODB tree#Write System Message|Write System Message]])<br />
* write to the elog (see [[/Alarms ODB tree#Write Elog Message|Write Elog Message]])<br />
* stop the run (see [[/Alarms ODB tree#Stop run|Stop run]])<br />
* spawn a detached script listed in the ODB variable [[/Alarms ODB tree#Execute command|Execute command]]. This feature is used when an Alarm triggers Email or SMS alerts (see [[#Alarm triggering Email or SMS alerts|example]]).<br />
<br><br />
<br />
= Alarm Types =<br />
<br />
The four available Alarm Types are shown in Table 1. They are defined in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/midas_8h.html&nbsp midas.h]. The alarm type is entered into the [[/Alarms ODB tree#Type|Type]] key.<br />
<br />
<br />
{| style="text-align: left; width: 100%; background-color: white;" border="3" cellpadding="2" cellspacing="2"<br />
|+ Table 1 : Defined Alarm Types<br />
|-<br />
| colspan="2" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Alarm Type<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | INT value<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Explanation<br />
<br />
|- <br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: bold;" |Internal alarms<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: normal;" | AT_INTERNAL<br />
|1<br />
|Trigger on internal (program) alarm setting through the use of the al_...() functions. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Program alarms<br />
|AT_PROGRAM<br />
|2<br />
|Triggered on condition of the state of the defined task (i.e. program not running)<br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Evaluated alarms<br />
|AT_EVALUATED<br />
|3<br />
|Triggered by ODB value on given arithmetical condition. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Periodic alarms<br />
|AT_PERIODIC<br />
|4<br />
|Triggered by timeout condition defined in the alarm setting. <br />
<br />
|}<br />
<br />
<br />
== Program Alarm ==<br />
<br />
Program (or rather "Program not running") alarms, when enabled, warn the user when a program is not running.<br />
<br />
Program alarms are enabled by setting the ODB key [[/Programs ODB tree#Alarm class|/Programs/<client-name>/Alarm class]] to a valid Alarm class specified in the [[/Alarms ODB tree]]. The first time the alarm is triggered, an <span style="color: purple; font-style:italic;">/Alarms/Alarms/<client-name></span> subtree will be created automatically. The program alarm will not be visible in the [[Alarms Page]] until the alarm has triggered, and the subtree created.<br />
<br />
<br />
The alarm system periodically calls [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_check()]. This causes every client listed in the {{Odbpath|path=/Programs}} ODB tree to be tested using [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__cmfunctionc.html cm_exist()] to see if it is running. If the client is not running, the time of first failure is recorded in the ODB key [[/Programs ODB tree#First failed|/Programs/<client-name>/First failed]].<br />
<br />
If the client has not been running for longer than the time set in ODB key [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]], a "Program not running" alarm is triggered (if enabled by [[/Programs ODB tree#Alarm class|Alarm class]]) and the program is restarted (if enabled by [[/Programs ODB tree#Auto restart|/Programs/<client-name>/Auto restart]] and a valid [[/Programs ODB tree#Start command|Start command]] is supplied).<br />
<br />
The "not running" condition is tested every 10 seconds (each time al_check() is called), but the frequency of ''Program not running'' alarms can be reduced by increasing the value of the ODB key<br />
[[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]<br />
(default value 60 seconds). This can be useful if [[/Alarms ODB tree#System message interval|System message interval]] in the specified alarm class subtree is set to zero.<br />
<br />
<br />
== Periodic Alarm ==<br />
The periodic alarm is activated periodically according to the time in [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]. An example of a periodic alarm is "Demo Periodic" in the [[/Alarms ODB tree#Example|example]].<br />
<br />
== Evaluated Alarm ==<br />
<br />
Evaluated alarms require an ''alarm condition'' which is entered into the ODB key [[/Alarms ODB tree#Condition|Condition]] in the [[/Alarms ODB tree#<alarm_name> subtree|<alarm_name> subtree]].<br />
The condition may be simply a '''comparison''' between any ODB variable and a threshold parameter, e.g.<br />
<br />
/Runinfo/Run number > 100<br />
or it may be an '''evaluated condition'''. One can write conditions like<br />
<br />
/Equipment/HV/Variables/Input[*] < 100<br />
or<br />
<br />
/Equipment/HV/Variables/Input[2-3] < 100<br />
to check all values from an array or a certain range. If one array element fulfills the alarm condition, the alarm is triggered. In addition, bit-wise alarm conditions are possible, e.g.<br />
<br />
/Equipment/Environment/Variables/Input[0] & 8<br />
The alarm is triggered if bit #3 is set in Input[0].<br />
<br />
The value of an evaluated alarm is computed using al_evaluate_condition() in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/alarm_8c_source.html alarm.c].<br />
<br />
== Internal Alarm ==<br />
These are triggered in a program using a call to <br />
[https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__alfunctioncode.html al_trigger_alarm()]. See also description of al_trigger_alarm() sequence [[#Implementation of the MIDAS Alarm System|above]].<br />
<br />
<br />
There is nothing surprising in these alarms. Each alarm is checked with a time period set by ODB key [[ /Alarms ODB tree#Check interval|Check interval]] in the [[/Alarms ODB tree]].<br />
<br />
= Alarm triggering Email or SMS alerts =<br />
<br />
It is possible to have the MIDAS alarm system send email or SMS alerts to cell phones when alarms are triggered. This can be configured by defining an ODB alarm on a critical ODB parameter, e.g.<br />
<br />
/Alarms/Alarms/Liquid Level<br />
Active y<br />
Triggered 0 (0x0)<br />
Type 3 (0x3)<br />
Check interval 60 (0x3C)<br />
Checked last 1227690148 (0x492D10A4)<br />
Time triggered first (empty)<br />
Time triggered last (empty)<br />
Condition /Equipment/Environment/Variables/Input[0] < 10<br />
Alarm Class Level Alarm<br />
Alarm Message Liquid Level is only %s<br />
<br />
In this example, the alarm triggers an alarm of class "Level Alarm". This alarm class is defined as follows:<br />
<br />
/Alarms/Classes/Level Alarm<br />
Write system message y<br />
Write Elog message n<br />
System message interval 600 (0x258)<br />
System message last 0 (0x0)<br />
Execute command /home/midas/level_alarm '%s'<br />
Execute interval 1800 (0x708)<br />
Execute last 0 (0x0)<br />
Stop run n<br />
Display BGColor red<br />
Display FGColor black<br />
<br />
The key here is to call a script "level_alarm", which can send emails. Use something like:<br />
<br />
#/bin/csh<br />
echo $1 | mail -s \"Level Alarm\" your.name@domain.edu<br />
odbedit -c 'msg 2 level_alarm \"Alarm was sent to your.name@domain.edu\"'<br />
<br />
The second command just generates a MIDAS system message for confirmation. Most cell phones (depends on the provider) have an email address. If you send an email there, it will be translated into a SMS message.<br />
<br />
The script file above can of course be more complicated. A perl script could be used that parses an address list, so other interested parties can register by adding his/her email address to that list. The script may also collects some other slow control variables (like pressure, temperature) and combine them into the SMS message.<br />
<br />
For very sensitive systems, having an alarm via SMS may not be sufficient, since the alarm system could be down (e.g. computer crash, network failure). In this case 'negative alarms' can be used. For example, every 30 minutes the system may send an SMS with the current parameter values. If the expected message is not received, it may indicate that something in the MIDAS system is wrong.<br />
<br />
= Alarm triggering Slack notifications =<br />
<br />
A more modern way for notification is to use messengers apps such as Slack. To send alarms to Slack, do the following:<br />
<br />
* Go to https://api.slack.com/apps<br />
* Create an App "MIDAS alarms", select "From scratch"<br />
* Name it "MIDAS alarm" or similar, select your workspace<br />
* Click on "Add features and functionality"<br />
* Select "Incoming Webhooks"<br />
* Activate Incoming Webhooks<br />
* Click on "Add New Webhook to Workspace"<br />
* Select channel where alarms get posted, allow access<br />
* Copy sample curl request and replace "Hello World" by "$1", such as: <br />
<br />
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$1\"}' https://hooks.slack.com/services/[xxx]/[yyy]/[zzz]<br />
<br />
(leave they keys xxx, yyy, zzz as shown on the web page).<br />
<br />
* Call the curl command from the alarm system by putting the above command under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
<br />
<br />
= Alarm triggering Telegram notifications =<br />
<br />
A telegram bot needs to be created to post Midas alarms into telegram channels.<br />
<br />
* Go to https://web.telegram.org and login by scanning the QR-code with the telegram app on your phone<br />
* Go to https://t.me/botfather<br />
* Click on "open in web"<br />
* A chat window will open. <br />
* Send "/newbot" into the chat and assign name and username to your bot<br />
* At the end of the bot creation a access token will be displayed. Save that access token.<br />
* Post "/mybots" into the chat<br />
* Select the bot that was just created<br />
* Click on "Bot settings"<br />
* Click on "Allow Groups"<br />
* Enable groups for the bot<br />
* Click on "back to settings"<br />
* Click on Channel Admin Rights<br />
* Click on "Manage channel" (a checkmark should appear next to the text)<br />
* Close this chat.<br />
<br />
* Add the created bot to a telegram group chat via searching for the bot name in "add members" (same as adding new People to the group)<br />
* Now you need the chat_id of the same group chat. When you open the group chat in a webbrowser the URL should look like this https://web.telegram.org/a/#-1700000000 (where 1700000000 will be a different number for you)<br />
* The group chat_id is then -1001700000000 (add -100 in front of the number in your URL)<br />
* With the chat_id from above and the access token from the bot creation process you can post to the telegram group like this: (replace <chat_id> and <accessToken> with your numbers)<br />
<br />
curl -s -X POST "https://api.telegram.org/bot<accessToken>/sendMessage" -d chat_id=<chat_id> -d text="🚨 Midas alarm triggered with message = $1"<br />
<br />
* Create a shell script with this command<br />
* Call the shell script from the alarm system by putting "path/to/script.sh '%1$s'" under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
* Should you want to post the same alarm to multiple systems you can just separate scripts in <code>/Alarms/Classes/All/Execute command</code> with a semicolon ("path/to/script.sh '%1$s';path/to/script2.sh '%1$s'")<br />
<br />
= Alarm triggering Mattermost notifications =<br />
<br />
First a mattermost bot needs to be created. To do this you need to be an Admin of the Team that the bot should post to.<br />
<br />
* In the drop down menu on the top left corner of the mattermost page click on "Integrations" (note: this field will be missing if you are not an Admin of the selected mattermost team) <br />
* Select "Incoming Webhooks"<br />
* Click on "Add Incoming Webhook"<br />
* Give the bot a title and username (e.g. midasalarms) and select the channel to post to, click "save"<br />
* Now a URL for the incoming webhook should be displayed (https://mattermost.gitlab.rlp.net/hooks/<token>). Copy that URL into the curl command in the following script:<br />
<br />
<br />
#!/bin/bash<br />
alarm_message=$1<br />
generate_post_data()<br />
{<br />
cat <<EOF<br />
{<br />
"text": "@all Midas alarm triggered with message = $alarm_message", <br />
"icon_emoji":":rotating_light:", <br />
"username":"Midas"}<br />
EOF<br />
}<br />
curl -i -X POST -H 'Content-Type: application/jsoni' --data "$(generate_post_data)" https://mattermost.gitlab.rlp.net/hooks/<token><br />
<br />
- Call the shell script from the alarm system by putting "path/to/script.sh '%1$s'" under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
<br />
[[Category:Alarms]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Alarm_System&diff=3212Alarm System2023-05-05T14:27:40Z<p>Stefan Ritt: /* Alarm triggering Telegram notifications */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Alarms ODB tree]]<br />
* [[Alarms Page]]<br />
* [[Mhttpd|mhttpd MIDAS web server]]<br />
</div><br />
<br />
= Introduction =<br />
MIDAS provides an alarm system, which by default is turned off. When the alarm system is [[/Alarms ODB tree#Alarm system active|activated]] and an alarm condition is detected, alarm messages are sent by the system which appear as an alarm banner on the [[Status Page|mhttpd status page]], and as a message on any windows running [[odbedit]] clients. The alarm system is flexible and can be extensively customized for each experiment<br />
using the [[Alarms Page|mhttpd Alarms Page]] or [[odbedit]]. <br />
<br />
The alarm system is built-in and part of the main experiment scheduler. This means no separate task is necessary to benefit from the alarm system. Its setup and activation is done through the '''[[/Alarms ODB tree]]'''. The alarm system includes several other features such as sequencing and control of the experiment. The alarm capabilities are:<br />
* Alarm setting on any ODB variable against a threshold parameter.<br />
* Alarm triggered by evaluated condition<br />
* Selection of Alarm check frequency<br />
* Selection of Alarm trigger frequency<br />
* Customization alarm scheme; under this scheme multiple choices of alarm type can be selected<br />
* Selection of alarm message destination (to system message log or to elog)<br />
* email or SMS alerts can be sent<br />
* Alarm triggered when a Program is not running <br />
<br />
<br />
= Implementation of the MIDAS Alarm System =<br />
<br />
The alarm system source code is [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/alarm_8c_source.html alarm.c].<br />
Alarms are checked inside [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html alarm.c::al_check()]. This function is called by cm_yield() every 10 seconds and by rpc_server_thread(), also every 10 seconds. For remote MIDAS clients, their al_check() issues an RPC_AL_CHECK RPC call into the MIDAS server utility [[mserver]], where rpc_server_dispatch() calls the local al_check(). As result, all alarm checks run inside a process directly attached to the local MIDAS shared memory (inside a local client or inside an mserver process for a remote client). Each and every MIDAS client runs the alarm checks. To prevent race conditions between different MIDAS clients, access to al_check() is serialized using the ALARM semaphore. Inside al_check(), alarms are triggered using <br />
[http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_trigger_alarm()], which in turn calls al_trigger_class(). Inside al_trigger_class(), the alarm is recorded into an elog or into [[Message System|midas.log]] using cm_msg(MTALK).<br />
<br />
Special note should be made of the ODB setting [[/Alarms ODB tree#System message interval|system message interval]], which has a surprising effect - after an alarm is recorded into system messages (using cm_msg(MTALK)), no record is made of any subsequent alarms until the time interval set by this variable elapses. With default value of 60 seconds, after one alarm, no more alarms are recorded for 60 seconds. Also, because all the alarms are checked at the same time, '''only the first''' triggered alarm will be recorded.<br />
<br />
As of alarm.c rev 4683, {{Odbpath|path=/Alarms/System message interval}} is set to 0 ensures that every alarm is recorded into the [[Message System#MIDAS Log file|MIDAS log file]]. (In previous revisions, this setting may still miss some alarms).<br />
<br />
<br />
<br />
<br />
<div id="Alarm class"></div><br />
= Alarms structure =<br />
The [[/Alarms ODB tree]] structure is split into 2 sections:<br />
*"Alarms" which define the condition to be tested. The user can create as many [[/Alarms ODB tree#Alarms subtree|Alarms]] as desired, but each must be one of the four defined [[#Alarm Types|Alarm types]] .<br />
*"Classes" which define the action to be taken when the alarm occurs. Two Classes (Alarm and Warning) are defined by default. The user can add more [[/Alarms ODB tree#Classes subtree|Classes]] as desired.<br />
<br><br><br />
<br />
In order to make the system flexible, each alarm class may perform different actions when an alarm is given. For example, it may<br />
* write a system message (see [[/Alarms ODB tree#Write System Message|Write System Message]])<br />
* write to the elog (see [[/Alarms ODB tree#Write Elog Message|Write Elog Message]])<br />
* stop the run (see [[/Alarms ODB tree#Stop run|Stop run]])<br />
* spawn a detached script listed in the ODB variable [[/Alarms ODB tree#Execute command|Execute command]]. This feature is used when an Alarm triggers Email or SMS alerts (see [[#Alarm triggering Email or SMS alerts|example]]).<br />
<br><br />
<br />
= Alarm Types =<br />
<br />
The four available Alarm Types are shown in Table 1. They are defined in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/midas_8h.html&nbsp midas.h]. The alarm type is entered into the [[/Alarms ODB tree#Type|Type]] key.<br />
<br />
<br />
{| style="text-align: left; width: 100%; background-color: white;" border="3" cellpadding="2" cellspacing="2"<br />
|+ Table 1 : Defined Alarm Types<br />
|-<br />
| colspan="2" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Alarm Type<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | INT value<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Explanation<br />
<br />
|- <br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: bold;" |Internal alarms<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: normal;" | AT_INTERNAL<br />
|1<br />
|Trigger on internal (program) alarm setting through the use of the al_...() functions. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Program alarms<br />
|AT_PROGRAM<br />
|2<br />
|Triggered on condition of the state of the defined task (i.e. program not running)<br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Evaluated alarms<br />
|AT_EVALUATED<br />
|3<br />
|Triggered by ODB value on given arithmetical condition. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Periodic alarms<br />
|AT_PERIODIC<br />
|4<br />
|Triggered by timeout condition defined in the alarm setting. <br />
<br />
|}<br />
<br />
<br />
== Program Alarm ==<br />
<br />
Program (or rather "Program not running") alarms, when enabled, warn the user when a program is not running.<br />
<br />
Program alarms are enabled by setting the ODB key [[/Programs ODB tree#Alarm class|/Programs/<client-name>/Alarm class]] to a valid Alarm class specified in the [[/Alarms ODB tree]]. The first time the alarm is triggered, an <span style="color: purple; font-style:italic;">/Alarms/Alarms/<client-name></span> subtree will be created automatically. The program alarm will not be visible in the [[Alarms Page]] until the alarm has triggered, and the subtree created.<br />
<br />
<br />
The alarm system periodically calls [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_check()]. This causes every client listed in the {{Odbpath|path=/Programs}} ODB tree to be tested using [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__cmfunctionc.html cm_exist()] to see if it is running. If the client is not running, the time of first failure is recorded in the ODB key [[/Programs ODB tree#First failed|/Programs/<client-name>/First failed]].<br />
<br />
If the client has not been running for longer than the time set in ODB key [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]], a "Program not running" alarm is triggered (if enabled by [[/Programs ODB tree#Alarm class|Alarm class]]) and the program is restarted (if enabled by [[/Programs ODB tree#Auto restart|/Programs/<client-name>/Auto restart]] and a valid [[/Programs ODB tree#Start command|Start command]] is supplied).<br />
<br />
The "not running" condition is tested every 10 seconds (each time al_check() is called), but the frequency of ''Program not running'' alarms can be reduced by increasing the value of the ODB key<br />
[[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]<br />
(default value 60 seconds). This can be useful if [[/Alarms ODB tree#System message interval|System message interval]] in the specified alarm class subtree is set to zero.<br />
<br />
<br />
== Periodic Alarm ==<br />
The periodic alarm is activated periodically according to the time in [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]. An example of a periodic alarm is "Demo Periodic" in the [[/Alarms ODB tree#Example|example]].<br />
<br />
== Evaluated Alarm ==<br />
<br />
Evaluated alarms require an ''alarm condition'' which is entered into the ODB key [[/Alarms ODB tree#Condition|Condition]] in the [[/Alarms ODB tree#<alarm_name> subtree|<alarm_name> subtree]].<br />
The condition may be simply a '''comparison''' between any ODB variable and a threshold parameter, e.g.<br />
<br />
/Runinfo/Run number > 100<br />
or it may be an '''evaluated condition'''. One can write conditions like<br />
<br />
/Equipment/HV/Variables/Input[*] < 100<br />
or<br />
<br />
/Equipment/HV/Variables/Input[2-3] < 100<br />
to check all values from an array or a certain range. If one array element fulfills the alarm condition, the alarm is triggered. In addition, bit-wise alarm conditions are possible, e.g.<br />
<br />
/Equipment/Environment/Variables/Input[0] & 8<br />
The alarm is triggered if bit #3 is set in Input[0].<br />
<br />
The value of an evaluated alarm is computed using al_evaluate_condition() in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/alarm_8c_source.html alarm.c].<br />
<br />
== Internal Alarm ==<br />
These are triggered in a program using a call to <br />
[https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__alfunctioncode.html al_trigger_alarm()]. See also description of al_trigger_alarm() sequence [[#Implementation of the MIDAS Alarm System|above]].<br />
<br />
<br />
There is nothing surprising in these alarms. Each alarm is checked with a time period set by ODB key [[ /Alarms ODB tree#Check interval|Check interval]] in the [[/Alarms ODB tree]].<br />
<br />
= Alarm triggering Email or SMS alerts =<br />
<br />
It is possible to have the MIDAS alarm system send email or SMS alerts to cell phones when alarms are triggered. This can be configured by defining an ODB alarm on a critical ODB parameter, e.g.<br />
<br />
/Alarms/Alarms/Liquid Level<br />
Active y<br />
Triggered 0 (0x0)<br />
Type 3 (0x3)<br />
Check interval 60 (0x3C)<br />
Checked last 1227690148 (0x492D10A4)<br />
Time triggered first (empty)<br />
Time triggered last (empty)<br />
Condition /Equipment/Environment/Variables/Input[0] < 10<br />
Alarm Class Level Alarm<br />
Alarm Message Liquid Level is only %s<br />
<br />
In this example, the alarm triggers an alarm of class "Level Alarm". This alarm class is defined as follows:<br />
<br />
/Alarms/Classes/Level Alarm<br />
Write system message y<br />
Write Elog message n<br />
System message interval 600 (0x258)<br />
System message last 0 (0x0)<br />
Execute command /home/midas/level_alarm '%s'<br />
Execute interval 1800 (0x708)<br />
Execute last 0 (0x0)<br />
Stop run n<br />
Display BGColor red<br />
Display FGColor black<br />
<br />
The key here is to call a script "level_alarm", which can send emails. Use something like:<br />
<br />
#/bin/csh<br />
echo $1 | mail -s \"Level Alarm\" your.name@domain.edu<br />
odbedit -c 'msg 2 level_alarm \"Alarm was sent to your.name@domain.edu\"'<br />
<br />
The second command just generates a MIDAS system message for confirmation. Most cell phones (depends on the provider) have an email address. If you send an email there, it will be translated into a SMS message.<br />
<br />
The script file above can of course be more complicated. A perl script could be used that parses an address list, so other interested parties can register by adding his/her email address to that list. The script may also collects some other slow control variables (like pressure, temperature) and combine them into the SMS message.<br />
<br />
For very sensitive systems, having an alarm via SMS may not be sufficient, since the alarm system could be down (e.g. computer crash, network failure). In this case 'negative alarms' can be used. For example, every 30 minutes the system may send an SMS with the current parameter values. If the expected message is not received, it may indicate that something in the MIDAS system is wrong.<br />
<br />
= Alarm triggering Slack notifications =<br />
<br />
A more modern way for notification is to use messengers apps such as Slack. To send alarms to Slack, do the following:<br />
<br />
* Go to https://api.slack.com/apps<br />
* Create an App "MIDAS alarms", select "From scratch"<br />
* Name it "MIDAS alarm" or similar, select your workspace<br />
* Click on "Add features and functionality"<br />
* Select "Incoming Webhooks"<br />
* Activate Incoming Webhooks<br />
* Click on "Add New Webhook to Workspace"<br />
* Select channel where alarms get posted, allow access<br />
* Copy sample curl request and replace "Hello World" by "$1", such as: <br />
<br />
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$1\"}' https://hooks.slack.com/services/[xxx]/[yyy]/[zzz]<br />
<br />
(leave they keys xxx, yyy, zzz as shown on the web page).<br />
<br />
* Call the curl command from the alarm system by putting the above command under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
<br />
<br />
= Alarm triggering Telegram notifications =<br />
<br />
A telegram bot needs to be created to post Midas alarms into telegram channels.<br />
<br />
* Go to https://web.telegram.org and login by scanning the QR-code with the telegram app on your phone<br />
* Go to https://t.me/botfather<br />
* Click on "open in web"<br />
* A chat window will open. <br />
* Send "/newbot" into the chat and assign name and username to your bot<br />
* At the end of the bot creation a access token will be displayed. Save that access token.<br />
* Post "/mybots" into the chat<br />
* Select the bot that was just created<br />
* Click on "Bot settings"<br />
* Click on "Allow Groups"<br />
* Enable groups for the bot<br />
* Click on "back to settings"<br />
* Click on Channel Admin Rights<br />
* Click on "Manage channel" (a checkmark should appear next to the text)<br />
* Close this chat.<br />
<br />
* Add the created bot to a telegram group chat via searching for the bot name in "add members" (same as adding new People to the group)<br />
* Now you need the chat_id of the same group chat. When you open the group chat in a webbrowser the URL should look like this https://web.telegram.org/a/#-1700000000 (where 1700000000 will be a different number for you)<br />
* The group chat_id is then -1001700000000 (add -100 in front of the number in your URL)<br />
* With the chat_id from above and the access token from the bot creation process you can post to the telegram group like this: (replace <chat_id> and <accessToken> with your numbers)<br />
<br />
curl -s -X POST "https://api.telegram.org/bot<accessToken>/sendMessage" -d chat_id=<chat_id> -d text="🚨 Midas alarm triggered with message = $1"<br />
<br />
* Create a shell script with this command<br />
* Call the shell script from the alarm system by putting "path/to/script.sh '%1$s'" under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
* Should you want to post the same alarm to multiple systems you can just separate scripts in <code>/Alarms/Classes/All/Execute command</code> with a semicolon ("path/to/script.sh '%1$s';path/to/script2.sh '%1$s'")<br />
<br />
= Alarm triggering Mattermost notifications =<br />
<br />
First a mattermost bot needs to be created. To do this you need to be an Admin of the Team that the bot should post to.<br />
<br />
* In the drop down menu on the top left corner of the mattermost page click on "Integrations" (note: this field will be missing if you are not an Admin of the selected mattermost team) <br />
* Select "Incoming Webhooks"<br />
* Click on "Add Incoming Webhook"<br />
* Give the bot a title and username (e.g. midasalarms) and select the channel to post to, click "save"<br />
* Now a URL for the incoming webhook should be displayed (https://mattermost.gitlab.rlp.net/hooks/<token>). Copy that URL into the curl command in the following script:<br />
<br />
<code><br />
#!/bin/bash<br />
alarm_message=$1<br />
generate_post_data()<br />
{<br />
cat <<EOF<br />
{<br />
"text": "@all Midas alarm triggered with message = $alarm_message", <br />
"icon_emoji":":rotating_light:", <br />
"username":"Midas"}<br />
EOF<br />
}<br />
curl -i -X POST -H 'Content-Type: application/jsoni' --data "$(generate_post_data)" https://mattermost.gitlab.rlp.net/hooks/<token><br />
</code><br />
<br />
- Call the shell script from the alarm system by putting "path/to/script.sh '%1$s'" under <code>/Alarms/Classes/All/Execute command<code> into the ODB.<br />
<br />
[[Category:Alarms]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Alarm_System&diff=3211Alarm System2023-05-05T14:25:49Z<p>Stefan Ritt: /* Alarm triggering Telegram notifications */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Alarms ODB tree]]<br />
* [[Alarms Page]]<br />
* [[Mhttpd|mhttpd MIDAS web server]]<br />
</div><br />
<br />
= Introduction =<br />
MIDAS provides an alarm system, which by default is turned off. When the alarm system is [[/Alarms ODB tree#Alarm system active|activated]] and an alarm condition is detected, alarm messages are sent by the system which appear as an alarm banner on the [[Status Page|mhttpd status page]], and as a message on any windows running [[odbedit]] clients. The alarm system is flexible and can be extensively customized for each experiment<br />
using the [[Alarms Page|mhttpd Alarms Page]] or [[odbedit]]. <br />
<br />
The alarm system is built-in and part of the main experiment scheduler. This means no separate task is necessary to benefit from the alarm system. Its setup and activation is done through the '''[[/Alarms ODB tree]]'''. The alarm system includes several other features such as sequencing and control of the experiment. The alarm capabilities are:<br />
* Alarm setting on any ODB variable against a threshold parameter.<br />
* Alarm triggered by evaluated condition<br />
* Selection of Alarm check frequency<br />
* Selection of Alarm trigger frequency<br />
* Customization alarm scheme; under this scheme multiple choices of alarm type can be selected<br />
* Selection of alarm message destination (to system message log or to elog)<br />
* email or SMS alerts can be sent<br />
* Alarm triggered when a Program is not running <br />
<br />
<br />
= Implementation of the MIDAS Alarm System =<br />
<br />
The alarm system source code is [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/alarm_8c_source.html alarm.c].<br />
Alarms are checked inside [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html alarm.c::al_check()]. This function is called by cm_yield() every 10 seconds and by rpc_server_thread(), also every 10 seconds. For remote MIDAS clients, their al_check() issues an RPC_AL_CHECK RPC call into the MIDAS server utility [[mserver]], where rpc_server_dispatch() calls the local al_check(). As result, all alarm checks run inside a process directly attached to the local MIDAS shared memory (inside a local client or inside an mserver process for a remote client). Each and every MIDAS client runs the alarm checks. To prevent race conditions between different MIDAS clients, access to al_check() is serialized using the ALARM semaphore. Inside al_check(), alarms are triggered using <br />
[http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_trigger_alarm()], which in turn calls al_trigger_class(). Inside al_trigger_class(), the alarm is recorded into an elog or into [[Message System|midas.log]] using cm_msg(MTALK).<br />
<br />
Special note should be made of the ODB setting [[/Alarms ODB tree#System message interval|system message interval]], which has a surprising effect - after an alarm is recorded into system messages (using cm_msg(MTALK)), no record is made of any subsequent alarms until the time interval set by this variable elapses. With default value of 60 seconds, after one alarm, no more alarms are recorded for 60 seconds. Also, because all the alarms are checked at the same time, '''only the first''' triggered alarm will be recorded.<br />
<br />
As of alarm.c rev 4683, {{Odbpath|path=/Alarms/System message interval}} is set to 0 ensures that every alarm is recorded into the [[Message System#MIDAS Log file|MIDAS log file]]. (In previous revisions, this setting may still miss some alarms).<br />
<br />
<br />
<br />
<br />
<div id="Alarm class"></div><br />
= Alarms structure =<br />
The [[/Alarms ODB tree]] structure is split into 2 sections:<br />
*"Alarms" which define the condition to be tested. The user can create as many [[/Alarms ODB tree#Alarms subtree|Alarms]] as desired, but each must be one of the four defined [[#Alarm Types|Alarm types]] .<br />
*"Classes" which define the action to be taken when the alarm occurs. Two Classes (Alarm and Warning) are defined by default. The user can add more [[/Alarms ODB tree#Classes subtree|Classes]] as desired.<br />
<br><br><br />
<br />
In order to make the system flexible, each alarm class may perform different actions when an alarm is given. For example, it may<br />
* write a system message (see [[/Alarms ODB tree#Write System Message|Write System Message]])<br />
* write to the elog (see [[/Alarms ODB tree#Write Elog Message|Write Elog Message]])<br />
* stop the run (see [[/Alarms ODB tree#Stop run|Stop run]])<br />
* spawn a detached script listed in the ODB variable [[/Alarms ODB tree#Execute command|Execute command]]. This feature is used when an Alarm triggers Email or SMS alerts (see [[#Alarm triggering Email or SMS alerts|example]]).<br />
<br><br />
<br />
= Alarm Types =<br />
<br />
The four available Alarm Types are shown in Table 1. They are defined in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/midas_8h.html&nbsp midas.h]. The alarm type is entered into the [[/Alarms ODB tree#Type|Type]] key.<br />
<br />
<br />
{| style="text-align: left; width: 100%; background-color: white;" border="3" cellpadding="2" cellspacing="2"<br />
|+ Table 1 : Defined Alarm Types<br />
|-<br />
| colspan="2" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Alarm Type<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | INT value<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Explanation<br />
<br />
|- <br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: bold;" |Internal alarms<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: normal;" | AT_INTERNAL<br />
|1<br />
|Trigger on internal (program) alarm setting through the use of the al_...() functions. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Program alarms<br />
|AT_PROGRAM<br />
|2<br />
|Triggered on condition of the state of the defined task (i.e. program not running)<br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Evaluated alarms<br />
|AT_EVALUATED<br />
|3<br />
|Triggered by ODB value on given arithmetical condition. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Periodic alarms<br />
|AT_PERIODIC<br />
|4<br />
|Triggered by timeout condition defined in the alarm setting. <br />
<br />
|}<br />
<br />
<br />
== Program Alarm ==<br />
<br />
Program (or rather "Program not running") alarms, when enabled, warn the user when a program is not running.<br />
<br />
Program alarms are enabled by setting the ODB key [[/Programs ODB tree#Alarm class|/Programs/<client-name>/Alarm class]] to a valid Alarm class specified in the [[/Alarms ODB tree]]. The first time the alarm is triggered, an <span style="color: purple; font-style:italic;">/Alarms/Alarms/<client-name></span> subtree will be created automatically. The program alarm will not be visible in the [[Alarms Page]] until the alarm has triggered, and the subtree created.<br />
<br />
<br />
The alarm system periodically calls [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_check()]. This causes every client listed in the {{Odbpath|path=/Programs}} ODB tree to be tested using [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__cmfunctionc.html cm_exist()] to see if it is running. If the client is not running, the time of first failure is recorded in the ODB key [[/Programs ODB tree#First failed|/Programs/<client-name>/First failed]].<br />
<br />
If the client has not been running for longer than the time set in ODB key [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]], a "Program not running" alarm is triggered (if enabled by [[/Programs ODB tree#Alarm class|Alarm class]]) and the program is restarted (if enabled by [[/Programs ODB tree#Auto restart|/Programs/<client-name>/Auto restart]] and a valid [[/Programs ODB tree#Start command|Start command]] is supplied).<br />
<br />
The "not running" condition is tested every 10 seconds (each time al_check() is called), but the frequency of ''Program not running'' alarms can be reduced by increasing the value of the ODB key<br />
[[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]<br />
(default value 60 seconds). This can be useful if [[/Alarms ODB tree#System message interval|System message interval]] in the specified alarm class subtree is set to zero.<br />
<br />
<br />
== Periodic Alarm ==<br />
The periodic alarm is activated periodically according to the time in [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]. An example of a periodic alarm is "Demo Periodic" in the [[/Alarms ODB tree#Example|example]].<br />
<br />
== Evaluated Alarm ==<br />
<br />
Evaluated alarms require an ''alarm condition'' which is entered into the ODB key [[/Alarms ODB tree#Condition|Condition]] in the [[/Alarms ODB tree#<alarm_name> subtree|<alarm_name> subtree]].<br />
The condition may be simply a '''comparison''' between any ODB variable and a threshold parameter, e.g.<br />
<br />
/Runinfo/Run number > 100<br />
or it may be an '''evaluated condition'''. One can write conditions like<br />
<br />
/Equipment/HV/Variables/Input[*] < 100<br />
or<br />
<br />
/Equipment/HV/Variables/Input[2-3] < 100<br />
to check all values from an array or a certain range. If one array element fulfills the alarm condition, the alarm is triggered. In addition, bit-wise alarm conditions are possible, e.g.<br />
<br />
/Equipment/Environment/Variables/Input[0] & 8<br />
The alarm is triggered if bit #3 is set in Input[0].<br />
<br />
The value of an evaluated alarm is computed using al_evaluate_condition() in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/alarm_8c_source.html alarm.c].<br />
<br />
== Internal Alarm ==<br />
These are triggered in a program using a call to <br />
[https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__alfunctioncode.html al_trigger_alarm()]. See also description of al_trigger_alarm() sequence [[#Implementation of the MIDAS Alarm System|above]].<br />
<br />
<br />
There is nothing surprising in these alarms. Each alarm is checked with a time period set by ODB key [[ /Alarms ODB tree#Check interval|Check interval]] in the [[/Alarms ODB tree]].<br />
<br />
= Alarm triggering Email or SMS alerts =<br />
<br />
It is possible to have the MIDAS alarm system send email or SMS alerts to cell phones when alarms are triggered. This can be configured by defining an ODB alarm on a critical ODB parameter, e.g.<br />
<br />
/Alarms/Alarms/Liquid Level<br />
Active y<br />
Triggered 0 (0x0)<br />
Type 3 (0x3)<br />
Check interval 60 (0x3C)<br />
Checked last 1227690148 (0x492D10A4)<br />
Time triggered first (empty)<br />
Time triggered last (empty)<br />
Condition /Equipment/Environment/Variables/Input[0] < 10<br />
Alarm Class Level Alarm<br />
Alarm Message Liquid Level is only %s<br />
<br />
In this example, the alarm triggers an alarm of class "Level Alarm". This alarm class is defined as follows:<br />
<br />
/Alarms/Classes/Level Alarm<br />
Write system message y<br />
Write Elog message n<br />
System message interval 600 (0x258)<br />
System message last 0 (0x0)<br />
Execute command /home/midas/level_alarm '%s'<br />
Execute interval 1800 (0x708)<br />
Execute last 0 (0x0)<br />
Stop run n<br />
Display BGColor red<br />
Display FGColor black<br />
<br />
The key here is to call a script "level_alarm", which can send emails. Use something like:<br />
<br />
#/bin/csh<br />
echo $1 | mail -s \"Level Alarm\" your.name@domain.edu<br />
odbedit -c 'msg 2 level_alarm \"Alarm was sent to your.name@domain.edu\"'<br />
<br />
The second command just generates a MIDAS system message for confirmation. Most cell phones (depends on the provider) have an email address. If you send an email there, it will be translated into a SMS message.<br />
<br />
The script file above can of course be more complicated. A perl script could be used that parses an address list, so other interested parties can register by adding his/her email address to that list. The script may also collects some other slow control variables (like pressure, temperature) and combine them into the SMS message.<br />
<br />
For very sensitive systems, having an alarm via SMS may not be sufficient, since the alarm system could be down (e.g. computer crash, network failure). In this case 'negative alarms' can be used. For example, every 30 minutes the system may send an SMS with the current parameter values. If the expected message is not received, it may indicate that something in the MIDAS system is wrong.<br />
<br />
= Alarm triggering Slack notifications =<br />
<br />
A more modern way for notification is to use messengers apps such as Slack. To send alarms to Slack, do the following:<br />
<br />
* Go to https://api.slack.com/apps<br />
* Create an App "MIDAS alarms", select "From scratch"<br />
* Name it "MIDAS alarm" or similar, select your workspace<br />
* Click on "Add features and functionality"<br />
* Select "Incoming Webhooks"<br />
* Activate Incoming Webhooks<br />
* Click on "Add New Webhook to Workspace"<br />
* Select channel where alarms get posted, allow access<br />
* Copy sample curl request and replace "Hello World" by "$1", such as: <br />
<br />
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$1\"}' https://hooks.slack.com/services/[xxx]/[yyy]/[zzz]<br />
<br />
(leave they keys xxx, yyy, zzz as shown on the web page).<br />
<br />
* Call the curl command from the alarm system by putting the above command under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
<br />
<br />
= Alarm triggering Telegram notifications =<br />
<br />
A telegram bot needs to be created to post Midas alarms into telegram channels.<br />
<br />
* Go to https://web.telegram.org and login by scanning the QR-code with the telegram app on your phone<br />
* Go to https://t.me/botfather<br />
* Click on "open in web"<br />
* A chat window will open. <br />
* Send "/newbot" into the chat and assign name and username to your bot<br />
* At the end of the bot creation a access token will be displayed. Save that access token.<br />
* Post "/mybots" into the chat<br />
* Select the bot that was just created<br />
* Click on "Bot settings"<br />
* Click on "Allow Groups"<br />
* Enable groups for the bot<br />
* Click on "back to settings"<br />
* Click on Channel Admin Rights<br />
* Click on "Manage channel" (a checkmark should appear next to the text)<br />
* Close this chat.<br />
<br />
* Add the created bot to a telegram group chat via searching for the bot name in "add members" (same as adding new People to the group)<br />
* Now you need the chat_id of the same group chat. When you open the group chat in a webbrowser the URL should look like this https://web.telegram.org/a/#-1700000000 (where 1700000000 will be a different number for you)<br />
* The group chat_id is then -1001700000000 (add -100 in front of the number in your URL)<br />
* With the chat_id from above and the access token from the bot creation process you can post to the telegram group like this: (replace <chat_id> and <accessToken> with your numbers)<br />
<br />
curl -s -X POST "https://api.telegram.org/bot<accessToken>/sendMessage" -d chat_id=<chat_id> -d text="🚨 Midas alarm triggered with message = $1"<br />
<br />
* Create a shell script with this command<br />
* Call the shell script from the alarm system by putting "path/to/script.sh '%1$s'" under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
* Should you want to post the same alarm to multiple systems you can just separate scripts in <code>/Alarms/Classes/All/Execute command</code> with a semicolon ("path/to/script.sh '%1$s';path/to/script2.sh '%1$s'")<br />
<br />
<br />
[[Category:Alarms]]</div>Stefan Ritthttps://daq00.triumf.ca/MidasWiki/index.php?title=Alarm_System&diff=3210Alarm System2023-05-05T14:24:53Z<p>Stefan Ritt: /* Alarm triggering Slack notifications */</p>
<hr />
<div>{{Pagelinks}}<br />
<br />
<br />
= Links =<br />
<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3"><br />
* [[/Alarms ODB tree]]<br />
* [[Alarms Page]]<br />
* [[Mhttpd|mhttpd MIDAS web server]]<br />
</div><br />
<br />
= Introduction =<br />
MIDAS provides an alarm system, which by default is turned off. When the alarm system is [[/Alarms ODB tree#Alarm system active|activated]] and an alarm condition is detected, alarm messages are sent by the system which appear as an alarm banner on the [[Status Page|mhttpd status page]], and as a message on any windows running [[odbedit]] clients. The alarm system is flexible and can be extensively customized for each experiment<br />
using the [[Alarms Page|mhttpd Alarms Page]] or [[odbedit]]. <br />
<br />
The alarm system is built-in and part of the main experiment scheduler. This means no separate task is necessary to benefit from the alarm system. Its setup and activation is done through the '''[[/Alarms ODB tree]]'''. The alarm system includes several other features such as sequencing and control of the experiment. The alarm capabilities are:<br />
* Alarm setting on any ODB variable against a threshold parameter.<br />
* Alarm triggered by evaluated condition<br />
* Selection of Alarm check frequency<br />
* Selection of Alarm trigger frequency<br />
* Customization alarm scheme; under this scheme multiple choices of alarm type can be selected<br />
* Selection of alarm message destination (to system message log or to elog)<br />
* email or SMS alerts can be sent<br />
* Alarm triggered when a Program is not running <br />
<br />
<br />
= Implementation of the MIDAS Alarm System =<br />
<br />
The alarm system source code is [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/alarm_8c_source.html alarm.c].<br />
Alarms are checked inside [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html alarm.c::al_check()]. This function is called by cm_yield() every 10 seconds and by rpc_server_thread(), also every 10 seconds. For remote MIDAS clients, their al_check() issues an RPC_AL_CHECK RPC call into the MIDAS server utility [[mserver]], where rpc_server_dispatch() calls the local al_check(). As result, all alarm checks run inside a process directly attached to the local MIDAS shared memory (inside a local client or inside an mserver process for a remote client). Each and every MIDAS client runs the alarm checks. To prevent race conditions between different MIDAS clients, access to al_check() is serialized using the ALARM semaphore. Inside al_check(), alarms are triggered using <br />
[http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_trigger_alarm()], which in turn calls al_trigger_class(). Inside al_trigger_class(), the alarm is recorded into an elog or into [[Message System|midas.log]] using cm_msg(MTALK).<br />
<br />
Special note should be made of the ODB setting [[/Alarms ODB tree#System message interval|system message interval]], which has a surprising effect - after an alarm is recorded into system messages (using cm_msg(MTALK)), no record is made of any subsequent alarms until the time interval set by this variable elapses. With default value of 60 seconds, after one alarm, no more alarms are recorded for 60 seconds. Also, because all the alarms are checked at the same time, '''only the first''' triggered alarm will be recorded.<br />
<br />
As of alarm.c rev 4683, {{Odbpath|path=/Alarms/System message interval}} is set to 0 ensures that every alarm is recorded into the [[Message System#MIDAS Log file|MIDAS log file]]. (In previous revisions, this setting may still miss some alarms).<br />
<br />
<br />
<br />
<br />
<div id="Alarm class"></div><br />
= Alarms structure =<br />
The [[/Alarms ODB tree]] structure is split into 2 sections:<br />
*"Alarms" which define the condition to be tested. The user can create as many [[/Alarms ODB tree#Alarms subtree|Alarms]] as desired, but each must be one of the four defined [[#Alarm Types|Alarm types]] .<br />
*"Classes" which define the action to be taken when the alarm occurs. Two Classes (Alarm and Warning) are defined by default. The user can add more [[/Alarms ODB tree#Classes subtree|Classes]] as desired.<br />
<br><br><br />
<br />
In order to make the system flexible, each alarm class may perform different actions when an alarm is given. For example, it may<br />
* write a system message (see [[/Alarms ODB tree#Write System Message|Write System Message]])<br />
* write to the elog (see [[/Alarms ODB tree#Write Elog Message|Write Elog Message]])<br />
* stop the run (see [[/Alarms ODB tree#Stop run|Stop run]])<br />
* spawn a detached script listed in the ODB variable [[/Alarms ODB tree#Execute command|Execute command]]. This feature is used when an Alarm triggers Email or SMS alerts (see [[#Alarm triggering Email or SMS alerts|example]]).<br />
<br><br />
<br />
= Alarm Types =<br />
<br />
The four available Alarm Types are shown in Table 1. They are defined in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/midas_8h.html&nbsp midas.h]. The alarm type is entered into the [[/Alarms ODB tree#Type|Type]] key.<br />
<br />
<br />
{| style="text-align: left; width: 100%; background-color: white;" border="3" cellpadding="2" cellspacing="2"<br />
|+ Table 1 : Defined Alarm Types<br />
|-<br />
| colspan="2" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Alarm Type<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | INT value<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: lavender; font-weight: bold;" | Explanation<br />
<br />
|- <br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: bold;" |Internal alarms<br />
| colspan="1" rowspan="1" style="vertical-align: top; background-color: white; font-weight: normal;" | AT_INTERNAL<br />
|1<br />
|Trigger on internal (program) alarm setting through the use of the al_...() functions. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Program alarms<br />
|AT_PROGRAM<br />
|2<br />
|Triggered on condition of the state of the defined task (i.e. program not running)<br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Evaluated alarms<br />
|AT_EVALUATED<br />
|3<br />
|Triggered by ODB value on given arithmetical condition. <br />
<br />
|- <br />
| style="vertical-align: top; background-color: white; font-weight: bold;" |Periodic alarms<br />
|AT_PERIODIC<br />
|4<br />
|Triggered by timeout condition defined in the alarm setting. <br />
<br />
|}<br />
<br />
<br />
== Program Alarm ==<br />
<br />
Program (or rather "Program not running") alarms, when enabled, warn the user when a program is not running.<br />
<br />
Program alarms are enabled by setting the ODB key [[/Programs ODB tree#Alarm class|/Programs/<client-name>/Alarm class]] to a valid Alarm class specified in the [[/Alarms ODB tree]]. The first time the alarm is triggered, an <span style="color: purple; font-style:italic;">/Alarms/Alarms/<client-name></span> subtree will be created automatically. The program alarm will not be visible in the [[Alarms Page]] until the alarm has triggered, and the subtree created.<br />
<br />
<br />
The alarm system periodically calls [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__alfunctioncode.html al_check()]. This causes every client listed in the {{Odbpath|path=/Programs}} ODB tree to be tested using [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/group__cmfunctionc.html cm_exist()] to see if it is running. If the client is not running, the time of first failure is recorded in the ODB key [[/Programs ODB tree#First failed|/Programs/<client-name>/First failed]].<br />
<br />
If the client has not been running for longer than the time set in ODB key [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]], a "Program not running" alarm is triggered (if enabled by [[/Programs ODB tree#Alarm class|Alarm class]]) and the program is restarted (if enabled by [[/Programs ODB tree#Auto restart|/Programs/<client-name>/Auto restart]] and a valid [[/Programs ODB tree#Start command|Start command]] is supplied).<br />
<br />
The "not running" condition is tested every 10 seconds (each time al_check() is called), but the frequency of ''Program not running'' alarms can be reduced by increasing the value of the ODB key<br />
[[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]<br />
(default value 60 seconds). This can be useful if [[/Alarms ODB tree#System message interval|System message interval]] in the specified alarm class subtree is set to zero.<br />
<br />
<br />
== Periodic Alarm ==<br />
The periodic alarm is activated periodically according to the time in [[/Programs ODB tree#Check interval|/Programs/<client-name>/Check interval]]. An example of a periodic alarm is "Demo Periodic" in the [[/Alarms ODB tree#Example|example]].<br />
<br />
== Evaluated Alarm ==<br />
<br />
Evaluated alarms require an ''alarm condition'' which is entered into the ODB key [[/Alarms ODB tree#Condition|Condition]] in the [[/Alarms ODB tree#<alarm_name> subtree|<alarm_name> subtree]].<br />
The condition may be simply a '''comparison''' between any ODB variable and a threshold parameter, e.g.<br />
<br />
/Runinfo/Run number > 100<br />
or it may be an '''evaluated condition'''. One can write conditions like<br />
<br />
/Equipment/HV/Variables/Input[*] < 100<br />
or<br />
<br />
/Equipment/HV/Variables/Input[2-3] < 100<br />
to check all values from an array or a certain range. If one array element fulfills the alarm condition, the alarm is triggered. In addition, bit-wise alarm conditions are possible, e.g.<br />
<br />
/Equipment/Environment/Variables/Input[0] & 8<br />
The alarm is triggered if bit #3 is set in Input[0].<br />
<br />
The value of an evaluated alarm is computed using al_evaluate_condition() in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/html/alarm_8c_source.html alarm.c].<br />
<br />
== Internal Alarm ==<br />
These are triggered in a program using a call to <br />
[https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__alfunctioncode.html al_trigger_alarm()]. See also description of al_trigger_alarm() sequence [[#Implementation of the MIDAS Alarm System|above]].<br />
<br />
<br />
There is nothing surprising in these alarms. Each alarm is checked with a time period set by ODB key [[ /Alarms ODB tree#Check interval|Check interval]] in the [[/Alarms ODB tree]].<br />
<br />
= Alarm triggering Email or SMS alerts =<br />
<br />
It is possible to have the MIDAS alarm system send email or SMS alerts to cell phones when alarms are triggered. This can be configured by defining an ODB alarm on a critical ODB parameter, e.g.<br />
<br />
/Alarms/Alarms/Liquid Level<br />
Active y<br />
Triggered 0 (0x0)<br />
Type 3 (0x3)<br />
Check interval 60 (0x3C)<br />
Checked last 1227690148 (0x492D10A4)<br />
Time triggered first (empty)<br />
Time triggered last (empty)<br />
Condition /Equipment/Environment/Variables/Input[0] < 10<br />
Alarm Class Level Alarm<br />
Alarm Message Liquid Level is only %s<br />
<br />
In this example, the alarm triggers an alarm of class "Level Alarm". This alarm class is defined as follows:<br />
<br />
/Alarms/Classes/Level Alarm<br />
Write system message y<br />
Write Elog message n<br />
System message interval 600 (0x258)<br />
System message last 0 (0x0)<br />
Execute command /home/midas/level_alarm '%s'<br />
Execute interval 1800 (0x708)<br />
Execute last 0 (0x0)<br />
Stop run n<br />
Display BGColor red<br />
Display FGColor black<br />
<br />
The key here is to call a script "level_alarm", which can send emails. Use something like:<br />
<br />
#/bin/csh<br />
echo $1 | mail -s \"Level Alarm\" your.name@domain.edu<br />
odbedit -c 'msg 2 level_alarm \"Alarm was sent to your.name@domain.edu\"'<br />
<br />
The second command just generates a MIDAS system message for confirmation. Most cell phones (depends on the provider) have an email address. If you send an email there, it will be translated into a SMS message.<br />
<br />
The script file above can of course be more complicated. A perl script could be used that parses an address list, so other interested parties can register by adding his/her email address to that list. The script may also collects some other slow control variables (like pressure, temperature) and combine them into the SMS message.<br />
<br />
For very sensitive systems, having an alarm via SMS may not be sufficient, since the alarm system could be down (e.g. computer crash, network failure). In this case 'negative alarms' can be used. For example, every 30 minutes the system may send an SMS with the current parameter values. If the expected message is not received, it may indicate that something in the MIDAS system is wrong.<br />
<br />
= Alarm triggering Slack notifications =<br />
<br />
A more modern way for notification is to use messengers apps such as Slack. To send alarms to Slack, do the following:<br />
<br />
* Go to https://api.slack.com/apps<br />
* Create an App "MIDAS alarms", select "From scratch"<br />
* Name it "MIDAS alarm" or similar, select your workspace<br />
* Click on "Add features and functionality"<br />
* Select "Incoming Webhooks"<br />
* Activate Incoming Webhooks<br />
* Click on "Add New Webhook to Workspace"<br />
* Select channel where alarms get posted, allow access<br />
* Copy sample curl request and replace "Hello World" by "$1", such as: <br />
<br />
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$1\"}' https://hooks.slack.com/services/[xxx]/[yyy]/[zzz]<br />
<br />
(leave they keys xxx, yyy, zzz as shown on the web page).<br />
<br />
* Call the curl command from the alarm system by putting the above command under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
<br />
<br />
= Alarm triggering Telegram notifications =<br />
<br />
* A telegram bot needs to be created to post Midas alarms into telegram channels<br />
* Go to https://web.telegram.org and login by scanning the QR-code with the telegram app on your phone<br />
* Go to https://t.me/botfather<br />
* click on "open in web"<br />
* A chat window will open. <br />
* Send "/newbot" into the chat and assign name and username to your bot<br />
* At the end of the bot creation a access token will be displayed. Save that access token.<br />
* Post "/mybots" into the chat<br />
* Select the bot that was just created<br />
* Click on "Bot settings"<br />
* Click on "Allow Groups"<br />
* Enable groups for the bot<br />
* Click on "back to settings"<br />
* Click on Channel Admin Rights<br />
* Click on "Manage channel" (a checkmark should appear next to the text)<br />
* Close this chat.<br />
<br />
* Add the created bot to a telegram group chat via searching for the bot name in "add members" (same as adding new People to the group)<br />
* Now you need the chat_id of the same group chat. When you open the group chat in a webbrowser the URL should look like this https://web.telegram.org/a/#-1700000000 (where 1700000000 will be a different number for you)<br />
* The group chat_id is then -1001700000000 (add -100 in front of the number in your URL)<br />
* With the chat_id from above and the access token from the bot creation process you can post to the telegram group like this: (replace <chat_id> and <accessToken> with your numbers)<br />
<br />
curl -s -X POST "https://api.telegram.org/bot<accessToken>/sendMessage" -d chat_id=<chat_id> -d text="🚨 Midas alarm triggered with message = $1"<br />
<br />
* Create a shell script with this command<br />
* Call the shell script from the alarm system by putting "path/to/script.sh '%1$s'" under <code>/Alarms/Classes/All/Execute command</code> into the ODB.<br />
* Should you want to post the same alarm to multiple systems you can just separate scripts in <code>/Alarms/Classes/All/Execute command</code> with a semicolon ("path/to/script.sh '%1$s';path/to/script2.sh '%1$s'")<br />
<br />
<br />
[[Category:Alarms]]</div>Stefan Ritt