<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://daq00.triumf.ca/MidasWiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Bsmith</id>
	<title>MidasWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://daq00.triumf.ca/MidasWiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Bsmith"/>
	<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php/Special:Contributions/Bsmith"/>
	<updated>2026-04-29T11:20:54Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.6</generator>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3609</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3609"/>
		<updated>2026-03-18T18:59:19Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* seq object reference */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Midas contains a [[Sequencer]] that can be used to automate data-taking (e.g. starting and stopping many runs with different settings). The original Seqeuncer uses the custom Midas Script Language (MSL), which has a limited syntax. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens when the user starts the sequence and prompts the user to input the parameter values to use.&lt;br /&gt;
&lt;br /&gt;
The user can track the state of the sequence via a webpage that shows:&lt;br /&gt;
* the current line in the script, highlighted in yellow&lt;br /&gt;
* the value of any parameters/variables, shown in a table&lt;br /&gt;
* the progress of any loops or waits, shown as progress bars&lt;br /&gt;
 &lt;br /&gt;
[[File:Pysequencer.png|800px]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= Command-line arguments =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
usage: sequencer.py [-c name] [-h host_name] [-e expt_name] [-D] [-O] [--verbose] [--log-to-midas] [--help]&lt;br /&gt;
&lt;br /&gt;
options:&lt;br /&gt;
  -c name         Name of additional sequencer. E.g. if &#039;Test&#039;, ODB location will be /PySequencerTest&lt;br /&gt;
  -h host_name&lt;br /&gt;
  -e expt_name&lt;br /&gt;
  -D              Become a daemon&lt;br /&gt;
  -O              Become a daemon but retain stdout&lt;br /&gt;
  --verbose&lt;br /&gt;
  --log-to-midas  Write to midas message log as well as stdout/stderr (only for logging.info() etc, not regular print() statements)&lt;br /&gt;
  --help          Show this help message and exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Commands =&lt;br /&gt;
&lt;br /&gt;
PySequencer is generally designed to be controlled via the web interface. However it can also be controlled via settings in the ODB. Most settings are in /PySequencer/Command, but a couple are also in /PySequencer/State&lt;br /&gt;
&lt;br /&gt;
* In &#039;&#039;&#039;/PySequencer/Command&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;Load new file&#039;&#039;&#039; - load the file specified in /PySequencer/State/Filename&lt;br /&gt;
** &#039;&#039;&#039;Start script&#039;&#039;&#039; - start the script that has been loaded&lt;br /&gt;
** &#039;&#039;&#039;Pause script&#039;&#039;&#039; - pause the script by preventing the python interpreter from executing the next line of code&lt;br /&gt;
** &#039;&#039;&#039;Resume script&#039;&#039;&#039; - resume the script after pausing it&lt;br /&gt;
** &#039;&#039;&#039;Stop immediately&#039;&#039;&#039; - stop the sequence() function as soon as possible&lt;br /&gt;
** &#039;&#039;&#039;Stop after run&#039;&#039;&#039; - stop the sequence() function when the next run ends&lt;br /&gt;
** &#039;&#039;&#039;Cancel stop after run&#039;&#039;&#039; - cancel the &amp;quot;Stop after run&amp;quot; request&lt;br /&gt;
** &#039;&#039;&#039;Debug script&#039;&#039;&#039; - start script in debug mode where we won&#039;t move to the next line until &amp;quot;Step over&amp;quot; is set (usually via button on webpage)&lt;br /&gt;
** &#039;&#039;&#039;Step over&#039;&#039;&#039; - move to next line when running in debug mode&lt;br /&gt;
* In &#039;&#039;&#039;/PySequencer/State&lt;br /&gt;
** &#039;&#039;&#039;Path&#039;&#039;&#039; - specify a subdirectory beneath &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer where your files are located&lt;br /&gt;
** &#039;&#039;&#039;Filename&#039;&#039;&#039; - filename to load (relative to &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer or &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer/&amp;lt;path&amp;gt;)&lt;br /&gt;
** &#039;&#039;&#039;Message&#039;&#039;&#039; - if &amp;quot;Message wait&amp;quot; is true, the sequence will not continue until &amp;quot;Message&amp;quot; has been reset to an empty string - see [[#Messages | the messages section]] for more details&lt;br /&gt;
&lt;br /&gt;
= User script content =&lt;br /&gt;
&lt;br /&gt;
All scripts must be located in the &#039;&#039;&#039;userfiles/sequencer&#039;&#039;&#039; subdirectory of your [[Exptab | experiment directory]]. PySequencer will only load files with a .py extension.&lt;br /&gt;
&lt;br /&gt;
PySequencer will look for three specific functions in your file:&lt;br /&gt;
* &#039;&#039;&#039;define_params(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
* &#039;&#039;&#039;sequence(seq)&#039;&#039;&#039; - required&lt;br /&gt;
* &#039;&#039;&#039;at_exit(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
These are described in more detail below. All three should accept one argument, which is a [[#seq_object_reference | midas.sequencer.SequenceClient]] object. This object is also available as a global variable within your script (e.g. if you want to define helper functions and don&#039;t want to pass the seq object to each of them).&lt;br /&gt;
&lt;br /&gt;
Your file may include any other modules, and you can define/call your own functions. If you edit the content of your file, you can reload it using the web interface or by setting the ODB parameter &amp;quot;/PySequencer/Command/Load new file&amp;quot; to true. When we reload a file, we also reload all modules that it uses (i.e. if you make changes to a custom module that contains helper functions, you can trigger loading the new definitions by reloading your main script).&lt;br /&gt;
&lt;br /&gt;
== define_params(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to create &amp;quot;parameters&amp;quot; that the user will be prompted for when they start the sequence. It is expected to be a set of &#039;&#039;&#039;seq.register_param()&#039;&#039;&#039; calls. For each parameter, you specify a name, a comment that will be shown in the prompt dialog, a default value, and optionally a list of acceptable values.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;default value&amp;quot; you provide determines the type of the variable in the ODB. If you specify a string it will be a TID_STRING, an integer will become TID_INT32 etc.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    # Will be stored as an integer&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a float&lt;br /&gt;
    seq.register_param(&amp;quot;voltage&amp;quot;, &amp;quot;PMT voltage&amp;quot;, 1342.7)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a string, and user will see a dropdown to choose from&lt;br /&gt;
    seq.register_param(&amp;quot;run type&amp;quot;, &amp;quot;Purpose of this run&amp;quot;, &amp;quot;normal&amp;quot;, [&amp;quot;normal&amp;quot;, &amp;quot;calibration&amp;quot;, &amp;quot;testing&amp;quot;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The main sequence() function can then retrieve the values of these parameters by calling &#039;&#039;&#039;seq.get_param(&amp;quot;voltage&amp;quot;)&#039;&#039;&#039; etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer params.png|400px]]&lt;br /&gt;
&lt;br /&gt;
== sequence(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function MUST be defined, and is the main script that will be executed. Full documentation of the functions available is in the [[#seq_object_reference | seq object reference]] section, but you may call/define your own functions as well, and also call functions from other modules.&lt;br /&gt;
&lt;br /&gt;
There are a few &amp;quot;best practices&amp;quot; to consider to maximise integration with the webpage:&lt;br /&gt;
* Use &#039;&#039;&#039;seq.wait_seconds()&#039;&#039;&#039; instead of &#039;&#039;&#039;time.sleep()&#039;&#039;&#039; so you get a progress bar on the webpage. &#039;&#039;&#039;seq.sleep()&#039;&#039;&#039; is an alias for seq.wait_seconds() if you prefer.&lt;br /&gt;
* In a normal &amp;quot;for&amp;quot; loop use &#039;&#039;&#039;seq.range()&#039;&#039;&#039; instead of &#039;&#039;&#039;range()&#039;&#039;&#039; so you get a progress bar on the webpage. We don&#039;t have a way to show progress through any other type of for/while loop&lt;br /&gt;
* Avoid any very long-lasting calls (e.g. blocking on a socket) as these may prevent us from being able to stop the sequence when the user calls the &amp;quot;Stop immediately&amp;quot; command. Better to poll in a loop until a condition is met.&lt;br /&gt;
&lt;br /&gt;
== at_exit(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to &amp;quot;tidy up&amp;quot; when the sequence stops (either due to the sequence() function exiting normally, the user requesting &amp;quot;Stop immediately&amp;quot; or &amp;quot;Stop after run&amp;quot;, or the sequence() function throwing an exception). For example, you may wish to move a motor back to a default location, or ramp down PMT voltages etc.&lt;br /&gt;
&lt;br /&gt;
Note that this is not bulletproof and should not be relied on for safety-critical items! Examples of situations where at_exit() will NOT be called include:&lt;br /&gt;
* sequencer.py being killed by a user (Ctrl-C etc) or the OS (out-of-memory killer, rebooting etc)&lt;br /&gt;
* the user&#039;s sequence() function corrupting the python interpreter so badly that it affects the main thread&lt;br /&gt;
* the user&#039;s sequence() function entering an uninterruptible state where &amp;quot;Stop immediately&amp;quot; can&#039;t actually stop anything (e.g. blocking wait on a socket)&lt;br /&gt;
* unidentified bugs in sequencer.py&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
There are two different types of messages in the sequencer:&lt;br /&gt;
* regular [[Message_System | Midas messages]] that get written to the message log&lt;br /&gt;
* sequencer-specific messages that show as a dialog box on the sequencer webpage&lt;br /&gt;
&lt;br /&gt;
You can emit regular midas messages using the &#039;&#039;&#039;seq.msg(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&#039;&#039;&#039; function. Error messages get highlighted in red on the [[Message_Page | message page]]. The &amp;quot;facility&amp;quot; routes the message to different log files / different buttons on the message page.&lt;br /&gt;
&lt;br /&gt;
You can emit sequencer-specific messages using the &#039;&#039;&#039;seq.sequencer_msg(text, wait=False)&#039;&#039;&#039; function. If &amp;quot;wait&amp;quot; is True, the sequence will not continue until the user has clicked the &amp;quot;Close&amp;quot; button on the webpage to acknowledge the message.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer message.png|600px]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
PySequencer is integrated with the python logging module. You may use the global &#039;&#039;&#039;logger&#039;&#039;&#039; variable to log your own info/debug messages. You can configure what happens to these logger messages using either command-line arguments or functions in the seq object.&lt;br /&gt;
&lt;br /&gt;
To print logger.debug() messages, you can pass the &#039;&#039;&#039;--verbose&#039;&#039;&#039; command-line flag and/or call &#039;&#039;&#039;seq.set_py_logger_debug(True)&#039;&#039;&#039; in your script. The default is to only print info() messages or higher.&lt;br /&gt;
&lt;br /&gt;
You can also copy messages to the regular midas message log (so you can call logger.info() instead of seq.msg(), and still have messages appear in the midas message log). The &amp;quot;facility&amp;quot; in this case is &amp;quot;pysequencer&amp;quot;. You can enable this functionality using the &#039;&#039;&#039;--log-to-midas&#039;&#039;&#039; command-line flag and/or by calling &#039;&#039;&#039;seq.set_py_logger_to_midas(True)&#039;&#039;&#039; in your script. This is most useful for experiments where terminal access is very limited, and they wish to debug/monitor everything via log files that are visible through webpages.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer log.png|800px]]&lt;br /&gt;
&lt;br /&gt;
== seq object reference ==&lt;br /&gt;
&lt;br /&gt;
Users have access to the &#039;&#039;&#039;seq&#039;&#039;&#039; object in their scripts, which is a &#039;&#039;&#039;midas.sequencer.SequenceClient&#039;&#039;&#039; object. This class inherits from the standard &#039;&#039;&#039;midas.client.MidasClient&#039;&#039;&#039; object, and adds extra sequencer-specific tools.&lt;br /&gt;
&lt;br /&gt;
Full documentation of these classes can be found in docstrings in the source code:&lt;br /&gt;
* MidasClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/client.py&lt;br /&gt;
* SequenceClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/sequencer.py&lt;br /&gt;
&lt;br /&gt;
A summary of the available commands is:&lt;br /&gt;
* Sequencer-specific:&lt;br /&gt;
** seq.&#039;&#039;&#039;register_param&#039;&#039;&#039;(name, comment, default_value, options=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;get_param&#039;&#039;&#039;(name)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;range&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_seconds&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_odb&#039;&#039;&#039;(path, op, target, between_upper_target=None, stable_for_n_secs=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_func&#039;&#039;&#039;(func, check_period_secs=0.1)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_clients_running&#039;&#039;&#039;(client_names, timeout_secs=10)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_events&#039;&#039;&#039;(target)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_run_description&#039;&#039;&#039;(desc)&lt;br /&gt;
** seq.&#039;&#039;&#039;disable_tracing&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;enable_tracing&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;keep_modules_when_reloading_script&#039;&#039;&#039;(module_names)&lt;br /&gt;
* Run control&lt;br /&gt;
** seq.&#039;&#039;&#039;start_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;pause_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;resume_run&#039;&#039;&#039;()&lt;br /&gt;
* ODB access&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get&#039;&#039;&#039;(path, recurse_dir=True, include_key_metadata=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_set&#039;&#039;&#039;(path, contents, .....) - many optional parameters!&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_exists&#039;&#039;&#039;(path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_delete&#039;&#039;&#039;(path, follow_links=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_link&#039;&#039;&#039;(link_path, destination_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get_link_destination&#039;&#039;&#039;(link_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_rename&#039;&#039;&#039;(current_path, new_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_last_update_time&#039;&#039;&#039;(path)&lt;br /&gt;
* Event buffers&lt;br /&gt;
** seq.&#039;&#039;&#039;open_event_buffer&#039;&#039;&#039;(buffer_name, buf_size=None, max_event_size=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;register_event_request&#039;&#039;&#039;(buffer_handle, event_id=-1, trigger_mask=-1, sampling_type=midas.GET_ALL)&lt;br /&gt;
** seq.&#039;&#039;&#039;receive_event&#039;&#039;&#039;(buffer_handle, async_flag=True, use_numpy=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;deregister_event_request&#039;&#039;&#039;(buffer_handle, request_id)&lt;br /&gt;
** seq.&#039;&#039;&#039;send_event&#039;&#039;&#039;(buffer_handle, event)&lt;br /&gt;
* Controlling other clients&lt;br /&gt;
** seq.&#039;&#039;&#039;start_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_all_required_program_names&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;start_all_required_program&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;client_exists&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;clients_exist&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;connect_to_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;disconnect_from_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;jrpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;brpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
* Alarms&lt;br /&gt;
** seq.&#039;&#039;&#039;get_triggered_alarms&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;reset_alarm&#039;&#039;&#039;(alarm_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;create_alarm_class&#039;&#039;&#039;(class_name, execute_command=&amp;quot;&amp;quot;, execute_interval_secs=0, stop_run=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;create_evaluated_alarm&#039;&#039;&#039;(alarm_name, odb_condition, message=None, alarm_class=&amp;quot;Alarm&amp;quot;, activate_immediately=True)&lt;br /&gt;
** seq.&#039;&#039;&#039;trigger_internal_alarm&#039;&#039;&#039;(alarm_name, message, default_alarm_class=&amp;quot;Alarm&amp;quot;)&lt;br /&gt;
* History&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_create_plot&#039;&#039;&#039;(group_name, panel_name, variables, labels=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_data&#039;&#039;&#039;(start_time, end_time, interval_secs, event_name, tag_name, index=None, timestamps_as_datetime=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_events&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_recent_data&#039;&#039;&#039;(num_hours, interval_secs, event_name, tag_name, index=None, timestamps_as_datetime=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_tags&#039;&#039;&#039;(event_name)&lt;br /&gt;
* Messages&lt;br /&gt;
** seq.&#039;&#039;&#039;msg&#039;&#039;&#039;(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;get_recent_messages&#039;&#039;&#039;(min_messages=1, before=None, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** seq.&#039;&#039;&#039;get_message_facilities&#039;&#039;&#039;()&lt;br /&gt;
* Logging&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger&#039;&#039;&#039;(debug, to_midas_msg_log)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger_debug&#039;&#039;&#039;(debug)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger_to_midas&#039;&#039;&#039;(to_midas_msg_log)&lt;br /&gt;
* Misc&lt;br /&gt;
** seq.&#039;&#039;&#039;get_midas_version&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_experiment_dir&#039;&#039;&#039;()&lt;br /&gt;
* Functions that are in MidasClient, but may NOT be used in a sequencer script&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_watch&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_jrpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_brpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_disconnect_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_transition_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;communicate&#039;&#039;&#039;()&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
&lt;br /&gt;
== Variables shown on webpage ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;variables&amp;quot; table on the webpage will display only local/global variables of the types: int, float, string, bool, numpy.number and numpy.bool. It will also display lists and numpy.ndarrays of those types. It does not display any other types (e.g. datetime.datetime, custom objects, dicts etc). We also don&#039;t show any variables where the name starts with a double underscore.&lt;br /&gt;
&lt;br /&gt;
== No callback functions ==&lt;br /&gt;
&lt;br /&gt;
Your script cannot use any of the callback-based functions present in MidasClient. This includes odb_watch(), register_jrpc_callback(), register_brpc_callback(), register_message_callback(), register_transition_callback(), register_disconnect_callback() and communicate(). This is because your script runs in a separate thread, but the main thread is the one responsible for talking to midas; any callbacks get executed in the context of the main thread, not your script&#039;s thread.&lt;br /&gt;
&lt;br /&gt;
If you want to check whether an ODB value has changed, you&#039;ll need to implement a loop and call odb_get() repeatedly. &lt;br /&gt;
&lt;br /&gt;
You MAY use register_event_request() if you want to look at data as part of your script, as you then call receive_event() to explicitly look at the events, rather than relying on a callback function.&lt;br /&gt;
&lt;br /&gt;
== No tracing in other modules ==&lt;br /&gt;
&lt;br /&gt;
It is expected that experiments will build a library/module of helper functions to automate certain tasks (e.g. &#039;&#039;&#039;move_calibration_source(x, y)&#039;&#039;&#039;). Your sequencer script can import such a module and call the functions, but the webpage will not show what is happening within that module - it would just show that we&#039;re in move_calibration_source().&lt;br /&gt;
&lt;br /&gt;
It is technically possible to enhance the PySequencer logic to trace within these modules, but it is not yet clear whether users want such a feature, and what the best user interface would be.&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3604</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3604"/>
		<updated>2026-02-19T21:45:47Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* mjshistory */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. 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).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
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 &amp;quot;virtual&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
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.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&lt;br /&gt;
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.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
To enable clicks on a menu item that result in an onclick event (no html refresh), one can create and ODB key in the /Custom (or its subdirectories) whose value is a string that does not end with a valid extension (txt|jpg|jpeg|png|gif|pdf|csv|html|js|css). In this case, the string will be encapsulated into and onclick event. For example,&lt;br /&gt;
odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    Click                             alert(1)&lt;br /&gt;
&lt;br /&gt;
Clicking on the menu item &amp;quot;Click&amp;quot; will result in an alert window.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* 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.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; 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 &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;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.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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 &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || 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.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; 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.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; 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 &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
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 &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Color of gauge bar, &amp;quot;black&amp;quot; if not present. Alternatively one can define color segments like &#039;&#039;&#039;[{0, &#039;green&#039;},{7,&#039;yellow&#039;},{9,&#039;red}]&#039;&#039;&#039; to change the bar color dynamically. If the value is above 0 and blow 7, the bar will be green in this example, yellow between 7 and below 9, and red at or above 9.&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
== Pre-configured plots ==&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| 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/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== On-demand plots ==&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). 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 &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;. Valid parameters are:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Meaning !! Default&lt;br /&gt;
|-&lt;br /&gt;
| width || Width in px || 400&lt;br /&gt;
|-&lt;br /&gt;
| height || Height in px || 300&lt;br /&gt;
|-&lt;br /&gt;
| x || X position on screen in px || Middle of screen&lt;br /&gt;
|-&lt;br /&gt;
| y || Y position on screen in px || Middle of screen&lt;br /&gt;
|-&lt;br /&gt;
| Timescale || X axis span - 10m, 1h, 24h, 3d etc || 1h&lt;br /&gt;
|-&lt;br /&gt;
| Minimum || Y axis minimum || 0&lt;br /&gt;
|-&lt;br /&gt;
| Maximum || Y axis maximum || 0 (setting Minimum equal to Maximum implies auto-scaling)&lt;br /&gt;
|-&lt;br /&gt;
| Zero ylow || Force Y minimum to 0 (true/false) || false&lt;br /&gt;
|-&lt;br /&gt;
| Log axis || Make Y axis logarithmic (true/false) || false &lt;br /&gt;
|-&lt;br /&gt;
| Show run markers || Show dashed lines when runs start/stop (true/false) || false&lt;br /&gt;
|-&lt;br /&gt;
| Show values || Show a legend that includes current value of each variable (true/false) || true&lt;br /&gt;
|-&lt;br /&gt;
| Show raw value || If using a formula, show the manipulated value on the Y axis, but the raw value in the legend and when hovering. (Note the parameter must be an array of length 1 containing true/false - e.g. [true] or [false]) || [false]&lt;br /&gt;
|-&lt;br /&gt;
| Show fill || Shade the area between data and Y=0 (true/false) || true&lt;br /&gt;
|-&lt;br /&gt;
| Formula || Transform the raw history variable using placeholder &amp;quot;x&amp;quot; (eg. &amp;quot;x * 1.234&amp;quot;) || Empty string, implying no formula&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with an on-demand history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button class=&amp;quot;mbutton&amp;quot; onclick=&amp;quot;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;, &amp;quot;Show run markers&amp;quot;: true});&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot; style=&amp;quot;width:10px;&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Implementing SVGs =&lt;br /&gt;
&lt;br /&gt;
An SVG file loaded on a custom page can be interactively modified using JavaScript.&lt;br /&gt;
This is useful for graphically displaying ODB values in complex service or detector systems.&lt;br /&gt;
&lt;br /&gt;
An SVG file can be embedded like this:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&amp;lt;object id=&amp;quot;svgObject&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; data=&amp;quot;Midas_helium_example.svg&amp;quot;&amp;gt;&amp;lt;/object&amp;gt; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Objects within the SVG can be modified—for example, by changing their fill color as shown below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
const svgDoc = document.getElementById(&#039;svgObject&#039;).contentDocument; &lt;br /&gt;
const obj_Valve = svgDoc.getElementById(valveID); &lt;br /&gt;
obj_Valve.style.fill = &amp;quot;#00FF00&amp;quot;; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SVG images are stored in XML format. In Inkscape, object properties can be interactively edited using the XML Editor.&lt;br /&gt;
A full example is given below in [[SVGs on Custom Pages]]&lt;br /&gt;
&lt;br /&gt;
= Tabbed Pages =&lt;br /&gt;
&lt;br /&gt;
Once custom pages get complex, it is possible to distribute the controls over several&lt;br /&gt;
pages. The user can then click on individual tabs at the top to switch between the pages.&lt;br /&gt;
This can be implemented following this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mtabs&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab active&amp;quot; data-target=&amp;quot;p1&amp;quot;&amp;gt;First&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p2&amp;quot;&amp;gt;Second&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p3&amp;quot;&amp;gt;Third&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p1&amp;quot; class=&amp;quot;mpanel active&amp;quot;&amp;gt;&lt;br /&gt;
   Page 1&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p2&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 2&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p3&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 3&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates three pages with three associated tabs &amp;quot;First&amp;quot;, &amp;quot;Second&amp;quot; and &amp;quot;Third&amp;quot;. The &amp;quot;active&amp;quot; flag indicates which tab is active initially. To activate a tab programmatically, one can simulate a mouse click on that tab with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
let tab = document.getElementsByClassName(&#039;mtab&#039;);&lt;br /&gt;
tab[1].click(); // index is 0, 1, 2 for the three pages&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is then what you get:&lt;br /&gt;
&lt;br /&gt;
[[File:Tabbed page.png|frame|left|alt=Example of a tabbed custom page|Figure 1  Example of a tabbed custom page]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 2  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Mjsonrpc&amp;diff=3575</id>
		<title>Mjsonrpc</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Mjsonrpc&amp;diff=3575"/>
		<updated>2025-11-14T00:05:57Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* MIDAS JSON RPC API Schema */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Mhttpd.js|MIDAS Javascript library]]&lt;br /&gt;
* [[Custom Page]]&lt;br /&gt;
* [[Custom Page Features]]&lt;br /&gt;
* [[mhttpd]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= MIDAS JSON-RPC interface =&lt;br /&gt;
This page described the MIDAS JSON-RPC interface found in the MIDAS Javascript library [[mhttpd.js]].&lt;br /&gt;
&lt;br /&gt;
== JSON general information ==&lt;br /&gt;
&lt;br /&gt;
JSON is a lightweight data-interchange format usually associated with Javascript and web programming. It is a popular choice as replacement for older general purpose data formats such as XML. JSON is defined by [https://tools.ietf.org/html/rfc7159 RFC-7159] (read more at http://www.json.org/). When necessary, the overhead of text encoded JSON is reduced by compressing JSON documents (using gzip), or by using binary-encoded JSON.&lt;br /&gt;
&lt;br /&gt;
JSON documents look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{ &amp;quot;Runinfo&amp;quot; : { &amp;quot;State&amp;quot; : 1, &amp;quot;Online Mode&amp;quot; : 1, &amp;quot;Run number&amp;quot; : 13585 } }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the JSON standard is incompatible with [https://en.wikipedia.org/wiki/IEEE_floating_point IEEE Standard for Floating-Point Arithmetic (IEEE 754)], specifically, there is no standard way to encode the special numerical values +Infinity, -Infinity and NaN (&amp;quot;-0.0&amp;quot; is ok).&lt;br /&gt;
&lt;br /&gt;
In MIDAS, JSON support is provided by an ODB data encoder (in odb.c), an ODB &amp;quot;JSON paste&amp;quot; decoder (in json_paste.cxx) and a general purpose JSON encoder/decoder (mjson.h, mjson.cxx, see https://daq.triumf.ca/~daqweb/doc/midas-devel/html/mjson_8h.html and https://daq.triumf.ca/~daqweb/doc/midas-devel/html/class_m_json_node.html).&lt;br /&gt;
&lt;br /&gt;
The MIDAS implementation of JSON has following variances from the JSON standard:&lt;br /&gt;
* numerical values +Infinity, -Infinity and NaN are encoded and decoded as JSON strings &amp;quot;Infinity&amp;quot;, &amp;quot;-Infinity&amp;quot; and &amp;quot;NaN&amp;quot;. This is compatible with most in-browser JSON implementations. See also http://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript and similar.&lt;br /&gt;
* numerical values that are usually presented as hex numbers, such a DWORD values, are encoded as JSON strings, i.e. &amp;quot;0x55b961c8&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== JSON encoding of ODB data ==&lt;br /&gt;
&lt;br /&gt;
MIDAS has 3 way to encode ODB data into JSON:&lt;br /&gt;
* &amp;quot;save&amp;quot; format for saving all ODB data and metadata, ODB can be fully reloaded or restored from an ODB JSON save file. Links are preserved as links, upper and lower case in names of ODB keys is preserved.&lt;br /&gt;
* &amp;quot;db_values&amp;quot; format for exporting ODB data to web pages: links are followed to their final values, &#039;&#039;&#039;ODB key names are converted to lower case&#039;&#039;&#039; for use with case-sensitive languages such as Javascript.&lt;br /&gt;
* &amp;quot;list&amp;quot; format encodes a single ODB directory and returns the full information printed by the odbedit &amp;quot;ls -l&amp;quot; command.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
=== JSON &amp;quot;save&amp;quot; format: db_copy_json_save() ===&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;save&amp;quot; format encodes all ODB data and metadata. ODB can be fully reloaded or restored from ODB JSON save files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ odbedit&lt;br /&gt;
odbedit&amp;gt; cd runinfo&lt;br /&gt;
[local:testexpt:S]/Runinfo&amp;gt;json&lt;br /&gt;
status: 1, json: {&lt;br /&gt;
  &amp;quot;State/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212553 },&lt;br /&gt;
  &amp;quot;State&amp;quot; : 1,&lt;br /&gt;
  &amp;quot;Online Mode/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1436830326 },&lt;br /&gt;
  &amp;quot;Online Mode&amp;quot; : 1,&lt;br /&gt;
  &amp;quot;Run number/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212481 },&lt;br /&gt;
  &amp;quot;Run number&amp;quot; : 13585,&lt;br /&gt;
  &amp;quot;Transition in progress/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212553 },&lt;br /&gt;
  &amp;quot;Transition in progress&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;Start abort/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212552 },&lt;br /&gt;
  &amp;quot;Start abort&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;Requested transition/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1436923816 },&lt;br /&gt;
  &amp;quot;Requested transition&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;Start time/key&amp;quot; : { &amp;quot;type&amp;quot; : 12, &amp;quot;item_size&amp;quot; : 32, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212481 },&lt;br /&gt;
  &amp;quot;Start time&amp;quot; : &amp;quot;Wed Jul 29 16:28:01 2015&amp;quot;,&lt;br /&gt;
  &amp;quot;Start time binary/key&amp;quot; : { &amp;quot;type&amp;quot; : 6, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212481 },&lt;br /&gt;
  &amp;quot;Start time binary&amp;quot; : &amp;quot;0x55b96181&amp;quot;,&lt;br /&gt;
  &amp;quot;Stop time/key&amp;quot; : { &amp;quot;type&amp;quot; : 12, &amp;quot;item_size&amp;quot; : 32, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212552 },&lt;br /&gt;
  &amp;quot;Stop time&amp;quot; : &amp;quot;Wed Jul 29 16:29:12 2015&amp;quot;,&lt;br /&gt;
  &amp;quot;Stop time binary/key&amp;quot; : { &amp;quot;type&amp;quot; : 6, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212552 },&lt;br /&gt;
  &amp;quot;Stop time binary&amp;quot; : &amp;quot;0x55b961c8&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
[local:testexpt:S]/Runinfo&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== JSON &amp;quot;db_values&amp;quot; format: db_copy_json_values() ===&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;db_values&amp;quot; format is intended for easy web page development - main thing is to return easy to parse ODB values with minimal metadata (only ODB last_written is provided).&lt;br /&gt;
&lt;br /&gt;
Because most programming languages and most JSON parser (including Javascript) are case sensitive, but ODB key names are not, this JSON encoding normalizes ODB key names by converting them to lower-case. (Converting spaces to underscores is also a possibility). Only the function &amp;quot;db_get_values&amp;quot; returns lower-case names - this remains the simplest way to have simple parsing of ODB JSON data in Javascript. &amp;quot;db_get_values&amp;quot; now returns an additional field for true names of ODB entries from before they were converted to lower case. This can be turned off with the &amp;quot;omit_names&amp;quot; RPC parameter.&lt;br /&gt;
&lt;br /&gt;
This allows easy use of parsed JSON in Javascript, i.e. runinfo.state or runinfo[&amp;quot;run number&amp;quot;] while avoiding case-matching getter functions (i.e. get_case_insensitive_property(runinfo, &amp;quot;state&amp;quot;)).&lt;br /&gt;
&lt;br /&gt;
Symlinks are followed to their final values and subdirectories are recursed.&lt;br /&gt;
&lt;br /&gt;
An option is provided to &#039;&#039;&#039;transfer the minimum amount of data&#039;&#039;&#039; by suppressing  the &#039;last_written&#039; value and the true names by setting the RPC parameters &amp;quot;omit_last_written&amp;quot; and &amp;quot;omit_names&amp;quot;. See $MIDASSYS/examples/javascript1/example.html. &lt;br /&gt;
&lt;br /&gt;
Also you can specify a timestamp and only data that is newer will be returned (caveats: no way to get rid of empty directories, for now, and no way to specify timestamps for individual array elements - you get the whole array or none of it).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ odbedit&lt;br /&gt;
odbedit&amp;gt; cd runinfo&lt;br /&gt;
[local:testexpt:S]/Runinfo&amp;gt;jsvalues&lt;br /&gt;
status: 1, json: {&lt;br /&gt;
  &amp;quot;state/last_written&amp;quot; : 1438212553,&lt;br /&gt;
  &amp;quot;state&amp;quot; : 1,&lt;br /&gt;
  &amp;quot;online mode/last_written&amp;quot; : 1436830326,&lt;br /&gt;
  &amp;quot;online mode&amp;quot; : 1,&lt;br /&gt;
  &amp;quot;run number/last_written&amp;quot; : 1438212481,&lt;br /&gt;
  &amp;quot;run number&amp;quot; : 13585,&lt;br /&gt;
  &amp;quot;transition in progress/last_written&amp;quot; : 1438212553,&lt;br /&gt;
  &amp;quot;transition in progress&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;start abort/last_written&amp;quot; : 1438212552,&lt;br /&gt;
  &amp;quot;start abort&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;requested transition/last_written&amp;quot; : 1436923816,&lt;br /&gt;
  &amp;quot;requested transition&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;start time/last_written&amp;quot; : 1438212481,&lt;br /&gt;
  &amp;quot;start time&amp;quot; : &amp;quot;Wed Jul 29 16:28:01 2015&amp;quot;,&lt;br /&gt;
  &amp;quot;start time binary/last_written&amp;quot; : 1438212481,&lt;br /&gt;
  &amp;quot;start time binary&amp;quot; : &amp;quot;0x55b96181&amp;quot;,&lt;br /&gt;
  &amp;quot;stop time/last_written&amp;quot; : 1438212552,&lt;br /&gt;
  &amp;quot;stop time&amp;quot; : &amp;quot;Wed Jul 29 16:29:12 2015&amp;quot;,&lt;br /&gt;
  &amp;quot;stop time binary/last_written&amp;quot; : 1438212552,&lt;br /&gt;
  &amp;quot;stop time binary&amp;quot; : &amp;quot;0x55b961c8&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
[local:testexpt:S]/Runinfo&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== JSON &amp;quot;ls&amp;quot; format: db_copy_json_ls() ===&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;ls&amp;quot; format returns all the data printed by odbedit command &amp;quot;ls -l&amp;quot; and shown by the mhttpd ODB editor web page. It is intended for implementing web ODB editor functions.&lt;br /&gt;
&lt;br /&gt;
Note how subdirectories are encoded as empty JSON objects &amp;quot;{}&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ odbedit&lt;br /&gt;
odbedit&amp;gt; cd experiment&lt;br /&gt;
[local:testexpt:S]/Experiment&amp;gt;jsls&lt;br /&gt;
jsls &amp;quot;/Experiment&amp;quot;, status: 1, json: {&lt;br /&gt;
  &amp;quot;Name/key&amp;quot; : { &amp;quot;type&amp;quot; : 12, &amp;quot;item_size&amp;quot; : 32, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1448038414 },&lt;br /&gt;
  &amp;quot;Name&amp;quot; : &amp;quot;testexpt&amp;quot;,&lt;br /&gt;
  &amp;quot;Buffer sizes&amp;quot; : { },&lt;br /&gt;
  &amp;quot;edit on start&amp;quot; : { },&lt;br /&gt;
  &amp;quot;Security&amp;quot; : { },&lt;br /&gt;
  &amp;quot;Transition debug flag/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1436830326 },&lt;br /&gt;
  &amp;quot;Transition debug flag&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;Transition timeout/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1436830326 },&lt;br /&gt;
  &amp;quot;Transition timeout&amp;quot; : 30000,&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Access to ODB arrays ==&lt;br /&gt;
&lt;br /&gt;
Some (not all) JSON-RPC methods allow access to individual array elements in ODB, i.e. {{Odbpath|path=/equipment/rpcexample/settings/a[10]}}.&lt;br /&gt;
&lt;br /&gt;
RPC methods that can do this are:&lt;br /&gt;
* db_copy&lt;br /&gt;
* db_get_values&lt;br /&gt;
* db_paste&lt;br /&gt;
&lt;br /&gt;
=== Supported array index syntax ===&lt;br /&gt;
An array index syntax has been implemented to make it easier to write individual array elements (Table 1)&lt;br /&gt;
;Table 1&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Array Syntax !! Array Elements !! Number of elements/data values&lt;br /&gt;
|-&lt;br /&gt;
| a[1] || 1 || 1 &lt;br /&gt;
|-&lt;br /&gt;
| a[1,2,3] || 1,2,3 || 3 &lt;br /&gt;
|-&lt;br /&gt;
| a[1-3] || 1,2,3 || 3 &lt;br /&gt;
|-&lt;br /&gt;
| a[3-1] || 3,2,1 || 3 &lt;br /&gt;
|-&lt;br /&gt;
| a[1,2,3-5,6] || 1,2,3,4,5,6 || 6 &lt;br /&gt;
|-&lt;br /&gt;
|  a[1-3,4-6,7-9]|| 1,2,3,4,5,6,7,8,9 || 9 &lt;br /&gt;
|-&lt;br /&gt;
| a[3-0,6-4,9-7] || 3,2,1,6,5,4,9,8,7 || 9  &lt;br /&gt;
|-&lt;br /&gt;
| a[4,2,5-6,8] || 4,2,5,6,8 || 5&lt;br /&gt;
|-&lt;br /&gt;
| a || 0-n || n &amp;lt;sup&amp;gt;**&amp;lt;/sup&amp;gt; &lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;sup&amp;gt;**&amp;lt;/sup&amp;gt; &amp;lt;small&amp;gt;n=number of data values supplied - see [[#array name only]].&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See [[#Writing an array]] and [[#Reading an array]] for examples.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== JSON-RPC general information ==&lt;br /&gt;
&lt;br /&gt;
* JSON-RPC standard: https://daq.triumf.ca/~daqweb/doc/midas-devel/doc/jsonrpc/JSON-RPC%202.0%20Specification.html&lt;br /&gt;
* JSON-RPC home page: http://www.jsonrpc.org/specification&lt;br /&gt;
* JSON-RPC via HTTP transport: https://daq.triumf.ca/~daqweb/doc/midas-devel/doc/jsonrpc/simple%20is%20better%20-%20JSON-RPC%202.0%20Transport_%20HTTP.html&lt;br /&gt;
* http://www.simple-is-better.org/json-rpc/index.html&lt;br /&gt;
&lt;br /&gt;
== Javascript client library ==&lt;br /&gt;
&lt;br /&gt;
MIDAS provides a simple client library:&lt;br /&gt;
* documentation, see the mjsonrpc functions: https://daq.triumf.ca/~daqweb/doc/midas-develop/html/group__mjsonrpc__js.html&lt;br /&gt;
* source code, see the mjsonrpc functions: https://daq.triumf.ca/~daqweb/doc/midas-develop/resources/mhttpd.js&lt;br /&gt;
&lt;br /&gt;
The MIDAS JSON RPC client library is based on the &#039;&#039;&#039;Javascript Promise&#039;&#039;&#039; pattern:&lt;br /&gt;
&lt;br /&gt;
* http://www.html5rocks.com/en/tutorials/es6/promises/&lt;br /&gt;
* https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise&lt;br /&gt;
&lt;br /&gt;
A simple example using the Promise API to display an ODB value on a web page:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mjsonrpc_db_get_values([&amp;quot;/runinfo/run number&amp;quot;]).then(function(rpc) {&lt;br /&gt;
   document.getElementById(&amp;quot;run_number&amp;quot;).innerHTML = rpc.result.data[0];&lt;br /&gt;
}).catch(function(error) {&lt;br /&gt;
   mjsonrpc_error_alert(error);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A partial list of available javascript functions. For the full list, and for full documentation, please go to the doxygen-generated documentation at&lt;br /&gt;
https://daq.triumf.ca/~daqweb/doc/midas-devel/html/mhttpd_8js.html&lt;br /&gt;
&lt;br /&gt;
* function 	mjsonrpc_set_url (url) - set the URL of the MIDAS JSON-RPC server, if different from web page URL. Cross-site access is fully supported (see CORS).&lt;br /&gt;
* function 	mjsonrpc_call (method, params, id) - call arbitrary RPC method&lt;br /&gt;
* function 	mjsonrpc_start_program (name, id, callback, error_callback)&lt;br /&gt;
* function 	mjsonrpc_stop_program (name, unique, id, callback, error_callback)&lt;br /&gt;
* function 	mjsonrpc_db_get_values (paths, id, callback, error_callback) - read ODB values&lt;br /&gt;
* function 	mjsonrpc_db_paste (paths, values, id, callback, error_callback) - write ODB values&lt;br /&gt;
&lt;br /&gt;
=== Batch Requests ===&lt;br /&gt;
&lt;br /&gt;
The MIDAS mjson_rpc supports making combining together multiple different requests into the same HTTP request.  This batch mode requesting is much more efficient and should be used whenever possible.  An example of a mjson_rpc batch request for the transition status and ODB values is shown here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var req = new Array;&lt;br /&gt;
req.push(mjsonrpc_make_request(&amp;quot;cm_transition_status&amp;quot;));&lt;br /&gt;
req.push(mjsonrpc_make_request(&amp;quot;db_get_values&amp;quot;, {&amp;quot;paths&amp;quot;: [&amp;quot;/runinfo&amp;quot;]}));&lt;br /&gt;
mjsonrpc_send_request(req).then(function (rpc) {&lt;br /&gt;
   USER CODE HERE&lt;br /&gt;
}).catch(function (error) {&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
Only a few examples are given below. For more examples:&lt;br /&gt;
* look elsewhere on this page&lt;br /&gt;
* look at the example experiment examples/javascript1/example.html&lt;br /&gt;
* look at the doxygen-generated documentation for mjsonrpc_xxx functions&lt;br /&gt;
* look at the implementation of the functions&lt;br /&gt;
* look at the implementation of mhttpd internal web pages (functions mhttpd_xxx in mhttpd.js)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ curl -H &amp;quot;Content-Type: application/json&amp;quot; --data &#039;{&amp;quot;jsonrpc&amp;quot;:&amp;quot;2.0&amp;quot;,&amp;quot;id&amp;quot;:null,&amp;quot;method&amp;quot;:&amp;quot;db_get_values&amp;quot;,&amp;quot;params&amp;quot;:{&amp;quot;paths&amp;quot;:[&amp;quot;/runinfo/run number&amp;quot;]}}&#039; &#039;http://localhost:8080?mjsonrpc&#039;&lt;br /&gt;
{&amp;quot;jsonrpc&amp;quot;: &amp;quot;2.0&amp;quot;,&amp;quot;result&amp;quot;:{&amp;quot;data&amp;quot;:[324],&amp;quot;status&amp;quot;:[1],&amp;quot;last_written&amp;quot;:[1443570804]},&amp;quot;id&amp;quot;:null}&lt;br /&gt;
$ &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ODB data ===&lt;br /&gt;
The following code illustrates how to use function &#039;&#039;&#039;mjsonrpc_db_get_values()&#039;&#039;&#039; (see [https://daq.triumf.ca/~daqweb/doc/midas-devel/htm/group__mjsonrpc__js.html#ga80cf049d190f57f6a3f6004791d7f0e9| rpc get_values]) to read data from the ODB. Some of the data are displayed using InnerHTML. The data are read every 10 seconds. The time changes and a counter increments each time the data are read. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;title&amp;gt;My Title&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;script src=&#039;mhttpd.js&#039;&amp;gt; &lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
var updatePeriod = 10000; // in msec&lt;br /&gt;
var updateTimerId = 0;&lt;br /&gt;
var counter = 0;&lt;br /&gt;
&lt;br /&gt;
function update()  {&lt;br /&gt;
   clearTimeout(updateTimerId);&lt;br /&gt;
   load();&lt;br /&gt;
   if (updatePeriod &amp;gt; 0)&lt;br /&gt;
   updateTimerId = setTimeout(&#039;update()&#039;, updatePeriod);&lt;br /&gt;
}&lt;br /&gt;
function load()   {&lt;br /&gt;
    counter++;&lt;br /&gt;
    document.getElementById(&#039;LastUpdated&#039;).innerHTML = &amp;quot;Updating...&amp;quot; + new Date;&lt;br /&gt;
    document.getElementById(&#039;counter&#039;).innerHTML = &#039;Counter: &#039;+ counter&lt;br /&gt;
&lt;br /&gt;
    mjsonrpc_db_get_values([&amp;quot;/Runinfo&amp;quot;,&amp;quot;/Experiment/name&amp;quot;]).then(function(rpc) {&lt;br /&gt;
       var runinfo= rpc.result.data[0]&lt;br /&gt;
       var name =  rpc.result.data[1]&lt;br /&gt;
       document.getElementById(&amp;quot;name&amp;quot;).innerHTML =&#039;Experiment name =&#039;+ name&lt;br /&gt;
       document.getElementById(&amp;quot;state&amp;quot;).innerHTML =&#039;Run State=&#039;+ runinfo.state&lt;br /&gt;
       document.getElementById(&amp;quot;rn&amp;quot;).innerHTML =&#039;Run number=&#039;+ runinfo[&amp;quot;run number&amp;quot;]&lt;br /&gt;
       document.getElementById(&amp;quot;status&amp;quot;).innerHTML = &#039;Status: &#039;+ rpc.result.status&lt;br /&gt;
    }).catch(function(error) {&lt;br /&gt;
       mjsonrpc_error_alert(error);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&amp;lt;h2&amp;gt; Javascript code example using mjson_db_get_values with Promises &amp;lt;/h2&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;LastUpdated&amp;quot;&amp;gt;Last updated: never&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;rn&amp;quot;&amp;gt;Run Number : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;state&amp;quot;&amp;gt;Run State : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;name&amp;quot;&amp;gt;Experiment name : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;status&amp;quot;&amp;gt;Status : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;counter&amp;quot;&amp;gt;Counter: zero&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
update()&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When run as a [[Custom Page]] using the web server [[mhttpd]], the above html code looks like this:&lt;br /&gt;
[[File: mjson_example1.jpg|center|frame|Figure 1: code to read from ODB run as a Custom Page]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;   &amp;lt;!-- clear wraparound after image --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The status value (rpc.result.status) is an array. &lt;br /&gt;
* rpc.result.status[0] is the status from reading &amp;quot;/Runinfo&amp;quot;, and &lt;br /&gt;
* rpc.result.status[1] is the status from reading &amp;quot;/Experiment/name&amp;quot;. &lt;br /&gt;
A value of 1 means success.&lt;br /&gt;
If the path does not exist, the status value will be 312, corresponding to the error DB_NO_KEY defined in midas.h.&lt;br /&gt;
&lt;br /&gt;
=== Write ODB data and read it back ===&lt;br /&gt;
The following example illustrates how to write ODB data using the function &#039;&#039;&#039;mjsonrpc_db_paste()&#039;&#039;&#039; (see [https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__mjsonrpc__js.html#ga2a1da2269d82fbb9edf410a9241997b8| rpc db_paste]). In this example, when the &amp;quot;Set&amp;quot; button is pressed, data are written to the ODB keys {{Odbpath|path=test}}(integer), {{Odbpath|path=pi}}(float) and {{Odbpath|path=my_string}}(string), all in the ODB subdirectory {{Odbpath|path=/equipment/rpcexample/settings/}}. When the &amp;quot;Read&amp;quot; button is pressed, the three ODB keys are read and the data written to the screen (Figure 2). For convenience, in function set(), the paths of the ODB keys to be written are stored in the array &amp;quot;paths&amp;quot;.  Unlike the example above, there is no automatic update.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;title&amp;gt;My Custom Page Title&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;script src=&#039;mhttpd.js&#039;&amp;gt; &lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
var counter = 0;&lt;br /&gt;
&lt;br /&gt;
function load()  {&lt;br /&gt;
      counter++;&lt;br /&gt;
      document.getElementById(&#039;LastUpdated&#039;).innerHTML = &amp;quot;Updating...&amp;quot; + new Date;&lt;br /&gt;
      mjsonrpc_db_get_values([&amp;quot;/equipment/rpcexample/settings/&amp;quot;]).then(function(rpc) {&lt;br /&gt;
         settings= rpc.result.data[0]&lt;br /&gt;
         document.getElementById(&amp;quot;test&amp;quot;).innerHTML =&#039;Integer test =&#039;+  settings.test&lt;br /&gt;
         document.getElementById(&amp;quot;pi&amp;quot;).innerHTML =&#039;Float pi =&#039;+  settings.pi&lt;br /&gt;
         document.getElementById(&amp;quot;string&amp;quot;).innerHTML =&#039;String my_string=&#039;+ settings[&amp;quot;my string&amp;quot;]&lt;br /&gt;
         document.getElementById(&amp;quot;status&amp;quot;).innerHTML = &#039;Read Status: &#039;+ rpc.result.status&lt;br /&gt;
      }).catch(function(error) {&lt;br /&gt;
         mjsonrpc_error_alert(error);&lt;br /&gt;
      });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function set()  {&lt;br /&gt;
      var paths=[&amp;quot;/equipment/rpcexample/settings/test&amp;quot;,&amp;quot;/equipment/rpcexample/settings/pi&amp;quot;,&amp;quot;/equipment/rpcexample/settings/my string&amp;quot;];&lt;br /&gt;
      mjsonrpc_db_paste(paths, [10,3.1416,&amp;quot;hallo world&amp;quot;]).then(function(rpc) {&lt;br /&gt;
	 result=rpc.result;	      &lt;br /&gt;
         document.getElementById(&amp;quot;wstatus&amp;quot;).innerHTML = &#039;Write status &#039;+rpc.result.status	      &lt;br /&gt;
      }).catch(function(error) {&lt;br /&gt;
          mjsonrpc_error_alert(error);&lt;br /&gt;
      });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&amp;lt;h2&amp;gt; Example code using mjsonrpc calls to write and read &amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;LastUpdated&amp;quot;&amp;gt;Last updated: never&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;test&amp;quot;&amp;gt; Test : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;pi&amp;quot;&amp;gt;Pi : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;string&amp;quot;&amp;gt;String : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;status&amp;quot;&amp;gt;Read Status : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;wstatus&amp;quot;&amp;gt;Write Status: zero&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt; &amp;lt;input type=button value=&#039;Set values&#039; onClick=&#039;set();&#039;&amp;gt; &amp;lt;/input&amp;gt;&lt;br /&gt;
       &amp;lt;input type=button value=&#039;Read values&#039; onClick=&#039;load();&#039;&amp;gt;&amp;lt;/input&amp;gt; &amp;gt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When run as a [[Custom Page]] using the web server [[mhttpd]], the output from the above html code is shown in Figure 2 below.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Figure 2a: Initial custom page !! Figure 2b: After pressing &amp;quot;Set&amp;quot; button !! Figure 2c: After pressing &amp;quot;Read&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
|[[File: mjson_example2a.jpg|frame]] || [[File: mjson_example2b.jpg|frame ]] || [[File: mjson_example2c.jpg|frame]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;   &amp;lt;!-- clear wraparound after image --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the data is written (Figure 2b) the write status is shown. &lt;br /&gt;
The status value (rpc.result.status) is an array with each element showing the status of the three write operations to the three keys, i.e. &lt;br /&gt;
* rpc.result.status[0] is the status from writing &amp;quot;test&amp;quot;, and &lt;br /&gt;
* rpc.result.status[1] is the status from writing &amp;quot;pi&amp;quot;. &lt;br /&gt;
* rpc.result.status[2] is the status from writing &amp;quot;my_string&amp;quot; &lt;br /&gt;
A value of 1 means success.&lt;br /&gt;
After the data is read (Figure 2c), the values read and the read status is shown. In this case, the read status only has one value (1=success) because one read operation of the whole subdirectory is performed.&lt;br /&gt;
&lt;br /&gt;
==== Error handling ====&lt;br /&gt;
If one of the paths to write does not exist, there will be an error reported in the status value. For example, if the path to write the variable &amp;quot;pi&amp;quot; does not exist, the write status will be&lt;br /&gt;
: Write status 1,312,1&lt;br /&gt;
The value 312 is that of the error DB_NO_KEY defined in midas.h.&lt;br /&gt;
&lt;br /&gt;
If the number of data values supplied to the function mjsonrpc_db_paste() does not equal the number of paths (i.e. lengths of paths and data arrays are not equal) an error popup will result (Figure 3). The error message also contains the response ID - see [https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__mjsonrpc__js.html#ga2a1da2269d82fbb9edf410a9241997b8| rpc db_paste] for more information.&lt;br /&gt;
[[File: mjson_slerr.jpg|center|frame|Figure 3: Error popup when lengths of paths array and data array are not equal]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;   &amp;lt;!-- clear wraparound after image --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Writing an array ===&lt;br /&gt;
The following example shows using mjsonrpc_db_paste() to write to an array in ODB. See above for [[#supported array index syntax]].&lt;br /&gt;
Because each array element is accessed individually, each array element value comes with a corresponding status value of odb db_get_data_index(), encoded as an array (i.e. rpc.result.status in example below). Note that the data to be written to the array elements must be enclosed by their own square brackets [] and the number of data values must match the number of array elements listed (except when the array name is used alone -  see [[#array name only]]). Too few data values will throw an error, too many will be ignored. &lt;br /&gt;
If the array in the ODB is not large enough for the number of elements and data to be written, it will be expanded automatically.&lt;br /&gt;
&lt;br /&gt;
==== Writing to a variable and an array ====&lt;br /&gt;
The following example shows using mjsonrpc_db_paste()  to&lt;br /&gt;
* write &amp;quot;9&amp;quot; to an integer variable called &amp;quot;test&amp;quot;, and to &lt;br /&gt;
* write values to a number of elements of the array &amp;quot;array&amp;quot;&lt;br /&gt;
Both &amp;quot;test&amp;quot; and &amp;quot;array&amp;quot; are keys in the ODB subdirectory {{Odbpath|path=/equipment/rpcexample/settings/}}.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/test&amp;quot;,&amp;quot;/equipment/rpcexample/settings/array[3-1,4,5,8-10]&amp;quot;], [9,[1,2,3,4,5,8,9,10]]).then(function(rpc) {&lt;br /&gt;
       document.getElementById(&amp;quot;wstatus&amp;quot;).innerHTML =&#039;Set Status=&#039;+ rpc.result.status&lt;br /&gt;
  }).catch(function(error) {&lt;br /&gt;
       mjsonrpc_error_alert(error);&lt;br /&gt;
    });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Assuming the array was filled with zeroes to start with, after writing to the array  and [[#reading an array|reading it back]], it would now contain the values&lt;br /&gt;
: Array= 0,3,2,1,4,5,0,0,8,9,10,11 &lt;br /&gt;
: Set Status = 1,1,1,1,1,1,1,1,1   (rpc.result.status)&lt;br /&gt;
There are 9 elements in the rpc.result.status array, where 1=success. The first element is the status after writing &amp;quot;test&amp;quot;, the next 8 the status after writing each array element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  Automatically expand the array ====&lt;br /&gt;
If the array in the ODB contains 12 elements, writing&lt;br /&gt;
 mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/test&amp;quot;,&amp;quot;/equipment/rpcexample/settings/array[3-1,4,5,8-10,14]&amp;quot;], [9,[1,2,3,4,5,8,9,10,14] ]).then(function(rpc) { ...&lt;br /&gt;
will automatically expand the array to 15 elements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Too few data values ====&lt;br /&gt;
Supplying too few data values, e.g. writing&lt;br /&gt;
 mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/array[4,5,8-10]&amp;quot;], [ [4,5,8,9] ]).then(function(rpc) { ...&lt;br /&gt;
results in an error (rpc.result.status= 315) i.e. DB_TYPE_MISMATCH because there aren&#039;t enough data words supplied (4) for the number of indices (5).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;array name only&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
==== Array name only supplied ====&lt;br /&gt;
If you supply just the array name, then you do not need to supply the same number of elements as the array length. For example,&lt;br /&gt;
 mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/array&amp;quot;],[ [7,6,0] ]).then(function(rpc){ ...&lt;br /&gt;
will write the first three elements of the array to 7,6,0. Other array elements will be untouched. In this case, there is only one status value for the whole array. &lt;br /&gt;
: Array= 7,6,0,1,4,5,0,0,8,9,10,11 &lt;br /&gt;
: Set Status = 1   (rpc.result.status)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Set all elements to one value ====&lt;br /&gt;
If you want to set all 19 elements of an array to 1, you can write&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/array[0-18]&amp;quot;], [1]).then(function(rpc) { ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
or to set a group of indices to 999, you can write&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/array[1-3,4,5,8-10]&amp;quot;], [999]).then(function(rpc) { ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the single value is written as [999] rather than as an array with single value, i.e. [ [999] ].&lt;br /&gt;
&lt;br /&gt;
=== Reading an array ===&lt;br /&gt;
In the same way, you can read all or part of an array.  See above for [[#Supported array index syntax]].&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var array = new Array();&lt;br /&gt;
mjsonrpc_db_get_values([&amp;quot;/Runinfo&amp;quot;,&amp;quot;/Experiment/name&amp;quot;,&amp;quot;/Equipment/rpcexample/Settings/array&amp;quot;,&amp;quot;/Equipment/rpcexample/Settings/array[3-6,11]&amp;quot;]).then(function(rpc) {&lt;br /&gt;
      document.getElementById(&amp;quot;status&amp;quot;).innerHTML = &#039;Status : &#039;+ result.status  &lt;br /&gt;
      var runinfo= rpc.result.data[0] &lt;br /&gt;
      var name =  rpc.result.data[1]&lt;br /&gt;
      array = rpc.result.data[2]   // whole array&lt;br /&gt;
      document.getElementById(&amp;quot;array&amp;quot;).innerHTML =&#039;Array=&#039;+ array&lt;br /&gt;
      array=rpc.result.data[3]    // array indices&lt;br /&gt;
      document.getElementById(&amp;quot;array_ele&amp;quot;).innerHTML =&#039;Array elements=&#039;+ array&lt;br /&gt;
}).catch(function(error) {&lt;br /&gt;
   mjsonrpc_error_alert(error);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This example gives &lt;br /&gt;
: Array=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0&lt;br /&gt;
: Array elements=4,5,6,7,12&lt;br /&gt;
: Status :  1,1,1,1,1,1,1,1  (rpc.result.status)&lt;br /&gt;
Note that the &amp;quot;Array elements&amp;quot; line has read the contents of indices 3-6 and 11 as requested (5 indices in total). The status array contains 8 values. The last 5 values are the status from reading the 5 array elements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Schema (List of all RPC methods) ==&lt;br /&gt;
&lt;br /&gt;
The MIDAS JSON RPC API is documented via an automatically generated JSON Schema.&lt;br /&gt;
&lt;br /&gt;
The JSON Schema (&amp;quot;describes your JSON data format&amp;quot;) is not a well established standard (there is no final RFC) but seems to be in common use. Multiple 3rd party tools exist&lt;br /&gt;
to visualize, explore and interface with JSON documents described by JSON Schemas. For more general information go here:&lt;br /&gt;
* http://json-schema.org/&lt;br /&gt;
* https://tools.ietf.org/html/draft-zyp-json-schema-04&lt;br /&gt;
&lt;br /&gt;
The MIDAS JSON RPC Schema is automatically generated by mhttpd and is &amp;lt;b&amp;gt;linked by the mhttpd &amp;quot;Help&amp;quot; page in JSON and in text format&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For reference, a recent copy of the JSON RPC API Schema is pasted here from the mhttpd &amp;quot;help&amp;quot; page. For final, complete and up-to-date schema, please get your own copy from the midas &amp;quot;help&amp;quot; page.&lt;br /&gt;
&lt;br /&gt;
In this schema, &amp;quot;?&amp;quot; means an optional parameter, &amp;quot;[]&amp;quot; means an array parameter. (There is an artefact: all method names have question marks because they are optional as far as the schema generator is concerned).&lt;br /&gt;
&lt;br /&gt;
=== MIDAS JSON RPC API Schema ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
Autogenerated schema for all MIDAS JSON-RPC methods&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
al_reset_alarm?             | reset alarms&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | alarms[]             | array of alarm names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of al_reset_alarm() for each alarm&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
al_trigger_alarm?           | trigger an alarm&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | name                 | string         | alarm name&lt;br /&gt;
                            |          | message              | string         | alarm message&lt;br /&gt;
                            |          | class                | string         | alarm class&lt;br /&gt;
                            |          | condition            | string         | alarm condition&lt;br /&gt;
                            |          | type                 | integer        | alarm type (AT_xxx)&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of al_trigger_alarm()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
al_trigger_class?           | trigger an alarm&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | class                | string         | alarm class&lt;br /&gt;
                            |          | message              | string         | alarm message&lt;br /&gt;
                            |          | first?               | bool           | see al_trigger_class() in midas.c&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of al_trigger_class()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
bm_receive_event?           | read event buffers&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | buffer_name          | string         | name of event buffer&lt;br /&gt;
                            |          | event_id?            | integer        | requested event id, -1 means any event id&lt;br /&gt;
                            |          | trigger_mask?        | integer        | requested trigger mask, -1 means any trigger mask&lt;br /&gt;
                            |          | get_recent?          | bool           | get last available event that matches this event request&lt;br /&gt;
                            |          | last_event_header[]? | do not resend an event we already received: event header of last received event [event_id,trigger_mask,serial_number,time_stamp]&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | timeout_millisec?    | number         | how long to wait for an event&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | binary data          | arraybuffer    | binary event data&lt;br /&gt;
                            |          | status               | integer        | return status of bm_open_buffer(), bm_request_event(), bm_set_cache_size(), bm_receive_alloc()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
brpc?                       | make RPC call into frontend program via RPC_BRPC&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | client_name          | string         | Connect to this MIDAS client, see cm_connect_client()&lt;br /&gt;
                            |          | cmd                  | string         | Command passed to client&lt;br /&gt;
                            |          | args                 | string         | Parameters passed to client as a string, could be JSON encoded&lt;br /&gt;
                            |          | max_reply_length?    | integer        | Optional maximum length of client reply. MIDAS RPC does not support returning data of arbitrary length, maximum length has to be known ahead of time.&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | reply                | string         | Reply from client as a string, could be JSON encoded&lt;br /&gt;
                            |          | status               | integer        | return status of cm_connect_client() and rpc_client_call()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_exist?                   | calls MIDAS cm_exist() to check if given MIDAS program is running&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | name                 | string         | name of the program, corresponding to ODB /Programs/name&lt;br /&gt;
                            |          | unique?              | bool           | bUnique argument to cm_exist()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_exist()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_msg1?                    | Generate a midas message using cm_msg1()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | facility?            | string         | message facility, default is &amp;quot;midas&amp;quot;&lt;br /&gt;
                            |          | user?                | string         | message user, default is &amp;quot;javascript_commands&amp;quot;&lt;br /&gt;
                            |          | type?                | integer        | message type, MT_xxx from midas.h, default is MT_INFO&lt;br /&gt;
                            |          | message              | string         | message text&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_msg1()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_msg_facilities?          | get message facilities using cm_msg_facilities()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_msg_facilities()&lt;br /&gt;
                            |          | facilities[]         | array of facility names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_msg_retrieve?            | Retrieve midas messages using cm_msg_retrieve2()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | facility?            | string         | message facility, default is &amp;quot;midas&amp;quot;&lt;br /&gt;
                            |          | min_messages?        | integer        | get at least this many messages, default is 1&lt;br /&gt;
                            |          | time?                | number         | start from given timestamp, value 0 means give me newest messages, default is 0&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | num_messages         | integer        | number of messages returned&lt;br /&gt;
                            |          | messages             | string         | messages separated by \n&lt;br /&gt;
                            |          | status               | integer        | return status of cm_msg_retrieve2()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_shutdown?                | calls MIDAS cm_shutdown() to stop given MIDAS program&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | name                 | string         | name of the program, corresponding to ODB /Programs/name&lt;br /&gt;
                            |          | unique?              | bool           | bUnique argument to cm_shutdown()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_shutdown()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_transition?              | start and stop runs&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | transition           | string         | requested transition: TR_START, TR_STOP, TR_PAUSE, TR_RESUME&lt;br /&gt;
                            |          | run_number?          | integer        | New run number, value 0 means /runinfo/run_number + 1, default is 0&lt;br /&gt;
                            |          | async_flag?          | integer        | Transition type. Default is multithreaded transition TR_MTHREAD&lt;br /&gt;
                            |          | debug_flag?          | integer        | See cm_transition(), value 1: trace to stdout, value 2: trace to midas.log&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_transition()&lt;br /&gt;
                            |          | error_string?        | string         | return error string from cm_transition()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_copy?                    | get complete ODB data in the &amp;quot;save&amp;quot; json encoding, suitable for reloading with odbedit command &amp;quot;load&amp;quot;&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB subtree paths&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | data[]               | keys and values of ODB data for each path&lt;br /&gt;
                            |          |                      | array of       | object     &lt;br /&gt;
                            |          | status[]             | return status of db_copy_json_save() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_create?                  | Create new ODB entries&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params[] | array of ODB paths to be created&lt;br /&gt;
                            |          | array of             | arguments to db_create_key() and db_set_num_values()&lt;br /&gt;
                            |          |                      | path           | string      | ODB path to be created&lt;br /&gt;
                            |          |                      | type           | integer     | MIDAS TID_xxx type&lt;br /&gt;
                            |          |                      | array_length?  | integer     | optional array length, default is 1&lt;br /&gt;
                            |          |                      | string_length? | integer     | for TID_STRING, optional string length, default is NAME_LENGTH&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_create_key(), db_set_num_values() and db_set_data() (for TID_STRING) for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_delete?                  | delete ODB keys&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths to delete&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_delete_key() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_get_values?              | get values of ODB data from given subtrees&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB subtree paths, see note on array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | omit_names?          | bool           | omit the /name entries&lt;br /&gt;
                            |          | omit_last_written?   | bool           | omit the /last_written entries and the last_written[] result&lt;br /&gt;
                            |          | omit_tid?            | bool           | omit the tid[] result&lt;br /&gt;
                            |          | omit_old_timestamp?  | number         | omit data older than given ODB timestamp&lt;br /&gt;
                            |          | preserve_case?       | bool           | preserve the capitalization of ODB key names (WARNING: ODB is not case sensitive); note that this will also have side effect of setting the omit_names option&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | data[]               | values of ODB data for each path, all key names are in lower case, all symlinks are followed&lt;br /&gt;
                            |          |                      | array of       | any        &lt;br /&gt;
                            |          | status[]             | return status of db_copy_json_values() or db_copy_json_index() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | tid[]?               | odb type id for each path, absent if omit_tid is true&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | last_written[]?      | last_written value of the ODB subtree for each path, absent if omit_last_written is true&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_key?                     | get ODB keys&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_key() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | key data for each path&lt;br /&gt;
                            |          |                      | array of       | object     &lt;br /&gt;
                            |          | keys[]               | key type TID_xxx&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | array length, 1 for normal entries&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | key name&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | keys[]               | data total size in bytes&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | array element size, string length for TID_STRING&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | access mode bitmap of MODE_xxx&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | number of hotlinks attached to this key&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | timestamp when data was last updated&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_link?                    | Create ODB symlinks&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | new_links[]          | array of new symlinks to be created&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | target_paths[]       | array of existing ODB paths for each link&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_create_link() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_ls?                      | get contents of given ODB subdirectory in the &amp;quot;ls&amp;quot; json encoding - similar to odbedit command &amp;quot;ls -l&amp;quot;&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB subtree paths&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | data[]               | keys and values of ODB data for each path&lt;br /&gt;
                            |          |                      | array of       | object     &lt;br /&gt;
                            |          | status[]             | return status of db_copy_json_ls() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_paste?                   | write data into ODB&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB subtree paths, see note on array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | values[]             | array of data values written to ODB via db_paste_json() for each path&lt;br /&gt;
                            |          |                      | array of       | any        &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | array of return status of db_paste_json() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_rename?                  | Change size of ODB arrays&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths to rename&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | new_names[]          | array of new names for each ODB path&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_rename_key() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_reorder?                 | Change order of ODB keys in a subdirectory&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of new symlinks to be created&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | indices[]            | array of existing ODB paths for each link&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_reorder_key() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_resize?                  | Change size of ODB arrays&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths to resize&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | new_lengths[]        | array of new lengths for each ODB path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_set_num_values() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_resize_string?           | Change size of ODB string arrays&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths to resize&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | new_lengths[]        | array of new lengths for each ODB path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | new_string_lengths[] | array of new string lengths for each ODB path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_resize_string() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_scl?                     | Show ODB clients&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | scl                  | json           | return value of db_scl()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_sor?                     | Show ODB open records starting from given ODB path&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | path?                | string         | ODB path&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | sor                  | json           | return value of db_sor()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
el_delete?                  | Delete elog message&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | tag                  | string         | tag of message to delete&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of el_delete&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
el_query?                   | Query elog messages&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | last_n_hours?        | integer        | return messages from the last N hours&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of el_retrieve&lt;br /&gt;
                            |          | msg[]                | message tag&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
el_retrieve?                | Get an elog message&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | tag                  | string         | elog message tag&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of el_retrieve&lt;br /&gt;
                            |          | msg.tag              | string         | message tag&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
exec_script?                | execute custom script defined in ODB /Script (scripts show in the menu) or /CustomScript (scripts from custom pages)&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | script?              | string         | Execute ODB /Script/xxx&lt;br /&gt;
                            |          | customscript?        | string         | Execute ODB /CustomScript/xxx&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_exec_script()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
ext_list_files?             | js_ext_list_files&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | subdir               | string         | List files in experiment_directory/userfiles/subdir&lt;br /&gt;
                            |          | fileext              | string         | Filename extension&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | path                 | string         | Search path&lt;br /&gt;
                            |          | subdirs[]            | list of subdirectories&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | files[]              | script filename&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | files[]              | script description&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
ext_read_file?              | js_ext_read_script&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | filename             | string         | File name, read from experiment_directory/userfiles/filename&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | content              | string         | ASCII file content&lt;br /&gt;
                            |          | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | error                | string         | error text&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
ext_save_file?              | js_ext_save_file&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | filename             | string         | File name, save in experiment_directory/userfiles/filename&lt;br /&gt;
                            |          | script               | string         | ASCII content&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | error                | string         | error text&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_alarms?                 | get alarm data&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | get_all?             | bool           | get all alarms, even in alarm system not active and alarms not triggered&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | alarm_system_active  | bool           | value of ODB &amp;quot;/Alarms/alarm system active&amp;quot;&lt;br /&gt;
                            |          | alarms               | object         | alarm data, keyed by alarm name&lt;br /&gt;
                            |          | alarms[]             | alarm is triggered&lt;br /&gt;
                            |          |                      | array of       | bool       &lt;br /&gt;
                            |          | alarms[]             | alarm is enabled&lt;br /&gt;
                            |          |                      | array of       | bool       &lt;br /&gt;
                            |          | alarms[]             | alarm class&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | alarm type AT_xxx&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | alarms[]             | display background color&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | display foreground color&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | alarm ODB message field&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | alarm ODB condition field&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | evaluated alarm condition (AT_EVALUATED alarms only)&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | next time the periodic alarm will fire (AT_PERIODIC alarms only)&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | time when alarm was triggered&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | final alarm text shown to user by mhttpd&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_debug?                  | get current value of mjsonrpc_debug&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of mjsonrpc_debug&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_http_trace?             | get current value of mhttpd http_trace&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of http_trace&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_schema?                 | Get the MIDAS JSON-RPC schema JSON object&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | object               | returns the MIDAS JSON-RPC schema JSON object&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_sleep?                  | get current value of mjsonrpc_sleep&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of mjsonrpc_sleep&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_time?                   | get current value of mjsonrpc_time&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of mjsonrpc_time&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_timezone?               | get current server timezone offset in seconds&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | offset in seconds&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_active_events?       | get list of active history events using hs_read_event_list()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of hs_read_event_list()&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_channels?            | get list of history channels in /Logger/History&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return success or failure status&lt;br /&gt;
                            |          | default_channel      | string         | name of the default logger history channel&lt;br /&gt;
                            |          | channels[]           | all logger history channel names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | active_channels[]    | active logger history channel names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_events?              | get list of history events that existed at give time using hs_get_events()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | time?                | number         | timestamp, value 0 means current time, default is 0&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of hs_get_events()&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_last_written?        | get list of history tags for given history events that existed at give time using hs_get_last_written()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | time?                | number         | timestamp, value 0 means current time, default is 0&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | last_written[]       | array of last-written times for each history event&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_tags?                | get list of history tags for given history events that existed at give time using hs_get_tags()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | time?                | number         | timestamp, value 0 means current time, default is 0&lt;br /&gt;
                            |          | events[]?            | array of history event names, default is get all events using hs_get_events()&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | events[]             | array of history event names for each history event&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | events[]             | array of status ohistory tags for each history event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | events[]             | array of history tags for each history event&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | events[]             | history tag name&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | events[]             | history tag midas data type&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | events[]             | history tag number of array elements, omitted if 1&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_image_retrieve?          | Get a list of history image files&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | image?               | string         | image name as defined under /History/Images/&amp;lt;image&amp;gt;&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | time[]               | array of time stamps in seconds&lt;br /&gt;
                            |          |                      | array of       | arraybuffer&lt;br /&gt;
                            |          | filename[]           | array of file names&lt;br /&gt;
                            |          |                      | array of       | arraybuffer&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_read?                    | get history data for given history events that existed at give time using hs_read_buffer()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | data[]               | array of history data&lt;br /&gt;
                            |          |                      | array of       | array      &lt;br /&gt;
                            |          | data[]               | status for each event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | number of data for each event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | time data&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | value data&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_read_arraybuffer?        | get history data for given history events that existed at give time using hs_read_buffer()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | binary data          | arraybuffer    | binary data, see documentation&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_read_binned?             | get history data for given history events that existed at give time using hs_read_buffer()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            |          | num_bins             | integer        | number of time bins&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | data[]               | array of history data&lt;br /&gt;
                            |          |                      | array of       | array      &lt;br /&gt;
                            |          | data[]               | status for each event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | number of data points for each event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | number of data points for each bin&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | mean for each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | rms for each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | minimum value for each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | maximum value for each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | first data point in each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | first data point in each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | last data point in each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | last data point in each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | time of last data entry&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | value of last data entry&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_read_binned_arraybuffer? | get history data for given history events that existed at give time using hs_read_buffer()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            |          | num_bins             | integer        | number of time bins&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | binary data          | arraybuffer    | binary data, see documentation&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_reopen?                  | reopen the history channel to make sure we see the latest list of events using hs_clear_cache()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of hs_get_events()&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
jrpc?                       | make RPC call into frontend program via RPC_JRPC&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | client_name          | string         | Connect to this MIDAS client, see cm_connect_client()&lt;br /&gt;
                            |          | cmd                  | string         | Command passed to client&lt;br /&gt;
                            |          | args                 | string         | Parameters passed to client as a string, could be JSON encoded&lt;br /&gt;
                            |          | max_reply_length?    | integer        | Optional maximum length of client reply. MIDAS RPC does not support returning strings of arbitrary length, maximum length has to be known ahead of time.&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | reply                | string         | Reply from client as a string, could be JSON encoded&lt;br /&gt;
                            |          | status               | integer        | return status of cm_connect_client() and rpc_client_call()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
make_subdir?                | js_make_subdir&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | subdir               | string         | Create folder experiment_directory/userfiles/subdir&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | path                 | string         | Search path&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
null?                       | RPC method always returns null&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | method parameters are ignored&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | null                 | always returns null&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
read_binary_file?           | js_read_binary_file&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | filename             | string         | File name, read from experiment_directory/userfiles/filename&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | binary data          | arraybuffer    | Binary file content&lt;br /&gt;
                            |          | status               | integer        | Return status of midas library calls&lt;br /&gt;
                            |          | error                | string         | Error text&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
set_debug?                  | set new value of mjsonrpc_debug&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | integer              | new value of mjsonrpc_debug&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | new value of mjsonrpc_debug&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
set_http_trace?             | set new value of mhttpd http_trace&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | integer              | new value of http_trace&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | new value of http_trace&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
set_sleep?                  | set new value of mjsonrpc_sleep&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | integer              | new value of mjsonrpc_sleep&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | new value of mjsonrpc_sleep&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
set_time?                   | set new value of mjsonrpc_time&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | integer              | new value of mjsonrpc_time&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | new value of mjsonrpc_time&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
ss_millitime?               | get current MIDAS time using ss_millitime()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of ss_millitime()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
start_program?              | start MIDAS program defined in ODB /Programs/name&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | name                 | string         | name of the program, corresponding to ODB /Programs/name&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of ss_system()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
user_example1?              | example of user defined RPC method that returns up to 3 results&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | arg                  | string         | example string argment&lt;br /&gt;
                            |          | optional_arg?        | integer        | optional example integer argument&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | string               | string         | returns the value of &amp;quot;arg&amp;quot; parameter&lt;br /&gt;
                            |          | integer              | integer        | returns the value of &amp;quot;optional_arg&amp;quot; parameter&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
user_example2?              | example of user defined RPC method that returns more than 3 results&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | arg                  | string         | example string argment&lt;br /&gt;
                            |          | optional_arg?        | integer        | optional example integer argument&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | string1              | string         | returns the value of &amp;quot;arg&amp;quot; parameter&lt;br /&gt;
                            |          | string2              | string         | returns &amp;quot;hello&amp;quot;&lt;br /&gt;
                            |          | string3              | string         | returns &amp;quot;world!&amp;quot;&lt;br /&gt;
                            |          | value1               | integer        | returns the value of &amp;quot;optional_arg&amp;quot; parameter&lt;br /&gt;
                            |          | value2               | number         | returns 3.14&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
user_example3?              | example of user defined RPC method that returns an error&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | arg                  | integer        | integer value, if zero, throws a JSON-RPC error&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | returns the value of &amp;quot;arg&amp;quot; parameter&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:Javascript library]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3569</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3569"/>
		<updated>2025-10-15T22:13:39Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Midas contains a [[Sequencer]] that can be used to automate data-taking (e.g. starting and stopping many runs with different settings). The original Seqeuncer uses the custom Midas Script Language (MSL), which has a limited syntax. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens when the user starts the sequence and prompts the user to input the parameter values to use.&lt;br /&gt;
&lt;br /&gt;
The user can track the state of the sequence via a webpage that shows:&lt;br /&gt;
* the current line in the script, highlighted in yellow&lt;br /&gt;
* the value of any parameters/variables, shown in a table&lt;br /&gt;
* the progress of any loops or waits, shown as progress bars&lt;br /&gt;
 &lt;br /&gt;
[[File:Pysequencer.png|800px]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= Command-line arguments =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
usage: sequencer.py [-c name] [-h host_name] [-e expt_name] [-D] [-O] [--verbose] [--log-to-midas] [--help]&lt;br /&gt;
&lt;br /&gt;
options:&lt;br /&gt;
  -c name         Name of additional sequencer. E.g. if &#039;Test&#039;, ODB location will be /PySequencerTest&lt;br /&gt;
  -h host_name&lt;br /&gt;
  -e expt_name&lt;br /&gt;
  -D              Become a daemon&lt;br /&gt;
  -O              Become a daemon but retain stdout&lt;br /&gt;
  --verbose&lt;br /&gt;
  --log-to-midas  Write to midas message log as well as stdout/stderr (only for logging.info() etc, not regular print() statements)&lt;br /&gt;
  --help          Show this help message and exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Commands =&lt;br /&gt;
&lt;br /&gt;
PySequencer is generally designed to be controlled via the web interface. However it can also be controlled via settings in the ODB. Most settings are in /PySequencer/Command, but a couple are also in /PySequencer/State&lt;br /&gt;
&lt;br /&gt;
* In &#039;&#039;&#039;/PySequencer/Command&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;Load new file&#039;&#039;&#039; - load the file specified in /PySequencer/State/Filename&lt;br /&gt;
** &#039;&#039;&#039;Start script&#039;&#039;&#039; - start the script that has been loaded&lt;br /&gt;
** &#039;&#039;&#039;Pause script&#039;&#039;&#039; - pause the script by preventing the python interpreter from executing the next line of code&lt;br /&gt;
** &#039;&#039;&#039;Resume script&#039;&#039;&#039; - resume the script after pausing it&lt;br /&gt;
** &#039;&#039;&#039;Stop immediately&#039;&#039;&#039; - stop the sequence() function as soon as possible&lt;br /&gt;
** &#039;&#039;&#039;Stop after run&#039;&#039;&#039; - stop the sequence() function when the next run ends&lt;br /&gt;
** &#039;&#039;&#039;Cancel stop after run&#039;&#039;&#039; - cancel the &amp;quot;Stop after run&amp;quot; request&lt;br /&gt;
** &#039;&#039;&#039;Debug script&#039;&#039;&#039; - start script in debug mode where we won&#039;t move to the next line until &amp;quot;Step over&amp;quot; is set (usually via button on webpage)&lt;br /&gt;
** &#039;&#039;&#039;Step over&#039;&#039;&#039; - move to next line when running in debug mode&lt;br /&gt;
* In &#039;&#039;&#039;/PySequencer/State&lt;br /&gt;
** &#039;&#039;&#039;Path&#039;&#039;&#039; - specify a subdirectory beneath &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer where your files are located&lt;br /&gt;
** &#039;&#039;&#039;Filename&#039;&#039;&#039; - filename to load (relative to &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer or &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer/&amp;lt;path&amp;gt;)&lt;br /&gt;
** &#039;&#039;&#039;Message&#039;&#039;&#039; - if &amp;quot;Message wait&amp;quot; is true, the sequence will not continue until &amp;quot;Message&amp;quot; has been reset to an empty string - see [[#Messages | the messages section]] for more details&lt;br /&gt;
&lt;br /&gt;
= User script content =&lt;br /&gt;
&lt;br /&gt;
All scripts must be located in the &#039;&#039;&#039;userfiles/sequencer&#039;&#039;&#039; subdirectory of your [[Exptab | experiment directory]]. PySequencer will only load files with a .py extension.&lt;br /&gt;
&lt;br /&gt;
PySequencer will look for three specific functions in your file:&lt;br /&gt;
* &#039;&#039;&#039;define_params(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
* &#039;&#039;&#039;sequence(seq)&#039;&#039;&#039; - required&lt;br /&gt;
* &#039;&#039;&#039;at_exit(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
These are described in more detail below. All three should accept one argument, which is a [[#seq_object_reference | midas.sequencer.SequenceClient]] object. This object is also available as a global variable within your script (e.g. if you want to define helper functions and don&#039;t want to pass the seq object to each of them).&lt;br /&gt;
&lt;br /&gt;
Your file may include any other modules, and you can define/call your own functions. If you edit the content of your file, you can reload it using the web interface or by setting the ODB parameter &amp;quot;/PySequencer/Command/Load new file&amp;quot; to true. When we reload a file, we also reload all modules that it uses (i.e. if you make changes to a custom module that contains helper functions, you can trigger loading the new definitions by reloading your main script).&lt;br /&gt;
&lt;br /&gt;
== define_params(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to create &amp;quot;parameters&amp;quot; that the user will be prompted for when they start the sequence. It is expected to be a set of &#039;&#039;&#039;seq.register_param()&#039;&#039;&#039; calls. For each parameter, you specify a name, a comment that will be shown in the prompt dialog, a default value, and optionally a list of acceptable values.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;default value&amp;quot; you provide determines the type of the variable in the ODB. If you specify a string it will be a TID_STRING, an integer will become TID_INT32 etc.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    # Will be stored as an integer&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a float&lt;br /&gt;
    seq.register_param(&amp;quot;voltage&amp;quot;, &amp;quot;PMT voltage&amp;quot;, 1342.7)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a string, and user will see a dropdown to choose from&lt;br /&gt;
    seq.register_param(&amp;quot;run type&amp;quot;, &amp;quot;Purpose of this run&amp;quot;, &amp;quot;normal&amp;quot;, [&amp;quot;normal&amp;quot;, &amp;quot;calibration&amp;quot;, &amp;quot;testing&amp;quot;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The main sequence() function can then retrieve the values of these parameters by calling &#039;&#039;&#039;seq.get_param(&amp;quot;voltage&amp;quot;)&#039;&#039;&#039; etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer params.png|400px]]&lt;br /&gt;
&lt;br /&gt;
== sequence(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function MUST be defined, and is the main script that will be executed. Full documentation of the functions available is in the [[#seq_object_reference | seq object reference]] section, but you may call/define your own functions as well, and also call functions from other modules.&lt;br /&gt;
&lt;br /&gt;
There are a few &amp;quot;best practices&amp;quot; to consider to maximise integration with the webpage:&lt;br /&gt;
* Use &#039;&#039;&#039;seq.wait_seconds()&#039;&#039;&#039; instead of &#039;&#039;&#039;time.sleep()&#039;&#039;&#039; so you get a progress bar on the webpage. &#039;&#039;&#039;seq.sleep()&#039;&#039;&#039; is an alias for seq.wait_seconds() if you prefer.&lt;br /&gt;
* In a normal &amp;quot;for&amp;quot; loop use &#039;&#039;&#039;seq.range()&#039;&#039;&#039; instead of &#039;&#039;&#039;range()&#039;&#039;&#039; so you get a progress bar on the webpage. We don&#039;t have a way to show progress through any other type of for/while loop&lt;br /&gt;
* Avoid any very long-lasting calls (e.g. blocking on a socket) as these may prevent us from being able to stop the sequence when the user calls the &amp;quot;Stop immediately&amp;quot; command. Better to poll in a loop until a condition is met.&lt;br /&gt;
&lt;br /&gt;
== at_exit(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to &amp;quot;tidy up&amp;quot; when the sequence stops (either due to the sequence() function exiting normally, the user requesting &amp;quot;Stop immediately&amp;quot; or &amp;quot;Stop after run&amp;quot;, or the sequence() function throwing an exception). For example, you may wish to move a motor back to a default location, or ramp down PMT voltages etc.&lt;br /&gt;
&lt;br /&gt;
Note that this is not bulletproof and should not be relied on for safety-critical items! Examples of situations where at_exit() will NOT be called include:&lt;br /&gt;
* sequencer.py being killed by a user (Ctrl-C etc) or the OS (out-of-memory killer, rebooting etc)&lt;br /&gt;
* the user&#039;s sequence() function corrupting the python interpreter so badly that it affects the main thread&lt;br /&gt;
* the user&#039;s sequence() function entering an uninterruptible state where &amp;quot;Stop immediately&amp;quot; can&#039;t actually stop anything (e.g. blocking wait on a socket)&lt;br /&gt;
* unidentified bugs in sequencer.py&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
There are two different types of messages in the sequencer:&lt;br /&gt;
* regular [[Message_System | Midas messages]] that get written to the message log&lt;br /&gt;
* sequencer-specific messages that show as a dialog box on the sequencer webpage&lt;br /&gt;
&lt;br /&gt;
You can emit regular midas messages using the &#039;&#039;&#039;seq.msg(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&#039;&#039;&#039; function. Error messages get highlighted in red on the [[Message_Page | message page]]. The &amp;quot;facility&amp;quot; routes the message to different log files / different buttons on the message page.&lt;br /&gt;
&lt;br /&gt;
You can emit sequencer-specific messages using the &#039;&#039;&#039;seq.sequencer_msg(text, wait=False)&#039;&#039;&#039; function. If &amp;quot;wait&amp;quot; is True, the sequence will not continue until the user has clicked the &amp;quot;Close&amp;quot; button on the webpage to acknowledge the message.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer message.png|600px]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
PySequencer is integrated with the python logging module. You may use the global &#039;&#039;&#039;logger&#039;&#039;&#039; variable to log your own info/debug messages. You can configure what happens to these logger messages using either command-line arguments or functions in the seq object.&lt;br /&gt;
&lt;br /&gt;
To print logger.debug() messages, you can pass the &#039;&#039;&#039;--verbose&#039;&#039;&#039; command-line flag and/or call &#039;&#039;&#039;seq.set_py_logger_debug(True)&#039;&#039;&#039; in your script. The default is to only print info() messages or higher.&lt;br /&gt;
&lt;br /&gt;
You can also copy messages to the regular midas message log (so you can call logger.info() instead of seq.msg(), and still have messages appear in the midas message log). The &amp;quot;facility&amp;quot; in this case is &amp;quot;pysequencer&amp;quot;. You can enable this functionality using the &#039;&#039;&#039;--log-to-midas&#039;&#039;&#039; command-line flag and/or by calling &#039;&#039;&#039;seq.set_py_logger_to_midas(True)&#039;&#039;&#039; in your script. This is most useful for experiments where terminal access is very limited, and they wish to debug/monitor everything via log files that are visible through webpages.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer log.png|800px]]&lt;br /&gt;
&lt;br /&gt;
== seq object reference ==&lt;br /&gt;
&lt;br /&gt;
Users have access to the &#039;&#039;&#039;seq&#039;&#039;&#039; object in their scripts, which is a &#039;&#039;&#039;midas.sequencer.SequenceClient&#039;&#039;&#039; object. This class inherits from the standard &#039;&#039;&#039;midas.client.MidasClient&#039;&#039;&#039; object, and adds extra sequencer-specific tools.&lt;br /&gt;
&lt;br /&gt;
Full documentation of these classes can be found in docstrings in the source code:&lt;br /&gt;
* MidasClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/client.py&lt;br /&gt;
* SequenceClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/sequencer.py&lt;br /&gt;
&lt;br /&gt;
A summary of the available commands is:&lt;br /&gt;
* Sequencer-specific:&lt;br /&gt;
** seq.&#039;&#039;&#039;register_param&#039;&#039;&#039;(name, comment, default_value, options=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;get_param&#039;&#039;&#039;(name)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;range&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_seconds&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_odb&#039;&#039;&#039;(path, op, target, between_upper_target=None, stable_for_n_secs=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_func&#039;&#039;&#039;(func, check_period_secs=0.1)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_clients_running&#039;&#039;&#039;(client_names, timeout_secs=10)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_events&#039;&#039;&#039;(target)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_run_description&#039;&#039;&#039;(desc)&lt;br /&gt;
** seq.&#039;&#039;&#039;disable_tracing&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;enable_tracing&#039;&#039;&#039;()&lt;br /&gt;
* Run control&lt;br /&gt;
** seq.&#039;&#039;&#039;start_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;pause_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;resume_run&#039;&#039;&#039;()&lt;br /&gt;
* ODB access&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get&#039;&#039;&#039;(path, recurse_dir=True, include_key_metadata=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_set&#039;&#039;&#039;(path, contents, .....) - many optional parameters!&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_exists&#039;&#039;&#039;(path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_delete&#039;&#039;&#039;(path, follow_links=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_link&#039;&#039;&#039;(link_path, destination_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get_link_destination&#039;&#039;&#039;(link_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_rename&#039;&#039;&#039;(current_path, new_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_last_update_time&#039;&#039;&#039;(path)&lt;br /&gt;
* Event buffers&lt;br /&gt;
** seq.&#039;&#039;&#039;open_event_buffer&#039;&#039;&#039;(buffer_name, buf_size=None, max_event_size=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;register_event_request&#039;&#039;&#039;(buffer_handle, event_id=-1, trigger_mask=-1, sampling_type=midas.GET_ALL)&lt;br /&gt;
** seq.&#039;&#039;&#039;receive_event&#039;&#039;&#039;(buffer_handle, async_flag=True, use_numpy=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;deregister_event_request&#039;&#039;&#039;(buffer_handle, request_id)&lt;br /&gt;
** seq.&#039;&#039;&#039;send_event&#039;&#039;&#039;(buffer_handle, event)&lt;br /&gt;
* Controlling other clients&lt;br /&gt;
** seq.&#039;&#039;&#039;start_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_all_required_program_names&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;start_all_required_program&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;client_exists&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;clients_exist&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;connect_to_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;disconnect_from_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;jrpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;brpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
* Alarms&lt;br /&gt;
** seq.&#039;&#039;&#039;get_triggered_alarms&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;reset_alarm&#039;&#039;&#039;(alarm_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;create_alarm_class&#039;&#039;&#039;(class_name, execute_command=&amp;quot;&amp;quot;, execute_interval_secs=0, stop_run=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;create_evaluated_alarm&#039;&#039;&#039;(alarm_name, odb_condition, message=None, alarm_class=&amp;quot;Alarm&amp;quot;, activate_immediately=True)&lt;br /&gt;
** seq.&#039;&#039;&#039;trigger_internal_alarm&#039;&#039;&#039;(alarm_name, message, default_alarm_class=&amp;quot;Alarm&amp;quot;)&lt;br /&gt;
* History&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_create_plot&#039;&#039;&#039;(group_name, panel_name, variables, labels=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_data&#039;&#039;&#039;(start_time, end_time, interval_secs, event_name, tag_name, index=None, timestamps_as_datetime=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_events&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_recent_data&#039;&#039;&#039;(num_hours, interval_secs, event_name, tag_name, index=None, timestamps_as_datetime=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_tags&#039;&#039;&#039;(event_name)&lt;br /&gt;
* Messages&lt;br /&gt;
** seq.&#039;&#039;&#039;msg&#039;&#039;&#039;(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;get_recent_messages&#039;&#039;&#039;(min_messages=1, before=None, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** seq.&#039;&#039;&#039;get_message_facilities&#039;&#039;&#039;()&lt;br /&gt;
* Logging&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger&#039;&#039;&#039;(debug, to_midas_msg_log)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger_debug&#039;&#039;&#039;(debug)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger_to_midas&#039;&#039;&#039;(to_midas_msg_log)&lt;br /&gt;
* Misc&lt;br /&gt;
** seq.&#039;&#039;&#039;get_midas_version&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_experiment_dir&#039;&#039;&#039;()&lt;br /&gt;
* Functions that are in MidasClient, but may NOT be used in a sequencer script&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_watch&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_jrpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_brpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_disconnect_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_transition_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;communicate&#039;&#039;&#039;()&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
&lt;br /&gt;
== Variables shown on webpage ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;variables&amp;quot; table on the webpage will display only local/global variables of the types: int, float, string, bool, numpy.number and numpy.bool. It will also display lists and numpy.ndarrays of those types. It does not display any other types (e.g. datetime.datetime, custom objects, dicts etc). We also don&#039;t show any variables where the name starts with a double underscore.&lt;br /&gt;
&lt;br /&gt;
== No callback functions ==&lt;br /&gt;
&lt;br /&gt;
Your script cannot use any of the callback-based functions present in MidasClient. This includes odb_watch(), register_jrpc_callback(), register_brpc_callback(), register_message_callback(), register_transition_callback(), register_disconnect_callback() and communicate(). This is because your script runs in a separate thread, but the main thread is the one responsible for talking to midas; any callbacks get executed in the context of the main thread, not your script&#039;s thread.&lt;br /&gt;
&lt;br /&gt;
If you want to check whether an ODB value has changed, you&#039;ll need to implement a loop and call odb_get() repeatedly. &lt;br /&gt;
&lt;br /&gt;
You MAY use register_event_request() if you want to look at data as part of your script, as you then call receive_event() to explicitly look at the events, rather than relying on a callback function.&lt;br /&gt;
&lt;br /&gt;
== No tracing in other modules ==&lt;br /&gt;
&lt;br /&gt;
It is expected that experiments will build a library/module of helper functions to automate certain tasks (e.g. &#039;&#039;&#039;move_calibration_source(x, y)&#039;&#039;&#039;). Your sequencer script can import such a module and call the functions, but the webpage will not show what is happening within that module - it would just show that we&#039;re in move_calibration_source().&lt;br /&gt;
&lt;br /&gt;
It is technically possible to enhance the PySequencer logic to trace within these modules, but it is not yet clear whether users want such a feature, and what the best user interface would be.&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3558</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3558"/>
		<updated>2025-09-05T19:11:30Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Midas contains a [[Sequencer]] that can be used to automate data-taking (e.g. starting and stopping many runs with different settings). The original Seqeuncer uses the custom Midas Script Language (MSL), which has a limited syntax. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens when the user starts the sequence and prompts the user to input the parameter values to use.&lt;br /&gt;
&lt;br /&gt;
The user can track the state of the sequence via a webpage that shows:&lt;br /&gt;
* the current line in the script, highlighted in yellow&lt;br /&gt;
* the value of any parameters/variables, shown in a table&lt;br /&gt;
* the progress of any loops or waits, shown as progress bars&lt;br /&gt;
 &lt;br /&gt;
[[File:Pysequencer.png|800px]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= Command-line arguments =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
usage: sequencer.py [-c name] [-h host_name] [-e expt_name] [-D] [-O] [--verbose] [--log-to-midas] [--help]&lt;br /&gt;
&lt;br /&gt;
options:&lt;br /&gt;
  -c name         Name of additional sequencer. E.g. if &#039;Test&#039;, ODB location will be /PySequencerTest&lt;br /&gt;
  -h host_name&lt;br /&gt;
  -e expt_name&lt;br /&gt;
  -D              Become a daemon&lt;br /&gt;
  -O              Become a daemon but retain stdout&lt;br /&gt;
  --verbose&lt;br /&gt;
  --log-to-midas  Write to midas message log as well as stdout/stderr (only for logging.info() etc, not regular print() statements)&lt;br /&gt;
  --help          Show this help message and exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Commands =&lt;br /&gt;
&lt;br /&gt;
PySequencer is generally designed to be controlled via the web interface. However it can also be controlled via settings in the ODB. Most settings are in /PySequencer/Command, but a couple are also in /PySequencer/State&lt;br /&gt;
&lt;br /&gt;
* In &#039;&#039;&#039;/PySequencer/Command&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;Load new file&#039;&#039;&#039; - load the file specified in /PySequencer/State/Filename&lt;br /&gt;
** &#039;&#039;&#039;Start script&#039;&#039;&#039; - start the script that has been loaded&lt;br /&gt;
** &#039;&#039;&#039;Pause script&#039;&#039;&#039; - pause the script by preventing the python interpreter from executing the next line of code&lt;br /&gt;
** &#039;&#039;&#039;Resume script&#039;&#039;&#039; - resume the script after pausing it&lt;br /&gt;
** &#039;&#039;&#039;Stop immediately&#039;&#039;&#039; - stop the sequence() function as soon as possible&lt;br /&gt;
** &#039;&#039;&#039;Stop after run&#039;&#039;&#039; - stop the sequence() function when the next run ends&lt;br /&gt;
** &#039;&#039;&#039;Cancel stop after run&#039;&#039;&#039; - cancel the &amp;quot;Stop after run&amp;quot; request&lt;br /&gt;
** &#039;&#039;&#039;Debug script&#039;&#039;&#039; - start script in debug mode where we won&#039;t move to the next line until &amp;quot;Step over&amp;quot; is set (usually via button on webpage)&lt;br /&gt;
** &#039;&#039;&#039;Step over&#039;&#039;&#039; - move to next line when running in debug mode&lt;br /&gt;
* In &#039;&#039;&#039;/PySequencer/State&lt;br /&gt;
** &#039;&#039;&#039;Path&#039;&#039;&#039; - specify a subdirectory beneath &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer where your files are located&lt;br /&gt;
** &#039;&#039;&#039;Filename&#039;&#039;&#039; - filename to load (relative to &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer or &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer/&amp;lt;path&amp;gt;)&lt;br /&gt;
** &#039;&#039;&#039;Message&#039;&#039;&#039; - if &amp;quot;Message wait&amp;quot; is true, the sequence will not continue until &amp;quot;Message&amp;quot; has been reset to an empty string - see [[#Messages | the messages section]] for more details&lt;br /&gt;
&lt;br /&gt;
= User script content =&lt;br /&gt;
&lt;br /&gt;
All scripts must be located in the &#039;&#039;&#039;userfiles/sequencer&#039;&#039;&#039; subdirectory of your [[Exptab | experiment directory]]. PySequencer will only load files with a .py extension.&lt;br /&gt;
&lt;br /&gt;
PySequencer will look for three specific functions in your file:&lt;br /&gt;
* &#039;&#039;&#039;define_params(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
* &#039;&#039;&#039;sequence(seq)&#039;&#039;&#039; - required&lt;br /&gt;
* &#039;&#039;&#039;at_exit(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
These are described in more detail below. All three should accept one argument, which is a [[#seq_object_reference | midas.sequencer.SequenceClient]] object. This object is also available as a global variable within your script (e.g. if you want to define helper functions and don&#039;t want to pass the seq object to each of them).&lt;br /&gt;
&lt;br /&gt;
Your file may include any other modules, and you can define/call your own functions. If you edit the content of your file, you can reload it using the web interface or by setting the ODB parameter &amp;quot;/PySequencer/Command/Load new file&amp;quot; to true. When we reload a file, we also reload all modules that it uses (i.e. if you make changes to a custom module that contains helper functions, you can trigger loading the new definitions by reloading your main script).&lt;br /&gt;
&lt;br /&gt;
== define_params(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to create &amp;quot;parameters&amp;quot; that the user will be prompted for when they start the sequence. It is expected to be a set of &#039;&#039;&#039;seq.register_param()&#039;&#039;&#039; calls. For each parameter, you specify a name, a comment that will be shown in the prompt dialog, a default value, and optionally a list of acceptable values.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;default value&amp;quot; you provide determines the type of the variable in the ODB. If you specify a string it will be a TID_STRING, an integer will become TID_INT32 etc.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    # Will be stored as an integer&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a float&lt;br /&gt;
    seq.register_param(&amp;quot;voltage&amp;quot;, &amp;quot;PMT voltage&amp;quot;, 1342.7)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a string, and user will see a dropdown to choose from&lt;br /&gt;
    seq.register_param(&amp;quot;run type&amp;quot;, &amp;quot;Purpose of this run&amp;quot;, &amp;quot;normal&amp;quot;, [&amp;quot;normal&amp;quot;, &amp;quot;calibration&amp;quot;, &amp;quot;testing&amp;quot;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The main sequence() function can then retrieve the values of these parameters by calling &#039;&#039;&#039;seq.get_param(&amp;quot;voltage&amp;quot;)&#039;&#039;&#039; etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer params.png|400px]]&lt;br /&gt;
&lt;br /&gt;
== sequence(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function MUST be defined, and is the main script that will be executed. Full documentation of the functions available is in the [[#seq_object_reference | seq object reference]] section, but you may call/define your own functions as well, and also call functions from other modules.&lt;br /&gt;
&lt;br /&gt;
There are a few &amp;quot;best practices&amp;quot; to consider to maximise integration with the webpage:&lt;br /&gt;
* Use &#039;&#039;&#039;seq.wait_seconds()&#039;&#039;&#039; instead of &#039;&#039;&#039;time.sleep()&#039;&#039;&#039; so you get a progress bar on the webpage. &#039;&#039;&#039;seq.sleep()&#039;&#039;&#039; is an alias for seq.wait_seconds() if you prefer.&lt;br /&gt;
* In a normal &amp;quot;for&amp;quot; loop use &#039;&#039;&#039;seq.range()&#039;&#039;&#039; instead of &#039;&#039;&#039;range()&#039;&#039;&#039; so you get a progress bar on the webpage. We don&#039;t have a way to show progress through any other type of for/while loop&lt;br /&gt;
* Avoid any very long-lasting calls (e.g. blocking on a socket) as these may prevent us from being able to stop the sequence when the user calls the &amp;quot;Stop immediately&amp;quot; command. Better to poll in a loop until a condition is met.&lt;br /&gt;
&lt;br /&gt;
== at_exit(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to &amp;quot;tidy up&amp;quot; when the sequence stops (either due to the sequence() function exiting normally, the user requesting &amp;quot;Stop immediately&amp;quot; or &amp;quot;Stop after run&amp;quot;, or the sequence() function throwing an exception). For example, you may wish to move a motor back to a default location, or ramp down PMT voltages etc.&lt;br /&gt;
&lt;br /&gt;
Note that this is not bulletproof and should not be relied on for safety-critical items! Examples of situations where at_exit() will NOT be called include:&lt;br /&gt;
* sequencer.py being killed by a user (Ctrl-C etc) or the OS (out-of-memory killer, rebooting etc)&lt;br /&gt;
* the user&#039;s sequence() function corrupting the python interpreter so badly that it affects the main thread&lt;br /&gt;
* the user&#039;s sequence() function entering an uninterruptible state where &amp;quot;Stop immediately&amp;quot; can&#039;t actually stop anything (e.g. blocking wait on a socket)&lt;br /&gt;
* unidentified bugs in sequencer.py&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
There are two different types of messages in the sequencer:&lt;br /&gt;
* regular [[Message_System | Midas messages]] that get written to the message log&lt;br /&gt;
* sequencer-specific messages that show as a dialog box on the sequencer webpage&lt;br /&gt;
&lt;br /&gt;
You can emit regular midas messages using the &#039;&#039;&#039;seq.msg(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&#039;&#039;&#039; function. Error messages get highlighted in red on the [[Message_Page | message page]]. The &amp;quot;facility&amp;quot; routes the message to different log files / different buttons on the message page.&lt;br /&gt;
&lt;br /&gt;
You can emit sequencer-specific messages using the &#039;&#039;&#039;seq.sequencer_msg(text, wait=False)&#039;&#039;&#039; function. If &amp;quot;wait&amp;quot; is True, the sequence will not continue until the user has clicked the &amp;quot;Close&amp;quot; button on the webpage to acknowledge the message.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer message.png|600px]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
PySequencer is integrated with the python logging module. You may use the global &#039;&#039;&#039;logger&#039;&#039;&#039; variable to log your own info/debug messages. You can configure what happens to these logger messages using either command-line arguments or functions in the seq object.&lt;br /&gt;
&lt;br /&gt;
To print logger.debug() messages, you can pass the &#039;&#039;&#039;--verbose&#039;&#039;&#039; command-line flag and/or call &#039;&#039;&#039;seq.set_py_logger_debug(True)&#039;&#039;&#039; in your script. The default is to only print info() messages or higher.&lt;br /&gt;
&lt;br /&gt;
You can also copy messages to the regular midas message log (so you can call logger.info() instead of seq.msg(), and still have messages appear in the midas message log). The &amp;quot;facility&amp;quot; in this case is &amp;quot;pysequencer&amp;quot;. You can enable this functionality using the &#039;&#039;&#039;--log-to-midas&#039;&#039;&#039; command-line flag and/or by calling &#039;&#039;&#039;seq.set_py_logger_to_midas(True)&#039;&#039;&#039; in your script. This is most useful for experiments where terminal access is very limited, and they wish to debug/monitor everything via log files that are visible through webpages.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer log.png|800px]]&lt;br /&gt;
&lt;br /&gt;
== seq object reference ==&lt;br /&gt;
&lt;br /&gt;
Users have access to the &#039;&#039;&#039;seq&#039;&#039;&#039; object in their scripts, which is a &#039;&#039;&#039;midas.sequencer.SequenceClient&#039;&#039;&#039; object. This class inherits from the standard &#039;&#039;&#039;midas.client.MidasClient&#039;&#039;&#039; object, and adds extra sequencer-specific tools.&lt;br /&gt;
&lt;br /&gt;
Full documentation of these classes can be found in docstrings in the source code:&lt;br /&gt;
* MidasClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/client.py&lt;br /&gt;
* SequenceClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/sequencer.py&lt;br /&gt;
&lt;br /&gt;
A summary of the available commands is:&lt;br /&gt;
* Sequencer-specific:&lt;br /&gt;
** seq.&#039;&#039;&#039;register_param&#039;&#039;&#039;(name, comment, default_value, options=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;get_param&#039;&#039;&#039;(name)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;range&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_seconds&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_odb&#039;&#039;&#039;(path, op, target, between_upper_target=None, stable_for_n_secs=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_func&#039;&#039;&#039;(func, check_period_secs=0.1)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_clients_running&#039;&#039;&#039;(client_names, timeout_secs=10)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_events&#039;&#039;&#039;(target)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_run_description&#039;&#039;&#039;(desc)&lt;br /&gt;
* Run control&lt;br /&gt;
** seq.&#039;&#039;&#039;start_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;pause_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;resume_run&#039;&#039;&#039;()&lt;br /&gt;
* ODB access&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get&#039;&#039;&#039;(path, recurse_dir=True, include_key_metadata=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_set&#039;&#039;&#039;(path, contents, .....) - many optional parameters!&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_exists&#039;&#039;&#039;(path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_delete&#039;&#039;&#039;(path, follow_links=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_link&#039;&#039;&#039;(link_path, destination_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get_link_destination&#039;&#039;&#039;(link_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_rename&#039;&#039;&#039;(current_path, new_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_last_update_time&#039;&#039;&#039;(path)&lt;br /&gt;
* Event buffers&lt;br /&gt;
** seq.&#039;&#039;&#039;open_event_buffer&#039;&#039;&#039;(buffer_name, buf_size=None, max_event_size=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;register_event_request&#039;&#039;&#039;(buffer_handle, event_id=-1, trigger_mask=-1, sampling_type=midas.GET_ALL)&lt;br /&gt;
** seq.&#039;&#039;&#039;receive_event&#039;&#039;&#039;(buffer_handle, async_flag=True, use_numpy=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;deregister_event_request&#039;&#039;&#039;(buffer_handle, request_id)&lt;br /&gt;
** seq.&#039;&#039;&#039;send_event&#039;&#039;&#039;(buffer_handle, event)&lt;br /&gt;
* Controlling other clients&lt;br /&gt;
** seq.&#039;&#039;&#039;start_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_all_required_program_names&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;start_all_required_program&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;client_exists&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;clients_exist&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;connect_to_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;disconnect_from_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;jrpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;brpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
* Alarms&lt;br /&gt;
** seq.&#039;&#039;&#039;get_triggered_alarms&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;reset_alarm&#039;&#039;&#039;(alarm_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;create_alarm_class&#039;&#039;&#039;(class_name, execute_command=&amp;quot;&amp;quot;, execute_interval_secs=0, stop_run=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;create_evaluated_alarm&#039;&#039;&#039;(alarm_name, odb_condition, message=None, alarm_class=&amp;quot;Alarm&amp;quot;, activate_immediately=True)&lt;br /&gt;
** seq.&#039;&#039;&#039;trigger_internal_alarm&#039;&#039;&#039;(alarm_name, message, default_alarm_class=&amp;quot;Alarm&amp;quot;)&lt;br /&gt;
* History&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_create_plot&#039;&#039;&#039;(group_name, panel_name, variables, labels=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_data&#039;&#039;&#039;(start_time, end_time, interval_secs, event_name, tag_name, index=None, timestamps_as_datetime=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_events&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_recent_data&#039;&#039;&#039;(num_hours, interval_secs, event_name, tag_name, index=None, timestamps_as_datetime=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;hist_get_tags&#039;&#039;&#039;(event_name)&lt;br /&gt;
* Messages&lt;br /&gt;
** seq.&#039;&#039;&#039;msg&#039;&#039;&#039;(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;get_recent_messages&#039;&#039;&#039;(min_messages=1, before=None, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** seq.&#039;&#039;&#039;get_message_facilities&#039;&#039;&#039;()&lt;br /&gt;
* Logging&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger&#039;&#039;&#039;(debug, to_midas_msg_log)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger_debug&#039;&#039;&#039;(debug)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger_to_midas&#039;&#039;&#039;(to_midas_msg_log)&lt;br /&gt;
* Misc&lt;br /&gt;
** seq.&#039;&#039;&#039;get_midas_version&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_experiment_dir&#039;&#039;&#039;()&lt;br /&gt;
* Functions that are in MidasClient, but may NOT be used in a sequencer script&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_watch&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_jrpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_brpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_disconnect_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_transition_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;communicate&#039;&#039;&#039;()&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
&lt;br /&gt;
== Variables shown on webpage ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;variables&amp;quot; table on the webpage will display only local/global variables of the types: int, float, string, bool, numpy.number and numpy.bool. It will also display lists and numpy.ndarrays of those types. It does not display any other types (e.g. datetime.datetime, custom objects, dicts etc). We also don&#039;t show any variables where the name starts with a double underscore.&lt;br /&gt;
&lt;br /&gt;
== No callback functions ==&lt;br /&gt;
&lt;br /&gt;
Your script cannot use any of the callback-based functions present in MidasClient. This includes odb_watch(), register_jrpc_callback(), register_brpc_callback(), register_message_callback(), register_transition_callback(), register_disconnect_callback() and communicate(). This is because your script runs in a separate thread, but the main thread is the one responsible for talking to midas; any callbacks get executed in the context of the main thread, not your script&#039;s thread.&lt;br /&gt;
&lt;br /&gt;
If you want to check whether an ODB value has changed, you&#039;ll need to implement a loop and call odb_get() repeatedly. &lt;br /&gt;
&lt;br /&gt;
You MAY use register_event_request() if you want to look at data as part of your script, as you then call receive_event() to explicitly look at the events, rather than relying on a callback function.&lt;br /&gt;
&lt;br /&gt;
== No tracing in other modules ==&lt;br /&gt;
&lt;br /&gt;
It is expected that experiments will build a library/module of helper functions to automate certain tasks (e.g. &#039;&#039;&#039;move_calibration_source(x, y)&#039;&#039;&#039;). Your sequencer script can import such a module and call the functions, but the webpage will not show what is happening within that module - it would just show that we&#039;re in move_calibration_source().&lt;br /&gt;
&lt;br /&gt;
It is technically possible to enhance the PySequencer logic to trace within these modules, but it is not yet clear whether users want such a feature, and what the best user interface would be.&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3557</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3557"/>
		<updated>2025-09-05T18:49:13Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Midas contains a [[Sequencer]] that can be used to automate data-taking (e.g. starting and stopping many runs with different settings). The original Seqeuncer uses the custom Midas Script Language (MSL), which has a limited syntax. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens when the user starts the sequence and prompts the user to input the parameter values to use.&lt;br /&gt;
&lt;br /&gt;
The user can track the state of the sequence via a webpage that shows:&lt;br /&gt;
* the current line in the script, highlighted in yellow&lt;br /&gt;
* the value of any parameters/variables, shown in a table&lt;br /&gt;
* the progress of any loops or waits, shown as progress bars&lt;br /&gt;
 &lt;br /&gt;
[[File:Pysequencer.png|800px]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= Command-line arguments =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
usage: sequencer.py [-c name] [-h host_name] [-e expt_name] [-D] [-O] [--verbose] [--log-to-midas] [--help]&lt;br /&gt;
&lt;br /&gt;
options:&lt;br /&gt;
  -c name         Name of additional sequencer. E.g. if &#039;Test&#039;, ODB location will be /PySequencerTest&lt;br /&gt;
  -h host_name&lt;br /&gt;
  -e expt_name&lt;br /&gt;
  -D              Become a daemon&lt;br /&gt;
  -O              Become a daemon but retain stdout&lt;br /&gt;
  --verbose&lt;br /&gt;
  --log-to-midas  Write to midas message log as well as stdout/stderr (only for logging.info() etc, not regular print() statements)&lt;br /&gt;
  --help          Show this help message and exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Commands =&lt;br /&gt;
&lt;br /&gt;
PySequencer is generally designed to be controlled via the web interface. However it can also be controlled via settings in the ODB. Most settings are in /PySequencer/Command, but a couple are also in /PySequencer/State&lt;br /&gt;
&lt;br /&gt;
* In &#039;&#039;&#039;/PySequencer/Command&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;Load new file&#039;&#039;&#039; - load the file specified in /PySequencer/State/Filename&lt;br /&gt;
** &#039;&#039;&#039;Start script&#039;&#039;&#039; - start the script that has been loaded&lt;br /&gt;
** &#039;&#039;&#039;Pause script&#039;&#039;&#039; - pause the script by preventing the python interpreter from executing the next line of code&lt;br /&gt;
** &#039;&#039;&#039;Resume script&#039;&#039;&#039; - resume the script after pausing it&lt;br /&gt;
** &#039;&#039;&#039;Stop immediately&#039;&#039;&#039; - stop the sequence() function as soon as possible&lt;br /&gt;
** &#039;&#039;&#039;Stop after run&#039;&#039;&#039; - stop the sequence() function when the next run ends&lt;br /&gt;
** &#039;&#039;&#039;Cancel stop after run&#039;&#039;&#039; - cancel the &amp;quot;Stop after run&amp;quot; request&lt;br /&gt;
** &#039;&#039;&#039;Debug script&#039;&#039;&#039; - start script in debug mode where we won&#039;t move to the next line until &amp;quot;Step over&amp;quot; is set (usually via button on webpage)&lt;br /&gt;
** &#039;&#039;&#039;Step over&#039;&#039;&#039; - move to next line when running in debug mode&lt;br /&gt;
* In &#039;&#039;&#039;/PySequencer/State&lt;br /&gt;
** &#039;&#039;&#039;Path&#039;&#039;&#039; - specify a subdirectory beneath &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer where your files are located&lt;br /&gt;
** &#039;&#039;&#039;Filename&#039;&#039;&#039; - filename to load (relative to &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer or &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer/&amp;lt;path&amp;gt;)&lt;br /&gt;
** &#039;&#039;&#039;Message&#039;&#039;&#039; - if &amp;quot;Message wait&amp;quot; is true, the sequence will not continue until &amp;quot;Message&amp;quot; has been reset to an empty string - see [[#Messages | the messages section]] for more details&lt;br /&gt;
&lt;br /&gt;
= User script content =&lt;br /&gt;
&lt;br /&gt;
All scripts must be located in the &#039;&#039;&#039;userfiles/sequencer&#039;&#039;&#039; subdirectory of your [[Exptab | experiment directory]]. PySequencer will only load files with a .py extension.&lt;br /&gt;
&lt;br /&gt;
PySequencer will look for three specific functions in your file:&lt;br /&gt;
* &#039;&#039;&#039;define_params(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
* &#039;&#039;&#039;sequence(seq)&#039;&#039;&#039; - required&lt;br /&gt;
* &#039;&#039;&#039;at_exit(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
These are described in more detail below. All three should accept one argument, which is a [[#seq_object_reference | midas.sequencer.SequenceClient]] object. This object is also available as a global variable within your script (e.g. if you want to define helper functions and don&#039;t want to pass the seq object to each of them).&lt;br /&gt;
&lt;br /&gt;
Your file may include any other modules, and you can define/call your own functions. If you edit the content of your file, you can reload it using the web interface or by setting the ODB parameter &amp;quot;/PySequencer/Command/Load new file&amp;quot; to true. When we reload a file, we also reload all modules that it uses (i.e. if you make changes to a custom module that contains helper functions, you can trigger loading the new definitions by reloading your main script).&lt;br /&gt;
&lt;br /&gt;
== define_params(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to create &amp;quot;parameters&amp;quot; that the user will be prompted for when they start the sequence. It is expected to be a set of &#039;&#039;&#039;seq.register_param()&#039;&#039;&#039; calls. For each parameter, you specify a name, a comment that will be shown in the prompt dialog, a default value, and optionally a list of acceptable values.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;default value&amp;quot; you provide determines the type of the variable in the ODB. If you specify a string it will be a TID_STRING, an integer will become TID_INT32 etc.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    # Will be stored as an integer&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a float&lt;br /&gt;
    seq.register_param(&amp;quot;voltage&amp;quot;, &amp;quot;PMT voltage&amp;quot;, 1342.7)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a string, and user will see a dropdown to choose from&lt;br /&gt;
    seq.register_param(&amp;quot;run type&amp;quot;, &amp;quot;Purpose of this run&amp;quot;, &amp;quot;normal&amp;quot;, [&amp;quot;normal&amp;quot;, &amp;quot;calibration&amp;quot;, &amp;quot;testing&amp;quot;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The main sequence() function can then retrieve the values of these parameters by calling &#039;&#039;&#039;seq.get_param(&amp;quot;voltage&amp;quot;)&#039;&#039;&#039; etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer params.png|400px]]&lt;br /&gt;
&lt;br /&gt;
== sequence(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function MUST be defined, and is the main script that will be executed. Full documentation of the functions available is in the [[#seq_object_reference | seq object reference]] section, but you may call/define your own functions as well, and also call functions from other modules.&lt;br /&gt;
&lt;br /&gt;
There are a few &amp;quot;best practices&amp;quot; to consider to maximise integration with the webpage:&lt;br /&gt;
* Use &#039;&#039;&#039;seq.wait_seconds()&#039;&#039;&#039; instead of &#039;&#039;&#039;time.sleep()&#039;&#039;&#039; so you get a progress bar on the webpage. &#039;&#039;&#039;seq.sleep()&#039;&#039;&#039; is an alias for seq.wait_seconds() if you prefer.&lt;br /&gt;
* In a normal &amp;quot;for&amp;quot; loop use &#039;&#039;&#039;seq.range()&#039;&#039;&#039; instead of &#039;&#039;&#039;range()&#039;&#039;&#039; so you get a progress bar on the webpage. We don&#039;t have a way to show progress through any other type of for/while loop&lt;br /&gt;
* Avoid any very long-lasting calls (e.g. blocking on a socket) as these may prevent us from being able to stop the sequence when the user calls the &amp;quot;Stop immediately&amp;quot; command. Better to poll in a loop until a condition is met.&lt;br /&gt;
&lt;br /&gt;
== at_exit(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to &amp;quot;tidy up&amp;quot; when the sequence stops (either due to the sequence() function exiting normally, the user requesting &amp;quot;Stop immediately&amp;quot; or &amp;quot;Stop after run&amp;quot;, or the sequence() function throwing an exception). For example, you may wish to move a motor back to a default location, or ramp down PMT voltages etc.&lt;br /&gt;
&lt;br /&gt;
Note that this is not bulletproof and should not be relied on for safety-critical items! Examples of situations where at_exit() will NOT be called include:&lt;br /&gt;
* sequencer.py being killed by a user (Ctrl-C etc) or the OS (out-of-memory killer, rebooting etc)&lt;br /&gt;
* the user&#039;s sequence() function corrupting the python interpreter so badly that it affects the main thread&lt;br /&gt;
* the user&#039;s sequence() function entering an uninterruptible state where &amp;quot;Stop immediately&amp;quot; can&#039;t actually stop anything (e.g. blocking wait on a socket)&lt;br /&gt;
* unidentified bugs in sequencer.py&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
There are two different types of messages in the sequencer:&lt;br /&gt;
* regular [[Message_System | Midas messages]] that get written to the message log&lt;br /&gt;
* sequencer-specific messages that show as a dialog box on the sequencer webpage&lt;br /&gt;
&lt;br /&gt;
You can emit regular midas messages using the &#039;&#039;&#039;seq.msg(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&#039;&#039;&#039; function. Error messages get highlighted in red on the [[Message_Page | message page]]. The &amp;quot;facility&amp;quot; routes the message to different log files / different buttons on the message page.&lt;br /&gt;
&lt;br /&gt;
You can emit sequencer-specific messages using the &#039;&#039;&#039;seq.sequencer_msg(text, wait=False)&#039;&#039;&#039; function. If &amp;quot;wait&amp;quot; is True, the sequence will not continue until the user has clicked the &amp;quot;Close&amp;quot; button on the webpage to acknowledge the message.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer message.png|600px]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
PySequencer is integrated with the python logging module. You may use the global &#039;&#039;&#039;logger&#039;&#039;&#039; variable to log your own info/debug messages. You can configure what happens to these logger messages using either command-line arguments or functions in the seq object.&lt;br /&gt;
&lt;br /&gt;
To print logger.debug() messages, you can pass the &#039;&#039;&#039;--verbose&#039;&#039;&#039; command-line flag and/or call &#039;&#039;&#039;seq.set_py_logger_debug(True)&#039;&#039;&#039; in your script. The default is to only print info() messages or higher.&lt;br /&gt;
&lt;br /&gt;
You can also copy messages to the regular midas message log (so you can call logger.info() instead of seq.msg(), and still have messages appear in the midas message log). The &amp;quot;facility&amp;quot; in this case is &amp;quot;pysequencer&amp;quot;. You can enable this functionality using the &#039;&#039;&#039;--log-to-midas&#039;&#039;&#039; command-line flag and/or by calling &#039;&#039;&#039;seq.set_py_logger_to_midas(True)&#039;&#039;&#039; in your script. This is most useful for experiments where terminal access is very limited, and they wish to debug/monitor everything via log files that are visible through webpages.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer log.png|800px]]&lt;br /&gt;
&lt;br /&gt;
== seq object reference ==&lt;br /&gt;
&lt;br /&gt;
Users have access to the &#039;&#039;&#039;seq&#039;&#039;&#039; object in their scripts, which is a &#039;&#039;&#039;midas.sequencer.SequenceClient&#039;&#039;&#039; object. This class inherits from the standard &#039;&#039;&#039;midas.client.MidasClient&#039;&#039;&#039; object, and adds extra sequencer-specific tools.&lt;br /&gt;
&lt;br /&gt;
Full documentation of these classes can be found in docstrings in the source code:&lt;br /&gt;
* MidasClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/client.py&lt;br /&gt;
* SequenceClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/sequencer.py&lt;br /&gt;
&lt;br /&gt;
A summary of the available commands is:&lt;br /&gt;
* Sequencer-specific:&lt;br /&gt;
** seq.&#039;&#039;&#039;register_param&#039;&#039;&#039;(name, comment, default_value, options=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;get_param&#039;&#039;&#039;(name)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;range&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_seconds&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_odb&#039;&#039;&#039;(path, op, target, between_upper_target=None, stable_for_n_secs=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_func&#039;&#039;&#039;(func, check_period_secs=0.1)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_clients_running&#039;&#039;&#039;(client_names, timeout_secs=10)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_events&#039;&#039;&#039;(target)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_run_description&#039;&#039;&#039;(desc)&lt;br /&gt;
* Run control&lt;br /&gt;
** seq.&#039;&#039;&#039;start_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;pause_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;resume_run&#039;&#039;&#039;()&lt;br /&gt;
* ODB access&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get&#039;&#039;&#039;(path, recurse_dir=True, include_key_metadata=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_set&#039;&#039;&#039;(path, contents) - many optional parameters!&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_exists&#039;&#039;&#039;(path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_delete&#039;&#039;&#039;(path, follow_links=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_link&#039;&#039;&#039;(link_path, destination_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get_link_destination&#039;&#039;&#039;(link_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_rename&#039;&#039;&#039;(current_path, new_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_last_update_time&#039;&#039;&#039;(path)&lt;br /&gt;
* Event buffers&lt;br /&gt;
** ...&lt;br /&gt;
* Controlling other clients&lt;br /&gt;
** seq.&#039;&#039;&#039;start_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_all_required_program_names&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;start_all_required_program&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;client_exists&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;clients_exist&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;connect_to_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;disconnect_from_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;jrpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;brpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
* Alarms&lt;br /&gt;
** ...&lt;br /&gt;
* History&lt;br /&gt;
** ...&lt;br /&gt;
* Messages&lt;br /&gt;
** seq.&#039;&#039;&#039;msg&#039;&#039;&#039;(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
* Logging&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger&#039;&#039;&#039;(debug, to_midas_msg_log)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger_debug&#039;&#039;&#039;(debug)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_py_logger_to_midas&#039;&#039;&#039;(to_midas_msg_log)&lt;br /&gt;
* Misc&lt;br /&gt;
** seq.&#039;&#039;&#039;get_midas_version&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_experiment_dir&#039;&#039;&#039;()&lt;br /&gt;
* Functions that are in MidasClient, but may NOT be used in a sequencer script&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_watch&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_jrpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_brpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_disconnect_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_transition_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;communicate&#039;&#039;&#039;()&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
&lt;br /&gt;
== Variables shown on webpage ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;variables&amp;quot; table on the webpage will display only local/global variables of the types: int, float, string, bool, numpy.number and numpy.bool. It will also display lists and numpy.ndarrays of those types. It does not display any other types (e.g. datetime.datetime, custom objects, dicts etc). We also don&#039;t show any variables where the name starts with a double underscore.&lt;br /&gt;
&lt;br /&gt;
== No callback functions ==&lt;br /&gt;
&lt;br /&gt;
Your script cannot use any of the callback-based functions present in MidasClient. This includes odb_watch(), register_jrpc_callback(), register_brpc_callback(), register_message_callback(), register_transition_callback(), register_disconnect_callback() and communicate(). This is because your script runs in a separate thread, but the main thread is the one responsible for talking to midas; any callbacks get executed in the context of the main thread, not your script&#039;s thread.&lt;br /&gt;
&lt;br /&gt;
If you want to check whether an ODB value has changed, you&#039;ll need to implement a loop and call odb_get() repeatedly. &lt;br /&gt;
&lt;br /&gt;
You MAY use register_event_request() if you want to look at data as part of your script, as you then call receive_event() to explicitly look at the events, rather than relying on a callback function.&lt;br /&gt;
&lt;br /&gt;
== No tracing in other modules ==&lt;br /&gt;
&lt;br /&gt;
It is expected that experiments will build a library/module of helper functions to automate certain tasks (e.g. &#039;&#039;&#039;move_calibration_source(x, y)&#039;&#039;&#039;). Your sequencer script can import such a module and call the functions, but the webpage will not show what is happening within that module - it would just show that we&#039;re in move_calibration_source().&lt;br /&gt;
&lt;br /&gt;
It is technically possible to enhance the PySequencer logic to trace within these modules, but it is not yet clear whether users want such a feature, and what the best user interface would be.&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Pysequencer_log.png&amp;diff=3556</id>
		<title>File:Pysequencer log.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Pysequencer_log.png&amp;diff=3556"/>
		<updated>2025-09-05T18:47:09Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Midas message log from PySequencer&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3555</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3555"/>
		<updated>2025-09-05T18:34:30Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Midas contains a [[Sequencer]] that can be used to automate data-taking (e.g. starting and stopping many runs with different settings). The original Seqeuncer uses the custom Midas Script Language (MSL), which has a limited syntax. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens when the user starts the sequence and prompts the user to input the parameter values to use.&lt;br /&gt;
&lt;br /&gt;
The user can track the state of the sequence via a webpage that shows:&lt;br /&gt;
* the current line in the script, highlighted in yellow&lt;br /&gt;
* the value of any parameters/variables, shown in a table&lt;br /&gt;
* the progress of any loops or waits, shown as progress bars&lt;br /&gt;
 &lt;br /&gt;
[[File:Pysequencer.png|800px]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= Commands =&lt;br /&gt;
&lt;br /&gt;
PySequencer is generally designed to be controlled via the web interface. However it can also be controlled via settings in the ODB. Most settings are in /PySequencer/Command, but a couple are also in /PySequencer/State&lt;br /&gt;
&lt;br /&gt;
* In /PySequencer/Command&lt;br /&gt;
** Load new file - load the file specified in /PySequencer/State/Filename&lt;br /&gt;
** Start script - start the script that has been loaded&lt;br /&gt;
** Pause script - pause the script by preventing the python interpreter from executing the next line of code&lt;br /&gt;
** Resume script - resume the script after pausing it&lt;br /&gt;
** Stop immediately - stop the sequence() function as soon as possible&lt;br /&gt;
** Stop after run - stop the sequence() function when the next run ends&lt;br /&gt;
** Cancel stop after run - cancel the &amp;quot;Stop after run&amp;quot; request&lt;br /&gt;
** Debug script - start script in debug mode where we won&#039;t move to the next line until &amp;quot;Step over&amp;quot; is set (usually via button on webpage)&lt;br /&gt;
** Step over - move to next line when running in debug mode&lt;br /&gt;
* In /PySequencer/State&lt;br /&gt;
** Path - specify a subdirectory beneath &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer where your files are located&lt;br /&gt;
** Filename - filename to load (relative to &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer or &amp;lt;experiment_dir&amp;gt;/userfiles/sequencer/&amp;lt;path&amp;gt;)&lt;br /&gt;
** Message - if &amp;quot;Message wait&amp;quot; is true, the sequence will not continue until &amp;quot;Message&amp;quot; has been reset to an empty string - see [[#Messages | the messages section]] for more details&lt;br /&gt;
&lt;br /&gt;
= User script content =&lt;br /&gt;
&lt;br /&gt;
All scripts must be located in the &#039;&#039;&#039;userfiles/sequencer&#039;&#039;&#039; subdirectory of your [[Exptab | experiment directory]]. PySequencer will only load files with a .py extension.&lt;br /&gt;
&lt;br /&gt;
PySequencer will look for three specific functions in your file:&lt;br /&gt;
* &#039;&#039;&#039;define_params(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
* &#039;&#039;&#039;sequence(seq)&#039;&#039;&#039; - required&lt;br /&gt;
* &#039;&#039;&#039;at_exit(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
These are described in more detail below. All three should accept one argument, which is a [[#seq_object_reference | midas.sequencer.SequenceClient]] object. This object is also available as a global variable within your script (e.g. if you want to define helper functions and don&#039;t want to pass the seq object to each of them).&lt;br /&gt;
&lt;br /&gt;
Your file may include any other modules, and you can define/call your own functions. If you edit the content of your file, you can reload it using the web interface or by setting the ODB parameter &amp;quot;/PySequencer/Command/Load new file&amp;quot; to true. When we reload a file, we also reload all modules that it uses (i.e. if you make changes to a custom module that contains helper functions, you can trigger loading the new definitions by reloading your main script).&lt;br /&gt;
&lt;br /&gt;
== define_params(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to create &amp;quot;parameters&amp;quot; that the user will be prompted for when they start the sequence. It is expected to be a set of &#039;&#039;&#039;seq.register_param()&#039;&#039;&#039; calls. For each parameter, you specify a name, a comment that will be shown in the prompt dialog, a default value, and optionally a list of acceptable values.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;default value&amp;quot; you provide determines the type of the variable in the ODB. If you specify a string it will be a TID_STRING, an integer will become TID_INT32 etc.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    # Will be stored as an integer&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a float&lt;br /&gt;
    seq.register_param(&amp;quot;voltage&amp;quot;, &amp;quot;PMT voltage&amp;quot;, 1342.7)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a string, and user will see a dropdown to choose from&lt;br /&gt;
    seq.register_param(&amp;quot;run type&amp;quot;, &amp;quot;Purpose of this run&amp;quot;, &amp;quot;normal&amp;quot;, [&amp;quot;normal&amp;quot;, &amp;quot;calibration&amp;quot;, &amp;quot;testing&amp;quot;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The main sequence() function can then retrieve the values of these parameters by calling &#039;&#039;&#039;seq.get_param(&amp;quot;voltage&amp;quot;)&#039;&#039;&#039; etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer params.png|400px]]&lt;br /&gt;
&lt;br /&gt;
== sequence(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function MUST be defined, and is the main script that will be executed. Full documentation of the functions available is in the [[#seq_object_reference | seq object reference]] section, but you may call/define your own functions as well, and also call functions from other modules.&lt;br /&gt;
&lt;br /&gt;
There are a few &amp;quot;best practices&amp;quot; to consider to maximise integration with the webpage:&lt;br /&gt;
* Use &#039;&#039;&#039;seq.wait_seconds()&#039;&#039;&#039; instead of &#039;&#039;&#039;time.sleep()&#039;&#039;&#039; so you get a progress bar on the webpage. &#039;&#039;&#039;seq.sleep()&#039;&#039;&#039; is an alias for seq.wait_seconds() if you prefer.&lt;br /&gt;
* In a normal &amp;quot;for&amp;quot; loop use &#039;&#039;&#039;seq.range()&#039;&#039;&#039; instead of &#039;&#039;&#039;range()&#039;&#039;&#039; so you get a progress bar on the webpage. We don&#039;t have a way to show progress through any other type of for/while loop&lt;br /&gt;
* Avoid any very long-lasting calls (e.g. blocking on a socket) as these may prevent us from being able to stop the sequence when the user calls the &amp;quot;Stop immediately&amp;quot; command. Better to poll in a loop until a condition is met.&lt;br /&gt;
&lt;br /&gt;
== at_exit(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to &amp;quot;tidy up&amp;quot; when the sequence stops (either due to the sequence() function exiting normally, the user requesting &amp;quot;Stop immediately&amp;quot; or &amp;quot;Stop after run&amp;quot;, or the sequence() function throwing an exception). For example, you may wish to move a motor back to a default location, or ramp down PMT voltages etc.&lt;br /&gt;
&lt;br /&gt;
Note that this is not bulletproof and should not be relied on for safety-critical items! Examples of situations where at_exit() will NOT be called include:&lt;br /&gt;
* sequencer.py being killed by a user (Ctrl-C etc) or the OS (out-of-memory killer, rebooting etc)&lt;br /&gt;
* the user&#039;s sequence() function corrupting the python interpreter so badly that it affects the main thread&lt;br /&gt;
* the user&#039;s sequence() function entering an uninterruptible state where &amp;quot;Stop immediately&amp;quot; can&#039;t actually stop anything (e.g. blocking wait on a socket)&lt;br /&gt;
* unidentified bugs in sequencer.py&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
There are two different types of messages in the sequencer:&lt;br /&gt;
* regular [[Message_System | Midas messages]] that get written to the message log&lt;br /&gt;
* sequencer-specific messages that show as a dialog box on the sequencer webpage&lt;br /&gt;
&lt;br /&gt;
You can emit regular midas messages using the &#039;&#039;&#039;seq.msg(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&#039;&#039;&#039; function. Error messages get highlighted in red on the [[Message_Page | message page]]. The &amp;quot;facility&amp;quot; routes the message to different log files / different buttons on the message page.&lt;br /&gt;
&lt;br /&gt;
You can emit sequencer-specific messages using the &#039;&#039;&#039;seq.sequencer_msg(text, wait=False)&#039;&#039;&#039; function. If &amp;quot;wait&amp;quot; is True, the sequence will not continue until the user has clicked the &amp;quot;Close&amp;quot; button on the webpage to acknowledge the message.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer message.png|600px]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
== seq object reference ==&lt;br /&gt;
&lt;br /&gt;
Users have access to the &#039;&#039;&#039;seq&#039;&#039;&#039; object in their scripts, which is a &#039;&#039;&#039;midas.sequencer.SequenceClient&#039;&#039;&#039; object. This class inherits from the standard &#039;&#039;&#039;midas.client.MidasClient&#039;&#039;&#039; object, and adds extra sequencer-specific tools.&lt;br /&gt;
&lt;br /&gt;
Full documentation of these classes can be found in docstrings in the source code:&lt;br /&gt;
* MidasClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/client.py&lt;br /&gt;
* SequenceClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/sequencer.py&lt;br /&gt;
&lt;br /&gt;
A summary of the available commands is:&lt;br /&gt;
* Sequencer-specific:&lt;br /&gt;
** seq.&#039;&#039;&#039;register_param&#039;&#039;&#039;(name, comment, default_value, options=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;get_param&#039;&#039;&#039;(name)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;range&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_seconds&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_odb&#039;&#039;&#039;(path, op, target, between_upper_target=None, stable_for_n_secs=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_func&#039;&#039;&#039;(func, check_period_secs=0.1)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_clients_running&#039;&#039;&#039;(client_names, timeout_secs=10)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_events&#039;&#039;&#039;(target)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_run_description&#039;&#039;&#039;(desc)&lt;br /&gt;
* Run control&lt;br /&gt;
** seq.&#039;&#039;&#039;start_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;pause_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;resume_run&#039;&#039;&#039;()&lt;br /&gt;
* ODB access&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get&#039;&#039;&#039;(path, recurse_dir=True, include_key_metadata=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_set&#039;&#039;&#039;(path, contents) - many optional parameters!&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_exists&#039;&#039;&#039;(path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_delete&#039;&#039;&#039;(path, follow_links=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_link&#039;&#039;&#039;(link_path, destination_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get_link_destination&#039;&#039;&#039;(link_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_rename&#039;&#039;&#039;(current_path, new_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_last_update_time&#039;&#039;&#039;(path)&lt;br /&gt;
* Event buffers&lt;br /&gt;
** ...&lt;br /&gt;
* Controlling other clients&lt;br /&gt;
** seq.&#039;&#039;&#039;start_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_all_required_program_names&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;start_all_required_program&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;client_exists&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;clients_exist&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;connect_to_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;disconnect_from_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;jrpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;brpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
* Alarms&lt;br /&gt;
** ...&lt;br /&gt;
* History&lt;br /&gt;
** ...&lt;br /&gt;
* Messages&lt;br /&gt;
** seq.&#039;&#039;&#039;msg&#039;&#039;&#039;(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** ...&lt;br /&gt;
* Misc&lt;br /&gt;
** seq.&#039;&#039;&#039;get_midas_version&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_experiment_dir&#039;&#039;&#039;()&lt;br /&gt;
* Functions that are in MidasClient, but may NOT be used in a sequencer script&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_watch&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_jrpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_brpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_disconnect_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_transition_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;communicate&#039;&#039;&#039;()&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
&lt;br /&gt;
== Variables shown on webpage ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;variables&amp;quot; table on the webpage will display only local/global variables of the types: int, float, string, bool, numpy.number and numpy.bool. It will also display lists and numpy.ndarrays of those types. It does not display any other types (e.g. datetime.datetime, custom objects, dicts etc). We also don&#039;t show any variables where the name starts with a double underscore.&lt;br /&gt;
&lt;br /&gt;
== No callback functions ==&lt;br /&gt;
&lt;br /&gt;
Your script cannot use any of the callback-based functions present in MidasClient. This includes odb_watch(), register_jrpc_callback(), register_brpc_callback(), register_message_callback(), register_transition_callback(), register_disconnect_callback() and communicate(). This is because your script runs in a separate thread, but the main thread is the one responsible for talking to midas; any callbacks get executed in the context of the main thread, not your script&#039;s thread.&lt;br /&gt;
&lt;br /&gt;
If you want to check whether an ODB value has changed, you&#039;ll need to implement a loop and call odb_get() repeatedly. &lt;br /&gt;
&lt;br /&gt;
You MAY use register_event_request() if you want to look at data as part of your script, as you then call receive_event() to explicitly look at the events, rather than relying on a callback function.&lt;br /&gt;
&lt;br /&gt;
== No tracing in other modules ==&lt;br /&gt;
&lt;br /&gt;
It is expected that experiments will build a library/module of helper functions to automate certain tasks (e.g. &#039;&#039;&#039;move_calibration_source(x, y)&#039;&#039;&#039;). Your sequencer script can import such a module and call the functions, but the webpage will not show what is happening within that module - it would just show that we&#039;re in move_calibration_source().&lt;br /&gt;
&lt;br /&gt;
It is technically possible to enhance the PySequencer logic to trace within these modules, but it is not yet clear whether users want such a feature, and what the best user interface would be.&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3554</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3554"/>
		<updated>2025-09-05T18:12:22Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Midas contains a [[Sequencer]] that can be used to automate data-taking (e.g. starting and stopping many runs with different settings). The original Seqeuncer uses the custom Midas Script Language (MSL), which has a limited syntax. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens when the user starts the sequence and prompts the user to input the parameter values to use.&lt;br /&gt;
&lt;br /&gt;
The user can track the state of the sequence via a webpage that shows:&lt;br /&gt;
* the current line in the script, highlighted in yellow&lt;br /&gt;
* the value of any parameters/variables, shown in a table&lt;br /&gt;
* the progress of any loops or waits, shown as progress bars&lt;br /&gt;
 &lt;br /&gt;
[[File:Pysequencer.png|800px]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= Commands =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= User script content =&lt;br /&gt;
&lt;br /&gt;
All scripts must be located in the &#039;&#039;&#039;userfiles/sequencer&#039;&#039;&#039; subdirectory of your [[Exptab | experiment directory]]. PySequencer will only load files with a .py extension.&lt;br /&gt;
&lt;br /&gt;
PySequencer will look for three specific functions in your file:&lt;br /&gt;
* &#039;&#039;&#039;define_params(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
* &#039;&#039;&#039;sequence(seq)&#039;&#039;&#039; - required&lt;br /&gt;
* &#039;&#039;&#039;at_exit(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
These are described in more detail below. All three should accept one argument, which is a [[#seq_object_reference | midas.sequencer.SequenceClient]] object. This object is also available as a global variable within your script (e.g. if you want to define helper functions and don&#039;t want to pass the seq object to each of them).&lt;br /&gt;
&lt;br /&gt;
Your file may include any other modules, and you can define/call your own functions. If you edit the content of your file, you can reload it using the web interface or by setting the ODB parameter &amp;quot;/PySequencer/Command/Load new file&amp;quot; to true. When we reload a file, we also reload all modules that it uses (i.e. if you make changes to a custom module that contains helper functions, you can trigger loading the new definitions by reloading your main script).&lt;br /&gt;
&lt;br /&gt;
== define_params(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to create &amp;quot;parameters&amp;quot; that the user will be prompted for when they start the sequence. It is expected to be a set of &#039;&#039;&#039;seq.register_param()&#039;&#039;&#039; calls. For each parameter, you specify a name, a comment that will be shown in the prompt dialog, a default value, and optionally a list of acceptable values.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;default value&amp;quot; you provide determines the type of the variable in the ODB. If you specify a string it will be a TID_STRING, an integer will become TID_INT32 etc.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    # Will be stored as an integer&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a float&lt;br /&gt;
    seq.register_param(&amp;quot;voltage&amp;quot;, &amp;quot;PMT voltage&amp;quot;, 1342.7)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a string, and user will see a dropdown to choose from&lt;br /&gt;
    seq.register_param(&amp;quot;run type&amp;quot;, &amp;quot;Purpose of this run&amp;quot;, &amp;quot;normal&amp;quot;, [&amp;quot;normal&amp;quot;, &amp;quot;calibration&amp;quot;, &amp;quot;testing&amp;quot;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The main sequence() function can then retrieve the values of these parameters by calling &#039;&#039;&#039;seq.get_param(&amp;quot;voltage&amp;quot;)&#039;&#039;&#039; etc.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer params.png|400px]]&lt;br /&gt;
&lt;br /&gt;
== sequence(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function MUST be defined, and is the main script that will be executed. Full documentation of the functions available is in the [[#seq_object_reference | seq object reference]] section, but you may call/define your own functions as well, and also call functions from other modules.&lt;br /&gt;
&lt;br /&gt;
There are a few &amp;quot;best practices&amp;quot; to consider to maximise integration with the webpage:&lt;br /&gt;
* Use &#039;&#039;&#039;seq.wait_seconds()&#039;&#039;&#039; instead of &#039;&#039;&#039;time.sleep()&#039;&#039;&#039; so you get a progress bar on the webpage. &#039;&#039;&#039;seq.sleep()&#039;&#039;&#039; is an alias for seq.wait_seconds() if you prefer.&lt;br /&gt;
* In a normal &amp;quot;for&amp;quot; loop use &#039;&#039;&#039;seq.range()&#039;&#039;&#039; instead of &#039;&#039;&#039;range()&#039;&#039;&#039; so you get a progress bar on the webpage. We don&#039;t have a way to show progress through any other type of for/while loop&lt;br /&gt;
* Avoid any very long-lasting calls (e.g. blocking on a socket) as these may prevent us from being able to stop the sequence when the user calls the &amp;quot;Stop immediately&amp;quot; command. Better to poll in a loop until a condition is met.&lt;br /&gt;
&lt;br /&gt;
== at_exit(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to &amp;quot;tidy up&amp;quot; when the sequence stops (either due to the sequence() function exiting normally, the user requesting &amp;quot;Stop immediately&amp;quot; or &amp;quot;Stop after run&amp;quot;, or the sequence() function throwing an exception). For example, you may wish to move a motor back to a default location, or ramp down PMT voltages etc.&lt;br /&gt;
&lt;br /&gt;
Note that this is not bulletproof and should not be relied on for safety-critical items! Examples of situations where at_exit() will NOT be called include:&lt;br /&gt;
* sequencer.py being killed by a user (Ctrl-C etc) or the OS (out-of-memory killer, rebooting etc)&lt;br /&gt;
* the user&#039;s sequence() function corrupting the python interpreter so badly that it affects the main thread&lt;br /&gt;
* the user&#039;s sequence() function entering an uninterruptible state where &amp;quot;Stop immediately&amp;quot; can&#039;t actually stop anything (e.g. blocking wait on a socket)&lt;br /&gt;
* unidentified bugs in sequencer.py&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
There are two different types of messages in the sequencer:&lt;br /&gt;
* regular [[Message_System | Midas messages]] that get written to the message log&lt;br /&gt;
* sequencer-specific messages that show as a dialog box on the sequencer webpage&lt;br /&gt;
&lt;br /&gt;
You can emit regular midas messages using the &#039;&#039;&#039;seq.msg(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&#039;&#039;&#039; function. Error messages get highlighted in red on the [[Message_Page | message page]]. The &amp;quot;facility&amp;quot; routes the message to different log files / different buttons on the message page.&lt;br /&gt;
&lt;br /&gt;
You can emit sequencer-specific messages using the &#039;&#039;&#039;seq.sequencer_msg(text, wait=False)&#039;&#039;&#039; function. If &amp;quot;wait&amp;quot; is True, the sequence will not continue until the user has clicked the &amp;quot;Close&amp;quot; button on the webpage to acknowledge the message.&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer message.png|600px]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
== seq object reference ==&lt;br /&gt;
&lt;br /&gt;
Users have access to the &#039;&#039;&#039;seq&#039;&#039;&#039; object in their scripts, which is a &#039;&#039;&#039;midas.sequencer.SequenceClient&#039;&#039;&#039; object. This class inherits from the standard &#039;&#039;&#039;midas.client.MidasClient&#039;&#039;&#039; object, and adds extra sequencer-specific tools.&lt;br /&gt;
&lt;br /&gt;
Full documentation of these classes can be found in docstrings in the source code:&lt;br /&gt;
* MidasClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/client.py&lt;br /&gt;
* SequenceClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/sequencer.py&lt;br /&gt;
&lt;br /&gt;
A summary of the available commands is:&lt;br /&gt;
* Sequencer-specific:&lt;br /&gt;
** seq.&#039;&#039;&#039;register_param&#039;&#039;&#039;(name, comment, default_value, options=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;get_param&#039;&#039;&#039;(name)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;range&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_seconds&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_odb&#039;&#039;&#039;(path, op, target, between_upper_target=None, stable_for_n_secs=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_func&#039;&#039;&#039;(func, check_period_secs=0.1)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_clients_running&#039;&#039;&#039;(client_names, timeout_secs=10)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_events&#039;&#039;&#039;(target)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_run_description&#039;&#039;&#039;(desc)&lt;br /&gt;
* Run control&lt;br /&gt;
** seq.&#039;&#039;&#039;start_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;pause_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;resume_run&#039;&#039;&#039;()&lt;br /&gt;
* ODB access&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get&#039;&#039;&#039;(path, recurse_dir=True, include_key_metadata=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_set&#039;&#039;&#039;(path, contents) - many optional parameters!&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_exists&#039;&#039;&#039;(path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_delete&#039;&#039;&#039;(path, follow_links=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_link&#039;&#039;&#039;(link_path, destination_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get_link_destination&#039;&#039;&#039;(link_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_rename&#039;&#039;&#039;(current_path, new_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_last_update_time&#039;&#039;&#039;(path)&lt;br /&gt;
* Event buffers&lt;br /&gt;
** ...&lt;br /&gt;
* Controlling other clients&lt;br /&gt;
** seq.&#039;&#039;&#039;start_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_all_required_program_names&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;start_all_required_program&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;client_exists&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;clients_exist&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;connect_to_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;disconnect_from_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;jrpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;brpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
* Alarms&lt;br /&gt;
** ...&lt;br /&gt;
* History&lt;br /&gt;
** ...&lt;br /&gt;
* Messages&lt;br /&gt;
** seq.&#039;&#039;&#039;msg&#039;&#039;&#039;(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** ...&lt;br /&gt;
* Misc&lt;br /&gt;
** seq.&#039;&#039;&#039;get_midas_version&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_experiment_dir&#039;&#039;&#039;()&lt;br /&gt;
* Functions that are in MidasClient, but may NOT be used in a sequencer script&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_watch&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_jrpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_brpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_disconnect_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_transition_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;communicate&#039;&#039;&#039;()&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
&lt;br /&gt;
== Variables shown on webpage ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;variables&amp;quot; table on the webpage will display only local/global variables of the types: int, float, string, bool, numpy.number and numpy.bool. It will also display lists and numpy.ndarrays of those types. It does not display any other types (e.g. datetime.datetime, custom objects, dicts etc). We also don&#039;t show any variables where the name starts with a double underscore.&lt;br /&gt;
&lt;br /&gt;
== No callback functions ==&lt;br /&gt;
&lt;br /&gt;
Your script cannot use any of the callback-based functions present in MidasClient. This includes odb_watch(), register_jrpc_callback(), register_brpc_callback(), register_message_callback(), register_transition_callback(), register_disconnect_callback() and communicate(). This is because your script runs in a separate thread, but the main thread is the one responsible for talking to midas; any callbacks get executed in the context of the main thread, not your script&#039;s thread.&lt;br /&gt;
&lt;br /&gt;
If you want to check whether an ODB value has changed, you&#039;ll need to implement a loop and call odb_get() repeatedly. &lt;br /&gt;
&lt;br /&gt;
You MAY use register_event_request() if you want to look at data as part of your script, as you then call receive_event() to explicitly look at the events, rather than relying on a callback function.&lt;br /&gt;
&lt;br /&gt;
== No tracing in other modules ==&lt;br /&gt;
&lt;br /&gt;
It is expected that experiments will build a library/module of helper functions to automate certain tasks (e.g. &#039;&#039;&#039;move_calibration_source(x, y)&#039;&#039;&#039;). Your sequencer script can import such a module and call the functions, but the webpage will not show what is happening within that module - it would just show that we&#039;re in move_calibration_source().&lt;br /&gt;
&lt;br /&gt;
It is technically possible to enhance the PySequencer logic to trace within these modules, but it is not yet clear whether users want such a feature, and what the best user interface would be.&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Pysequencer_message.png&amp;diff=3553</id>
		<title>File:Pysequencer message.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Pysequencer_message.png&amp;diff=3553"/>
		<updated>2025-09-05T18:10:35Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;PySequencer message dialog&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3552</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3552"/>
		<updated>2025-09-05T17:34:39Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Midas contains a [[Sequencer]] that can be used to automate data-taking (e.g. starting and stopping many runs with different settings). The original Seqeuncer uses the custom Midas Script Language (MSL), which has a limited syntax. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens when the user starts the sequence and prompts the user to input the parameter values to use.&lt;br /&gt;
&lt;br /&gt;
The user can track the state of the sequence via a webpage that shows:&lt;br /&gt;
* the current line in the script, highlighted in yellow&lt;br /&gt;
* the value of any parameters/variables, shown in a table&lt;br /&gt;
* the progress of any loops or waits, shown as progress bars&lt;br /&gt;
 &lt;br /&gt;
[[File:Pysequencer.png|800px]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= User script content =&lt;br /&gt;
&lt;br /&gt;
All scripts must be located in the &#039;&#039;&#039;userfiles/sequencer&#039;&#039;&#039; subdirectory of your [[Exptab | experiment directory]]. PySequencer will only load files with a .py extension.&lt;br /&gt;
&lt;br /&gt;
PySequencer will look for three specific functions in your file:&lt;br /&gt;
* &#039;&#039;&#039;define_params(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
* &#039;&#039;&#039;sequence(seq)&#039;&#039;&#039; - required&lt;br /&gt;
* &#039;&#039;&#039;at_exit(seq)&#039;&#039;&#039; - optional&lt;br /&gt;
These are described in more detail below. All three should accept one argument, which is a [[#seq_object_reference | midas.sequencer.SequenceClient]] object. This object is also available as a global variable within your script (e.g. if you want to define helper functions and don&#039;t want to pass the seq object to each of them).&lt;br /&gt;
&lt;br /&gt;
Your file may include any other modules, and you can define/call your own functions. If you edit the content of your file, you can reload it using the web interface or by setting the ODB parameter &amp;quot;/PySequencer/Command/Load new file&amp;quot; to true. When we reload a file, we also reload all modules that it uses (i.e. if you make changes to a custom module that contains helper functions, you can trigger loading the new definitions by reloading your main script).&lt;br /&gt;
&lt;br /&gt;
== define_params(seq) function ==&lt;br /&gt;
&lt;br /&gt;
This function is optional, and is used to create &amp;quot;parameters&amp;quot; that the user will be prompted for when they start the sequence. It is expected to be a set of &#039;&#039;&#039;seq.register_param()&#039;&#039;&#039; calls. For each parameter, you specify a name, a comment that will be shown in the prompt dialog, a default value, and optionally a list of acceptable values.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;default value&amp;quot; you provide determines the type of the variable in the ODB. If you specify a string it will be a TID_STRING, an integer will become TID_INT32 etc.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    # Will be stored as an integer&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a float&lt;br /&gt;
    seq.register_param(&amp;quot;voltage&amp;quot;, &amp;quot;PMT voltage&amp;quot;, 1342.7)&lt;br /&gt;
&lt;br /&gt;
    # Will be stored as a string, and user will see a dropdown to choose from&lt;br /&gt;
    seq.register_param(&amp;quot;run type&amp;quot;, &amp;quot;Purpose of this run&amp;quot;, &amp;quot;normal&amp;quot;, [&amp;quot;normal&amp;quot;, &amp;quot;calibration&amp;quot;, &amp;quot;testing&amp;quot;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Pysequencer params.png|400px]]&lt;br /&gt;
&lt;br /&gt;
== sequence(seq) function ==&lt;br /&gt;
&lt;br /&gt;
== at_exit(seq) function ==&lt;br /&gt;
&lt;br /&gt;
== Messages ==&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
== seq object reference ==&lt;br /&gt;
&lt;br /&gt;
Users have access to the &#039;&#039;&#039;seq&#039;&#039;&#039; object in their scripts, which is a &#039;&#039;&#039;midas.sequencer.SequenceClient&#039;&#039;&#039; object. This class inherits from the standard &#039;&#039;&#039;midas.client.MidasClient&#039;&#039;&#039; object, and adds extra sequencer-specific tools.&lt;br /&gt;
&lt;br /&gt;
Full documentation of these classes can be found in docstrings in the source code:&lt;br /&gt;
* MidasClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/client.py&lt;br /&gt;
* SequenceClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/sequencer.py&lt;br /&gt;
&lt;br /&gt;
A summary of the available commands is:&lt;br /&gt;
* Sequencer-specific:&lt;br /&gt;
** seq.&#039;&#039;&#039;register_param&#039;&#039;&#039;(name, comment, default_value, options=[])&lt;br /&gt;
** seq.&#039;&#039;&#039;get_param&#039;&#039;&#039;(name)&lt;br /&gt;
** seq.&#039;&#039;&#039;sequencer_msg&#039;&#039;&#039;(text, wait=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;range&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_seconds&#039;&#039;&#039;(n)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_odb&#039;&#039;&#039;(path, op, target, between_upper_target=None, stable_for_n_secs=None)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_func&#039;&#039;&#039;(func, check_period_secs=0.1)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_clients_running&#039;&#039;&#039;(client_names, timeout_secs=10)&lt;br /&gt;
** seq.&#039;&#039;&#039;wait_events&#039;&#039;&#039;(target)&lt;br /&gt;
** seq.&#039;&#039;&#039;set_run_description&#039;&#039;&#039;(desc)&lt;br /&gt;
* Run control&lt;br /&gt;
** seq.&#039;&#039;&#039;start_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;pause_run&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;resume_run&#039;&#039;&#039;()&lt;br /&gt;
* ODB access&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get&#039;&#039;&#039;(path, recurse_dir=True, include_key_metadata=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_set&#039;&#039;&#039;(path, contents) - many optional parameters!&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_exists&#039;&#039;&#039;(path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_delete&#039;&#039;&#039;(path, follow_links=False)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_link&#039;&#039;&#039;(link_path, destination_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_get_link_destination&#039;&#039;&#039;(link_path)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_rename&#039;&#039;&#039;(current_path, new_name)&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_last_update_time&#039;&#039;&#039;(path)&lt;br /&gt;
* Event buffers&lt;br /&gt;
** ...&lt;br /&gt;
* Controlling other clients&lt;br /&gt;
** seq.&#039;&#039;&#039;start_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;stop_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_all_required_program_names&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;start_all_required_program&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;client_exists&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;clients_exist&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;connect_to_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;disconnect_from_other_client&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;jrpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;brpc_client_call&#039;&#039;&#039;()&lt;br /&gt;
* Alarms&lt;br /&gt;
** ...&lt;br /&gt;
* History&lt;br /&gt;
** ...&lt;br /&gt;
* Messages&lt;br /&gt;
** seq.&#039;&#039;&#039;msg&#039;&#039;&#039;(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&lt;br /&gt;
** ...&lt;br /&gt;
* Misc&lt;br /&gt;
** seq.&#039;&#039;&#039;get_midas_version&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;get_experiment_dir&#039;&#039;&#039;()&lt;br /&gt;
* Functions that are in MidasClient, but may NOT be used in a sequencer script&lt;br /&gt;
** seq.&#039;&#039;&#039;odb_watch&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_jrpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_brpc_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_disconnect_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;register_transition_callback&#039;&#039;&#039;()&lt;br /&gt;
** seq.&#039;&#039;&#039;communicate&#039;&#039;&#039;()&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
&lt;br /&gt;
== Variables shown on webpage ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;variables&amp;quot; table on the webpage will display only local/global variables of the types: int, float, string, bool, numpy.number and numpy.bool. It will also display lists and numpy.ndarrays of those types. It does not display any other types (e.g. datetime.datetime, custom objects, dicts etc). We also don&#039;t show any variables where the name starts with a double underscore.&lt;br /&gt;
&lt;br /&gt;
== No callback functions ==&lt;br /&gt;
&lt;br /&gt;
Your script cannot use any of the callback-based functions present in MidasClient. This includes odb_watch(), register_jrpc_callback(), register_brpc_callback(), register_message_callback(), register_transition_callback(), register_disconnect_callback() and communicate(). This is because your script runs in a separate thread, but the main thread is the one responsible for talking to midas; any callbacks get executed in the context of the main thread, not your script&#039;s thread.&lt;br /&gt;
&lt;br /&gt;
If you want to check whether an ODB value has changed, you&#039;ll need to implement a loop and call odb_get() repeatedly. &lt;br /&gt;
&lt;br /&gt;
You MAY use register_event_request() if you want to look at data as part of your script, as you then call receive_event() to explicitly look at the events, rather than relying on a callback function.&lt;br /&gt;
&lt;br /&gt;
== No tracing in other modules ==&lt;br /&gt;
&lt;br /&gt;
It is expected that experiments will build a library/module of helper functions to automate certain tasks (e.g. &#039;&#039;&#039;move_calibration_source(x, y)&#039;&#039;&#039;). Your sequencer script can import such a module and call the functions, but the webpage will not show what is happening within that module - it would just show that we&#039;re in move_calibration_source().&lt;br /&gt;
&lt;br /&gt;
It is technically possible to enhance the PySequencer logic to trace within these modules, but it is not yet clear whether users want such a feature, and what the best user interface would be.&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Pysequencer_params.png&amp;diff=3551</id>
		<title>File:Pysequencer params.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Pysequencer_params.png&amp;diff=3551"/>
		<updated>2025-09-05T17:26:24Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Parameters dialog from PySequencer webpage&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3550</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3550"/>
		<updated>2025-09-04T22:51:28Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* seq Object Reference */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Midas contains a [[Sequencer]] that can be used to automate data-taking (e.g. starting and stopping many runs with different settings). The original Seqeuncer uses the custom Midas Script Language (MSL), which has a limited syntax. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens when the user starts the sequence and prompts the user to input the parameter values to use.&lt;br /&gt;
&lt;br /&gt;
The user can track the state of the sequence via a webpage that shows:&lt;br /&gt;
* the current line in the script, highlighted in yellow&lt;br /&gt;
* the value of any parameters/variables, shown in a table&lt;br /&gt;
* the progress of any loops or waits, shown as progress bars&lt;br /&gt;
 &lt;br /&gt;
[[File:Pysequencer.png|800px]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= seq Object Reference =&lt;br /&gt;
&lt;br /&gt;
Users have access to the &#039;&#039;&#039;seq&#039;&#039;&#039; object in their scripts, which is a &#039;&#039;&#039;midas.sequencer.SequenceClient&#039;&#039;&#039; object. This class inherits from the standard &#039;&#039;&#039;midas.client.MidasClient&#039;&#039;&#039; object, and adds extra sequencer-specific tools.&lt;br /&gt;
&lt;br /&gt;
Full documentation of these classes can be found in docstrings in the source code:&lt;br /&gt;
* MidasClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/client.py&lt;br /&gt;
* SequenceClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/sequencer.py&lt;br /&gt;
&lt;br /&gt;
A summary of the available commands is:&lt;br /&gt;
* Sequencer-specific:&lt;br /&gt;
** &#039;&#039;&#039;seq.register_param(name, comment, default_value, options=[])&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.get_param(name)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.sequencer_msg(text, wait=False)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.range(n)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_seconds(n)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_odb(path, op, target, between_upper_target=None, stable_for_n_secs=None)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_func(func, check_period_secs=0.1)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_clients_running(client_names, timeout_secs=10)&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_events(target)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.set_run_description(desc)&#039;&#039;&#039;&lt;br /&gt;
* Run control&lt;br /&gt;
** &#039;&#039;&#039;seq.start_run()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.stop_run()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.pause_run()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.resume_run()&#039;&#039;&#039;&lt;br /&gt;
* ODB access&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_get(path, recurse_dir=True, include_key_metadata=False)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_set(path, contents)&#039;&#039;&#039; - many optional parameters!&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_exists(path)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_delete(path, follow_links=False)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_link(link_path, destination_path)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_get_link_destination(link_path)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_rename(current_path, new_name)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_last_update_time(path)&#039;&#039;&#039;&lt;br /&gt;
* Event buffers&lt;br /&gt;
** ...&lt;br /&gt;
* Controlling other clients&lt;br /&gt;
** &#039;&#039;&#039;seq.start_client()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.stop_client()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.get_all_required_program_names()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.start_all_required_programs()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.client_exists()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.clients_exist()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.connect_to_other_client()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.disconnect_from_other_client()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.jrpc_client_call()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.brpc_client_call()&#039;&#039;&#039;&lt;br /&gt;
* Alarms&lt;br /&gt;
** ...&lt;br /&gt;
* History&lt;br /&gt;
** ...&lt;br /&gt;
* Messages&lt;br /&gt;
** &#039;&#039;&#039;seq.msg(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&#039;&#039;&#039;&lt;br /&gt;
** ...&lt;br /&gt;
* Misc&lt;br /&gt;
** &#039;&#039;&#039;seq.get_midas_version()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.get_experiment_dir()&#039;&#039;&#039;&lt;br /&gt;
* Functions that are in MidasClient, but may NOT be used in a sequencer script&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_watch()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.register_jrpc_callback()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.register_brpc_callback()&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3549</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3549"/>
		<updated>2025-09-04T22:50:56Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
Midas contains a [[Sequencer]] that can be used to automate data-taking (e.g. starting and stopping many runs with different settings). The original Seqeuncer uses the custom Midas Script Language (MSL), which has a limited syntax. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens when the user starts the sequence and prompts the user to input the parameter values to use.&lt;br /&gt;
&lt;br /&gt;
The user can track the state of the sequence via a webpage that shows:&lt;br /&gt;
* the current line in the script, highlighted in yellow&lt;br /&gt;
* the value of any parameters/variables, shown in a table&lt;br /&gt;
* the progress of any loops or waits, shown as progress bars&lt;br /&gt;
 &lt;br /&gt;
[[File:Pysequencer.png|800px]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= seq Object Reference =&lt;br /&gt;
&lt;br /&gt;
Users have access to the &#039;&#039;&#039;seq&#039;&#039;&#039; object in their scripts, which is a &#039;&#039;&#039;midas.sequencer.SequenceClient&#039;&#039;&#039; object. This class inherits from the standard &#039;&#039;&#039;midas.client.MidasClient&#039;&#039;&#039; object, and adds extra sequencer-specific tools.&lt;br /&gt;
&lt;br /&gt;
Full documentation of these classes can be found in docstrings in the source code:&lt;br /&gt;
* MidasClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/client.py&lt;br /&gt;
* SequenceClient - https://bitbucket.org/tmidas/midas/src/develop/python/midas/sequencer.py&lt;br /&gt;
&lt;br /&gt;
A summary of the available commands is:&lt;br /&gt;
* Sequencer-specific:&lt;br /&gt;
** &#039;&#039;&#039;seq.register_param(name, comment, default_value, options=[])&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.get_param(name)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.sequencer_msg(text, wait=False)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.range(n)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_seconds(n)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_odb(path, op, target, between_upper_target=None, stable_for_n_secs=None)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_func(func, check_period_secs=0.1)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_clients_running(client_names, timeout_secs=10)&lt;br /&gt;
** &#039;&#039;&#039;seq.wait_events(target)&#039;&#039;&#039; retrieves a parameter &#039;&#039;name&#039;&#039; from the sequence start&lt;br /&gt;
** &#039;&#039;&#039;seq.set_run_description(desc)&#039;&#039;&#039;&lt;br /&gt;
* Run control&lt;br /&gt;
** &#039;&#039;&#039;seq.start_run()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.stop_run()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.pause_run()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.resume_run()&#039;&#039;&#039;&lt;br /&gt;
* ODB access&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_get(path, recurse_dir=True, include_key_metadata=False)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_set(path, contents)&#039;&#039;&#039; - many optional parameters!&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_exists(path)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_delete(path, follow_links=False)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_link(link_path, destination_path)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_get_link_destination(link_path)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_rename(current_path, new_name)&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_last_update_time(path)&#039;&#039;&#039;&lt;br /&gt;
* Event buffers&lt;br /&gt;
** ...&lt;br /&gt;
* Controlling other clients&lt;br /&gt;
** &#039;&#039;&#039;seq.start_client()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.stop_client()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.get_all_required_program_names()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.start_all_required_programs()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.client_exists()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.clients_exist()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.connect_to_other_client()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.disconnect_from_other_client()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.jrpc_client_call()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.brpc_client_call()&#039;&#039;&#039;&lt;br /&gt;
* Alarms&lt;br /&gt;
** ...&lt;br /&gt;
* History&lt;br /&gt;
** ...&lt;br /&gt;
* Messages&lt;br /&gt;
** &#039;&#039;&#039;seq.msg(message, is_error=False, facility=&amp;quot;midas&amp;quot;)&#039;&#039;&#039;&lt;br /&gt;
** ...&lt;br /&gt;
* Misc&lt;br /&gt;
** &#039;&#039;&#039;seq.get_midas_version()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.get_experiment_dir()&#039;&#039;&#039;&lt;br /&gt;
* Functions that are in MidasClient, but may NOT be used in a sequencer script&lt;br /&gt;
** &#039;&#039;&#039;seq.odb_watch()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.register_jrpc_callback()&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;seq.register_brpc_callback()&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Pysequencer.png&amp;diff=3548</id>
		<title>File:Pysequencer.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Pysequencer.png&amp;diff=3548"/>
		<updated>2025-09-04T22:24:25Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;PySequencer example screenshot&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Remote_Procedure_Calls_(RPC)&amp;diff=3464</id>
		<title>Remote Procedure Calls (RPC)</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Remote_Procedure_Calls_(RPC)&amp;diff=3464"/>
		<updated>2025-01-20T23:08:07Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= RPC in a nutshell =&lt;br /&gt;
&lt;br /&gt;
Midas uses a Remote Procedure Call (RPC) system to allow different midas clients to talk to each other directly. One client can issue a command to another, and the second client will perform some action and send a response back to the caller.&lt;br /&gt;
&lt;br /&gt;
= Internal usage in midas =&lt;br /&gt;
&lt;br /&gt;
Internally, midas uses the RPC system to handle run transitions, ODB hotlinks and more. &lt;br /&gt;
&lt;br /&gt;
For example, if you start a run using the web interface, mhttpd (the webserver) will send an &amp;lt;code&amp;gt;RPC_RC_TRANSITION&amp;lt;/code&amp;gt; message to clients that want to handle the begin-of-run transition. Each client responds with whether it&#039;s okay for the transition to proceed.&lt;br /&gt;
&lt;br /&gt;
Any clients that are running on a remote server use RPC for all their communications with midas. The special mserver program (running on the main host) is responsible for handling these requests. For example, whereas a local client can read an ODB value directly from shared memory on the main host; a remote client instead issues an RPC call to mserver, which reads the value from shared memory and returns it to the caller.&lt;br /&gt;
&lt;br /&gt;
At the very lowest level, each program checks if it has any outstanding RPC requests to handle as part of the &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; function, which should be called periodically by every midas client.&lt;br /&gt;
&lt;br /&gt;
= Use in user code - JRPC =&lt;br /&gt;
&lt;br /&gt;
One of the RPC commands, &amp;lt;code&amp;gt;RPC_JRPC&amp;lt;/code&amp;gt;, is very flexible and allows the user to easily integrate custom commands into their programs. There are interfaces in C++, python and javascript. &lt;br /&gt;
&lt;br /&gt;
The calling code specifies:&lt;br /&gt;
* The program it wants to talk to&lt;br /&gt;
* The command it wants to issue (generally a short string)&lt;br /&gt;
* Some arguments (either plain text or JSON)&lt;br /&gt;
&lt;br /&gt;
The client then:&lt;br /&gt;
* Decides what to do based on the command and arguments&lt;br /&gt;
* Responds with a status code and text message (either plain text or JSON)&lt;br /&gt;
&lt;br /&gt;
Some example uses of this system are:&lt;br /&gt;
* Have a client turn some equipment off when the user clicks a button on a webpage.&lt;br /&gt;
* Offload some complex calculations to another program (e.g. if your main client is in C++ but it&#039;s much easier to implement the calculations in python).&lt;br /&gt;
* Implement a web-based plot display that gets data as JSON from a python/C++ client&lt;br /&gt;
* Much much more...&lt;br /&gt;
&lt;br /&gt;
== Benefits vs ODB hotlinks ==&lt;br /&gt;
&lt;br /&gt;
Often users implement inter-process communication using ODB hotlinks (where a client can say &amp;quot;tell me whenever /Path/to/something in the ODB changes&amp;quot;). Although this works for simple cases, the JRPC system is much more flexible and robust.&lt;br /&gt;
&lt;br /&gt;
* Much easier to specify arguments. Avoids race conditions if you were to use multiple ODB keys to tell the target what to do.&lt;br /&gt;
* There is no &amp;quot;competition&amp;quot; for the ODB key(s). Multiple clients can call the same RPC without interfering with each other.&lt;br /&gt;
* More flexible response format as you can return a (very) long string. Alternatives would be to put the response in an ODB value (limited maximum length) or in a file on disk (extra overhead, and possibly annoying to deal with stale files over NFS).&lt;br /&gt;
&lt;br /&gt;
== Registering your JRPC handler ==&lt;br /&gt;
&lt;br /&gt;
If you want other midas clients to be able to talk to you, you need to register an RPC handler. You specify the RPC you&#039;re listening for (&amp;lt;code&amp;gt;RPC_JRPC&amp;lt;/code&amp;gt; in this case) and a function that midas should call.&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
&lt;br /&gt;
The midas function to use is:&lt;br /&gt;
&lt;br /&gt;
 // * id - RPC_JRPC in this case&lt;br /&gt;
 // * func - Function to call (see below)&lt;br /&gt;
 INT cm_register_function(INT id, INT(*func)(INT, void **))&lt;br /&gt;
&lt;br /&gt;
The callback function (&amp;lt;code&amp;gt;func&amp;lt;/code&amp;gt; in the above example) should look something like the below:&lt;br /&gt;
&lt;br /&gt;
 // * index is the RPC id (RPC_JRPC in our case).&lt;br /&gt;
 // * prpc_param is a list of pointers that we need to cast to the appropriate types:&lt;br /&gt;
 //   - [0] is a C-string containing the &amp;quot;command&amp;quot;&lt;br /&gt;
 //   - [1] is a C-string containing the &amp;quot;arguments&amp;quot;&lt;br /&gt;
 //   - [2] is a C-string where you can write a response&lt;br /&gt;
 //   - [3] is an integer stating the maximum response length the caller expects&lt;br /&gt;
 // * The function should return SUCCESS if everything is okay.&lt;br /&gt;
 INT my_rpc_callback(INT index, void *prpc_param[]) {&lt;br /&gt;
   const char* cmd  = CSTRING(0);&lt;br /&gt;
   const char* args = CSTRING(1);&lt;br /&gt;
   char* return_buf = CSTRING(2);&lt;br /&gt;
   int   return_max_length = CINT(3);&lt;br /&gt;
 &lt;br /&gt;
   if (strcmp(cmd, &amp;quot;add_one&amp;quot;) == 0) {&lt;br /&gt;
      // Silly example that parses `args` as a number and returns x+1 in return_buf&lt;br /&gt;
      int val = atoi(args);&lt;br /&gt;
      val += 1;&lt;br /&gt;
      sprintf(return_buf, &amp;quot;%d&amp;quot;, val);&lt;br /&gt;
   } else {&lt;br /&gt;
      // Up to you how to handle error conditions - report in return_buf or via return value.&lt;br /&gt;
      sprintf(return_buf, &amp;quot;err&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 cm_register_function(RPC_JRPC, my_rpc_callback);&lt;br /&gt;
&lt;br /&gt;
=== C++ (TMFE) ===&lt;br /&gt;
&lt;br /&gt;
In the [[Frontend_user_code_(object_oriented_-_TMFE)|TMFE]] framework, an RPC handler has already been registered.&lt;br /&gt;
&lt;br /&gt;
You should implement the &amp;lt;code&amp;gt;HandleRpc()&amp;lt;/code&amp;gt; function in your class that derives from &amp;lt;code&amp;gt;TMFeEquipment&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  TMFeResult HandleRpc(const char* cmd, const char* args, std::string&amp;amp; result) {&lt;br /&gt;
     if (strcmp(cmd, &amp;quot;add_one&amp;quot;) == 0) {&lt;br /&gt;
        // Silly example that parses `args` as a number and returns x+1 as the result&lt;br /&gt;
        int val = atoi(args);&lt;br /&gt;
        val += 1;&lt;br /&gt;
        sprintf(return_buf, &amp;quot;%d&amp;quot;, val);&lt;br /&gt;
     } else {&lt;br /&gt;
        // In TMFE, you must report errors via the result.&lt;br /&gt;
        result = &amp;quot;err&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
  &lt;br /&gt;
     return TMFeOk();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
&lt;br /&gt;
The [[Python|python]] interface is very similar to the C++ interface for registering RPC callbacks.&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;midas.client&amp;lt;/code&amp;gt; you call &amp;lt;code&amp;gt;register_jrpc_callback(callback, return_success_even_on_failure)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The callback function should be defined as &amp;lt;code&amp;gt;def rpc_handler(client, cmd, args, max_len)&amp;lt;/code&amp;gt;, where the arguments are:&lt;br /&gt;
* client (midas.client.MidasClient)&lt;br /&gt;
* cmd (str) - The command user wants to execute&lt;br /&gt;
* args (str) - Other arguments the user supplied&lt;br /&gt;
* max_len (int) - The maximum string length the user accepts in the return value&lt;br /&gt;
&lt;br /&gt;
The callback function should return either a tuple of (int, str) or just an int. The integer should be a status code from midas.status_codes. The string, if present, should be any text that should be returned to the caller.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;return_success_even_on_failure&amp;lt;/code&amp;gt; argument to &amp;lt;code&amp;gt;register_jrpc_callback&amp;lt;/code&amp;gt; deals with a subtlety regarding mjsonrpc (the web interface for calling JRPC functions), as it does not return any message if the status code isn&#039;t &amp;quot;SUCCESS&amp;quot;. This can be annoying if you want to show a specific error message to the user, and not have them trawl through the midas message log.  If you set this parameter to False, then you get the &amp;quot;normal&amp;quot; behaviour, where the returned status code and result string are exactly what is returned from the callback function. If you set this parameter to True, then the status code will always be &amp;quot;SUCCESS&amp;quot;, and the result string will be JSON-encoded text of the form &amp;lt;code&amp;gt;{&amp;quot;code&amp;quot;: 604, &amp;quot;msg&amp;quot;: &amp;quot;Some error message&amp;quot;}&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Example code is:&lt;br /&gt;
&lt;br /&gt;
 def my_rpc_callback(client, cmd, args, max_len):&lt;br /&gt;
    ret_int = midas.status_codes[&amp;quot;SUCCESS&amp;quot;]&lt;br /&gt;
    ret_str = &amp;quot;&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    if cmd == &amp;quot;add_one&amp;quot;:&lt;br /&gt;
        val = int(args)&lt;br /&gt;
        val = val + 1&lt;br /&gt;
        &lt;br /&gt;
        ret_str = str(val)&lt;br /&gt;
    else:&lt;br /&gt;
        ret_int = midas.status_codes[&amp;quot;FE_ERR_DRIVER&amp;quot;]&lt;br /&gt;
        ret_str = &amp;quot;err&amp;quot;    &lt;br /&gt;
        &lt;br /&gt;
    return (ret_int, ret_str)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    client = midas.client.MidasClient(&amp;quot;pytest&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
    # Register our function.&lt;br /&gt;
    client.register_jrpc_callback(my_rpc_callback, True)&lt;br /&gt;
    &lt;br /&gt;
    # Spin forever. Program can be killed by Ctrl+C or&lt;br /&gt;
    # &amp;quot;Stop Program&amp;quot; through mhttpd.&lt;br /&gt;
    while True:&lt;br /&gt;
        client.communicate(10) &lt;br /&gt;
&lt;br /&gt;
== Calling JRPC on other clients ==&lt;br /&gt;
&lt;br /&gt;
In all languages, you specify which program you want to talk to, a command and some arguments. You will then be returned a status code and a message string. As you are the person writing both sides of the JRPC communication, it is up to you to define what format the arguments and returned message take (e.g. plain text or JSON).&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
&lt;br /&gt;
In C++ you use &amp;lt;code&amp;gt;cm_connect_client()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;rpc_client_call()&amp;lt;/code&amp;gt; to call JRPC on another program.&lt;br /&gt;
&lt;br /&gt;
 INT status = SUCCESS;&lt;br /&gt;
 &lt;br /&gt;
 // First connect to the other client&lt;br /&gt;
 HNDLE hConn = 0; &lt;br /&gt;
 status = cm_connect_client(&amp;quot;other_program&amp;quot;, &amp;amp;hConn);&lt;br /&gt;
 &lt;br /&gt;
 if (status != SUCCESS) {return;}&lt;br /&gt;
 &lt;br /&gt;
 // Then build arguments and call the RPC&lt;br /&gt;
 std::string cmd = &amp;quot;add_one&amp;quot;;&lt;br /&gt;
 std::string args = &amp;quot;6&amp;quot;;&lt;br /&gt;
 char buf[1000];&lt;br /&gt;
 int buf_length = 1000;&lt;br /&gt;
 &lt;br /&gt;
 status = rpc_client_call(hConn, RPC_JRPC, cmd.c_str(), args.c_str(), buf, buf_length);&lt;br /&gt;
 &lt;br /&gt;
 if (status != SUCCESS) {return;}&lt;br /&gt;
 &lt;br /&gt;
 // &#039;buf&#039; should contain &amp;quot;7&amp;quot; in our example.&lt;br /&gt;
&lt;br /&gt;
Note that [https://github.com/nlohmann/json/raw/develop/single_include/nlohmann/json.hpp json.hpp] is a very useful tool if you want to use JSON in your C++ code.&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
&lt;br /&gt;
In python you use the &amp;lt;code&amp;gt;connect_to_other_client()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;jrpc_client_call()&amp;lt;/code&amp;gt; functions in &amp;lt;code&amp;gt;midas.client&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 client = midas.client.MidasClient(&amp;quot;my_program&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 # First connect to the other client&lt;br /&gt;
 other = client.connect_to_other_client(&amp;quot;other_program&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 # Then build arguments and call the RPC&lt;br /&gt;
 command = &amp;quot;add_one&amp;quot;&lt;br /&gt;
 args = &amp;quot;6&amp;quot;&lt;br /&gt;
 retval = client.jrpc_client_call(other, command, args)&lt;br /&gt;
 &lt;br /&gt;
 # Note that jrpc_client_call() also takes an optional parameter &amp;quot;max_len&amp;quot; that defaults to 1024 bytes).&lt;br /&gt;
 # Any responses from the client that are longer than this will be truncated.&lt;br /&gt;
 &lt;br /&gt;
 # retval should now contain &amp;quot;7&amp;quot;&lt;br /&gt;
 # An exception will be raised if the other program returned a non-SUCCESS status.&lt;br /&gt;
&lt;br /&gt;
=== Javascript ===&lt;br /&gt;
&lt;br /&gt;
In javascript you use the &amp;lt;code&amp;gt;mjsonrpc_call&amp;lt;/code&amp;gt; function.&lt;br /&gt;
&lt;br /&gt;
 // In JS, client to connect to is specified as a parameter&lt;br /&gt;
 let params = Object();&lt;br /&gt;
 params.client_name = &amp;quot;other_program&amp;quot;;&lt;br /&gt;
 params.cmd = &amp;quot;add_one&amp;quot;;&lt;br /&gt;
 params.args = &amp;quot;6&amp;quot;;&lt;br /&gt;
 params.max_reply_length = 100000; // Optional, defaults to 1024 bytes. Any responses longer than this will be truncated.&lt;br /&gt;
 &lt;br /&gt;
 // Call RPC asynchronously. Returns a Promise that you deal with using &amp;quot;then()&amp;quot;&lt;br /&gt;
 mjsonrpc_call(&amp;quot;jrpc&amp;quot;, params).then(function(rpc) {&lt;br /&gt;
    if (rpc.result.status != 1) {&lt;br /&gt;
       // Other program reported a problem&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    // rpc.result.reply should contain &amp;quot;7&amp;quot;&lt;br /&gt;
 }).catch(function(error) {&lt;br /&gt;
    mjsonrpc_error_alert(error);&lt;br /&gt;
 });&lt;br /&gt;
&lt;br /&gt;
More documentation on the mjsonrpc commands can be found in the [[Mjsonrpc#Schema_(List_of_all_RPC_methods)|mjsonrpc schema]].&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Remote_Procedure_Calls_(RPC)&amp;diff=3463</id>
		<title>Remote Procedure Calls (RPC)</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Remote_Procedure_Calls_(RPC)&amp;diff=3463"/>
		<updated>2025-01-20T23:01:11Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Javascript */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= RPC in a nutshell =&lt;br /&gt;
&lt;br /&gt;
Midas uses a Remote Procedure Call (RPC) system to allow different midas clients to talk to each other directly. One client can issue a command to another, and the second client will perform some action and send a response back to the caller.&lt;br /&gt;
&lt;br /&gt;
= Internal usage in midas =&lt;br /&gt;
&lt;br /&gt;
Internally, midas uses the RPC system to handle run transitions, ODB hotlinks and more. &lt;br /&gt;
&lt;br /&gt;
For example, if you start a run using the web interface, mhttpd (the webserver) will send an &amp;lt;code&amp;gt;RPC_RC_TRANSITION&amp;lt;/code&amp;gt; message to clients that want to handle the begin-of-run transition. Each client responds with whether it&#039;s okay for the transition to proceed.&lt;br /&gt;
&lt;br /&gt;
Any clients that are running on a remote server use RPC for all their communications with midas. The special mserver program (running on the main host) is responsible for handling these requests. For example, whereas a local client can read an ODB value directly from shared memory on the main host; a remote client instead issues an RPC call to mserver, which reads the value from shared memory and returns it to the caller.&lt;br /&gt;
&lt;br /&gt;
At the very lowest level, each program checks if it has any outstanding RPC requests to handle as part of the &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; function, which should be called periodically by every midas client.&lt;br /&gt;
&lt;br /&gt;
= Use in user code - JRPC =&lt;br /&gt;
&lt;br /&gt;
One of the RPC commands, &amp;lt;code&amp;gt;RPC_JRPC&amp;lt;/code&amp;gt;, is very flexible and allows the user to easily integrate custom commands into their programs. There are interfaces in C++, python and javascript. &lt;br /&gt;
&lt;br /&gt;
The calling code specifies:&lt;br /&gt;
* The program it wants to talk to&lt;br /&gt;
* The command it wants to issue (generally a short string)&lt;br /&gt;
* Some arguments (either plain text or JSON)&lt;br /&gt;
&lt;br /&gt;
The client then:&lt;br /&gt;
* Decides what to do based on the command and arguments&lt;br /&gt;
* Responds with a status code and text message (either plain text or JSON)&lt;br /&gt;
&lt;br /&gt;
Some example uses of this system are:&lt;br /&gt;
* Have a client turn some equipment off when the user clicks a button on a webpage.&lt;br /&gt;
* Offload some complex calculations to another program (e.g. if your main client is in C++ but it&#039;s much easier to implement the calculations in python).&lt;br /&gt;
* Implement a web-based plot display that gets data as JSON from a python/C++ client&lt;br /&gt;
* Much much more...&lt;br /&gt;
&lt;br /&gt;
== Benefits vs ODB hotlinks ==&lt;br /&gt;
&lt;br /&gt;
Often users implement inter-process communication using ODB hotlinks (where a client can say &amp;quot;tell me whenever /Path/to/something in the ODB changes&amp;quot;). Although this works for simple cases, the JRPC system is much more flexible and robust.&lt;br /&gt;
&lt;br /&gt;
* Much easier to specify arguments. Avoids race conditions if you were to use multiple ODB keys to tell the target what to do.&lt;br /&gt;
* There is no &amp;quot;competition&amp;quot; for the ODB key(s). Multiple clients can call the same RPC without interfering with each other.&lt;br /&gt;
* More flexible response format as you can return a (very) long string. Alternatives would be to put the response in an ODB value (limited maximum length) or in a file on disk (extra overhead, and possibly annoying to deal with stale files over NFS).&lt;br /&gt;
&lt;br /&gt;
== Registering your JRPC handler ==&lt;br /&gt;
&lt;br /&gt;
If you want other midas clients to be able to talk to you, you need to register an RPC handler. You specify the RPC you&#039;re listening for (&amp;lt;code&amp;gt;RPC_JRPC&amp;lt;/code&amp;gt; in this case) and a function that midas should call.&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
&lt;br /&gt;
The midas function to use is:&lt;br /&gt;
&lt;br /&gt;
 // * id - RPC_JRPC in this case&lt;br /&gt;
 // * func - Function to call (see below)&lt;br /&gt;
 INT cm_register_function(INT id, INT(*func)(INT, void **))&lt;br /&gt;
&lt;br /&gt;
The callback function (&amp;lt;code&amp;gt;func&amp;lt;/code&amp;gt; in the above example) should look something like the below:&lt;br /&gt;
&lt;br /&gt;
 // * index is the RPC id (RPC_JRPC in our case).&lt;br /&gt;
 // * prpc_param is a list of pointers that we need to cast to the appropriate types:&lt;br /&gt;
 //   - [0] is a C-string containing the &amp;quot;command&amp;quot;&lt;br /&gt;
 //   - [1] is a C-string containing the &amp;quot;arguments&amp;quot;&lt;br /&gt;
 //   - [2] is a C-string where you can write a response&lt;br /&gt;
 //   - [3] is an integer stating the maximum response length the caller expects&lt;br /&gt;
 // * The function should return SUCCESS if everything is okay.&lt;br /&gt;
 INT my_rpc_callback(INT index, void *prpc_param[]) {&lt;br /&gt;
   const char* cmd  = CSTRING(0);&lt;br /&gt;
   const char* args = CSTRING(1);&lt;br /&gt;
   char* return_buf = CSTRING(2);&lt;br /&gt;
   int   return_max_length = CINT(3);&lt;br /&gt;
 &lt;br /&gt;
   if (strcmp(cmd, &amp;quot;add_one&amp;quot;) == 0) {&lt;br /&gt;
      // Silly example that parses `args` as a number and returns x+1 in return_buf&lt;br /&gt;
      int val = atoi(args);&lt;br /&gt;
      val += 1;&lt;br /&gt;
      sprintf(return_buf, &amp;quot;%d&amp;quot;, val);&lt;br /&gt;
   } else {&lt;br /&gt;
      // Up to you how to handle error conditions - report in return_buf or via return value.&lt;br /&gt;
      sprintf(return_buf, &amp;quot;err&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 cm_register_function(RPC_JRPC, my_rpc_callback);&lt;br /&gt;
&lt;br /&gt;
=== C++ (TMFE) ===&lt;br /&gt;
&lt;br /&gt;
In the [[Frontend_user_code_(object_oriented_-_TMFE)|TMFE]] framework, an RPC handler has already been registered.&lt;br /&gt;
&lt;br /&gt;
You should implement the &amp;lt;code&amp;gt;HandleRpc()&amp;lt;/code&amp;gt; function in your class that derives from &amp;lt;code&amp;gt;TMFeEquipment&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  TMFeResult HandleRpc(const char* cmd, const char* args, std::string&amp;amp; result) {&lt;br /&gt;
     if (strcmp(cmd, &amp;quot;add_one&amp;quot;) == 0) {&lt;br /&gt;
        // Silly example that parses `args` as a number and returns x+1 as the result&lt;br /&gt;
        int val = atoi(args);&lt;br /&gt;
        val += 1;&lt;br /&gt;
        sprintf(return_buf, &amp;quot;%d&amp;quot;, val);&lt;br /&gt;
     } else {&lt;br /&gt;
        // In TMFE, you must report errors via the result.&lt;br /&gt;
        result = &amp;quot;err&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
  &lt;br /&gt;
     return TMFeOk();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
&lt;br /&gt;
The [[Python|python]] interface is very similar to the C++ interface for registering RPC callbacks.&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;midas.client&amp;lt;/code&amp;gt; you call &amp;lt;code&amp;gt;register_jrpc_callback(callback, return_success_even_on_failure)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The callback function should be defined as &amp;lt;code&amp;gt;def rpc_handler(client, cmd, args, max_len)&amp;lt;/code&amp;gt;, where the arguments are:&lt;br /&gt;
* client (midas.client.MidasClient)&lt;br /&gt;
* cmd (str) - The command user wants to execute&lt;br /&gt;
* args (str) - Other arguments the user supplied&lt;br /&gt;
* max_len (int) - The maximum string length the user accepts in the return value&lt;br /&gt;
&lt;br /&gt;
The callback function should return either a tuple of (int, str) or just an int. The integer should be a status code from midas.status_codes. The string, if present, should be any text that should be returned to the caller.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;return_success_even_on_failure&amp;lt;/code&amp;gt; argument to &amp;lt;code&amp;gt;register_jrpc_callback&amp;lt;/code&amp;gt; deals with a subtlety regarding mjsonrpc (the web interface for calling JRPC functions), as it does not return any message if the status code isn&#039;t &amp;quot;SUCCESS&amp;quot;. This can be annoying if you want to show a specific error message to the user, and not have them trawl through the midas message log.  If you set this parameter to False, then you get the &amp;quot;normal&amp;quot; behaviour, where the returned status code and result string are exactly what is returned from the callback function. If you set this parameter to True, then the status code will always be &amp;quot;SUCCESS&amp;quot;, and the result string will be JSON-encoded text of the form &amp;lt;code&amp;gt;{&amp;quot;code&amp;quot;: 604, &amp;quot;msg&amp;quot;: &amp;quot;Some error message&amp;quot;}&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Example code is:&lt;br /&gt;
&lt;br /&gt;
 def my_rpc_callback(client, cmd, args, max_len):&lt;br /&gt;
    ret_int = midas.status_codes[&amp;quot;SUCCESS&amp;quot;]&lt;br /&gt;
    ret_str = &amp;quot;&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    if cmd == &amp;quot;add_one&amp;quot;:&lt;br /&gt;
        val = int(args)&lt;br /&gt;
        val = val + 1&lt;br /&gt;
        &lt;br /&gt;
        ret_str = str(val)&lt;br /&gt;
    else:&lt;br /&gt;
        ret_int = midas.status_codes[&amp;quot;FE_ERR_DRIVER&amp;quot;]&lt;br /&gt;
        ret_str = &amp;quot;err&amp;quot;    &lt;br /&gt;
        &lt;br /&gt;
    return (ret_int, ret_str)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    client = midas.client.MidasClient(&amp;quot;pytest&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
    # Register our function.&lt;br /&gt;
    client.register_jrpc_callback(my_rpc_callback, True)&lt;br /&gt;
    &lt;br /&gt;
    # Spin forever. Program can be killed by Ctrl+C or&lt;br /&gt;
    # &amp;quot;Stop Program&amp;quot; through mhttpd.&lt;br /&gt;
    while True:&lt;br /&gt;
        client.communicate(10) &lt;br /&gt;
&lt;br /&gt;
== Calling JRPC on other clients ==&lt;br /&gt;
&lt;br /&gt;
In all languages, you specify which program you want to talk to, a command and some arguments. You will then be returned a status code and a message string. As you are the person writing both sides of the JRPC communication, it is up to you to define what format the arguments and returned message take (e.g. plain text or JSON).&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
&lt;br /&gt;
In C++ you use &amp;lt;code&amp;gt;cm_connect_client()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;rpc_client_call()&amp;lt;/code&amp;gt; to call JRPC on another program.&lt;br /&gt;
&lt;br /&gt;
 INT status = SUCCESS;&lt;br /&gt;
 &lt;br /&gt;
 // First connect to the other client&lt;br /&gt;
 HNDLE hConn = 0; &lt;br /&gt;
 status = cm_connect_client(&amp;quot;other_program&amp;quot;, &amp;amp;hConn);&lt;br /&gt;
 &lt;br /&gt;
 if (status != SUCCESS) {return;}&lt;br /&gt;
 &lt;br /&gt;
 // Then build arguments and call the RPC&lt;br /&gt;
 std::string cmd = &amp;quot;add_one&amp;quot;;&lt;br /&gt;
 std::string args = &amp;quot;6&amp;quot;;&lt;br /&gt;
 char buf[1000];&lt;br /&gt;
 int buf_length = 1000;&lt;br /&gt;
 &lt;br /&gt;
 status = rpc_client_call(hConn, RPC_JRPC, cmd.c_str(), args.c_str(), buf, buf_length);&lt;br /&gt;
 &lt;br /&gt;
 if (status != SUCCESS) {return;}&lt;br /&gt;
 &lt;br /&gt;
 // &#039;buf&#039; should contain &amp;quot;7&amp;quot; in our example.&lt;br /&gt;
&lt;br /&gt;
Note that [https://github.com/nlohmann/json/raw/develop/single_include/nlohmann/json.hpp json.hpp] is a very useful tool if you want to use JSON in your C++ code.&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
&lt;br /&gt;
In python you use the &amp;lt;code&amp;gt;connect_to_other_client()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;jrpc_client_call()&amp;lt;/code&amp;gt; functions in &amp;lt;code&amp;gt;midas.client&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 client = midas.client.MidasClient(&amp;quot;my_program&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 # First connect to the other client&lt;br /&gt;
 other = client.connect_to_other_client(&amp;quot;other_program&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 # Then build arguments and call the RPC&lt;br /&gt;
 command = &amp;quot;add_one&amp;quot;&lt;br /&gt;
 args = &amp;quot;6&amp;quot;&lt;br /&gt;
 retval = client.jrpc_client_call(other, command, args)&lt;br /&gt;
 &lt;br /&gt;
 # retval should now contain &amp;quot;7&amp;quot;&lt;br /&gt;
 # An exception will be raised if the other program returned a non-SUCCESS status.&lt;br /&gt;
&lt;br /&gt;
=== Javascript ===&lt;br /&gt;
&lt;br /&gt;
In javascript you use the &amp;lt;code&amp;gt;mjsonrpc_call&amp;lt;/code&amp;gt; function.&lt;br /&gt;
&lt;br /&gt;
 // In JS, client to connect to is specified as a parameter&lt;br /&gt;
 let params = Object();&lt;br /&gt;
 params.client_name = &amp;quot;other_program&amp;quot;;&lt;br /&gt;
 params.cmd = &amp;quot;add_one&amp;quot;;&lt;br /&gt;
 params.args = &amp;quot;6&amp;quot;;&lt;br /&gt;
 params.max_reply_length = 100000; // Optional, defaults to 1024 bytes&lt;br /&gt;
 &lt;br /&gt;
 // Call RPC asynchronously. Returns a Promise that you deal with using &amp;quot;then()&amp;quot;&lt;br /&gt;
 mjsonrpc_call(&amp;quot;jrpc&amp;quot;, params).then(function(rpc) {&lt;br /&gt;
    if (rpc.result.status != 1) {&lt;br /&gt;
       // Other program reported a problem&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    // rpc.result.reply should contain &amp;quot;7&amp;quot;&lt;br /&gt;
 }).catch(function(error) {&lt;br /&gt;
    mjsonrpc_error_alert(error);&lt;br /&gt;
 });&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Remote_Procedure_Calls_(RPC)&amp;diff=3459</id>
		<title>Remote Procedure Calls (RPC)</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Remote_Procedure_Calls_(RPC)&amp;diff=3459"/>
		<updated>2025-01-20T18:34:34Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= RPC in a nutshell =&lt;br /&gt;
&lt;br /&gt;
Midas uses a Remote Procedure Call (RPC) system to allow different midas clients to talk to each other directly. One client can issue a command to another, and the second client will perform some action and send a response back to the caller.&lt;br /&gt;
&lt;br /&gt;
= Internal usage in midas =&lt;br /&gt;
&lt;br /&gt;
Internally, midas uses the RPC system to handle run transitions, ODB hotlinks and more. &lt;br /&gt;
&lt;br /&gt;
For example, if you start a run using the web interface, mhttpd (the webserver) will send an &amp;lt;code&amp;gt;RPC_RC_TRANSITION&amp;lt;/code&amp;gt; message to clients that want to handle the begin-of-run transition. Each client responds with whether it&#039;s okay for the transition to proceed.&lt;br /&gt;
&lt;br /&gt;
Any clients that are running on a remote server use RPC for all their communications with midas. The special mserver program (running on the main host) is responsible for handling these requests. For example, whereas a local client can read an ODB value directly from shared memory on the main host; a remote client instead issues an RPC call to mserver, which reads the value from shared memory and returns it to the caller.&lt;br /&gt;
&lt;br /&gt;
At the very lowest level, each program checks if it has any outstanding RPC requests to handle as part of the &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; function, which should be called periodically by every midas client.&lt;br /&gt;
&lt;br /&gt;
= Use in user code - JRPC =&lt;br /&gt;
&lt;br /&gt;
One of the RPC commands, &amp;lt;code&amp;gt;RPC_JRPC&amp;lt;/code&amp;gt;, is very flexible and allows the user to easily integrate custom commands into their programs. There are interfaces in C++, python and javascript. &lt;br /&gt;
&lt;br /&gt;
The calling code specifies:&lt;br /&gt;
* The program it wants to talk to&lt;br /&gt;
* The command it wants to issue (generally a short string)&lt;br /&gt;
* Some arguments (either plain text or JSON)&lt;br /&gt;
&lt;br /&gt;
The client then:&lt;br /&gt;
* Decides what to do based on the command and arguments&lt;br /&gt;
* Responds with a status code and text message (either plain text or JSON)&lt;br /&gt;
&lt;br /&gt;
Some example uses of this system are:&lt;br /&gt;
* Have a client turn some equipment off when the user clicks a button on a webpage.&lt;br /&gt;
* Offload some complex calculations to another program (e.g. if your main client is in C++ but it&#039;s much easier to implement the calculations in python).&lt;br /&gt;
* Implement a web-based plot display that gets data as JSON from a python/C++ client&lt;br /&gt;
* Much much more...&lt;br /&gt;
&lt;br /&gt;
== Benefits vs ODB hotlinks ==&lt;br /&gt;
&lt;br /&gt;
Often users implement inter-process communication using ODB hotlinks (where a client can say &amp;quot;tell me whenever /Path/to/something in the ODB changes&amp;quot;). Although this works for simple cases, the JRPC system is much more flexible and robust.&lt;br /&gt;
&lt;br /&gt;
* Much easier to specify arguments. Avoids race conditions if you were to use multiple ODB keys to tell the target what to do.&lt;br /&gt;
* There is no &amp;quot;competition&amp;quot; for the ODB key(s). Multiple clients can call the same RPC without interfering with each other.&lt;br /&gt;
* More flexible response format as you can return a (very) long string. Alternatives would be to put the response in an ODB value (limited maximum length) or in a file on disk (extra overhead, and possibly annoying to deal with stale files over NFS).&lt;br /&gt;
&lt;br /&gt;
== Registering your JRPC handler ==&lt;br /&gt;
&lt;br /&gt;
If you want other midas clients to be able to talk to you, you need to register an RPC handler. You specify the RPC you&#039;re listening for (&amp;lt;code&amp;gt;RPC_JRPC&amp;lt;/code&amp;gt; in this case) and a function that midas should call.&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
&lt;br /&gt;
The midas function to use is:&lt;br /&gt;
&lt;br /&gt;
 // * id - RPC_JRPC in this case&lt;br /&gt;
 // * func - Function to call (see below)&lt;br /&gt;
 INT cm_register_function(INT id, INT(*func)(INT, void **))&lt;br /&gt;
&lt;br /&gt;
The callback function (&amp;lt;code&amp;gt;func&amp;lt;/code&amp;gt; in the above example) should look something like the below:&lt;br /&gt;
&lt;br /&gt;
 // * index is the RPC id (RPC_JRPC in our case).&lt;br /&gt;
 // * prpc_param is a list of pointers that we need to cast to the appropriate types:&lt;br /&gt;
 //   - [0] is a C-string containing the &amp;quot;command&amp;quot;&lt;br /&gt;
 //   - [1] is a C-string containing the &amp;quot;arguments&amp;quot;&lt;br /&gt;
 //   - [2] is a C-string where you can write a response&lt;br /&gt;
 //   - [3] is an integer stating the maximum response length the caller expects&lt;br /&gt;
 // * The function should return SUCCESS if everything is okay.&lt;br /&gt;
 INT my_rpc_callback(INT index, void *prpc_param[]) {&lt;br /&gt;
   const char* cmd  = CSTRING(0);&lt;br /&gt;
   const char* args = CSTRING(1);&lt;br /&gt;
   char* return_buf = CSTRING(2);&lt;br /&gt;
   int   return_max_length = CINT(3);&lt;br /&gt;
 &lt;br /&gt;
   if (strcmp(cmd, &amp;quot;add_one&amp;quot;) == 0) {&lt;br /&gt;
      // Silly example that parses `args` as a number and returns x+1 in return_buf&lt;br /&gt;
      int val = atoi(args);&lt;br /&gt;
      val += 1;&lt;br /&gt;
      sprintf(return_buf, &amp;quot;%d&amp;quot;, val);&lt;br /&gt;
   } else {&lt;br /&gt;
      // Up to you how to handle error conditions - report in return_buf or via return value.&lt;br /&gt;
      sprintf(return_buf, &amp;quot;err&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
 cm_register_function(RPC_JRPC, my_rpc_callback);&lt;br /&gt;
&lt;br /&gt;
=== C++ (TMFE) ===&lt;br /&gt;
&lt;br /&gt;
In the [[Frontend_user_code_(object_oriented_-_TMFE)|TMFE]] framework, an RPC handler has already been registered.&lt;br /&gt;
&lt;br /&gt;
You should implement the &amp;lt;code&amp;gt;HandleRpc()&amp;lt;/code&amp;gt; function in your class that derives from &amp;lt;code&amp;gt;TMFeEquipment&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  TMFeResult HandleRpc(const char* cmd, const char* args, std::string&amp;amp; result) {&lt;br /&gt;
     if (strcmp(cmd, &amp;quot;add_one&amp;quot;) == 0) {&lt;br /&gt;
        // Silly example that parses `args` as a number and returns x+1 as the result&lt;br /&gt;
        int val = atoi(args);&lt;br /&gt;
        val += 1;&lt;br /&gt;
        sprintf(return_buf, &amp;quot;%d&amp;quot;, val);&lt;br /&gt;
     } else {&lt;br /&gt;
        // In TMFE, you must report errors via the result.&lt;br /&gt;
        result = &amp;quot;err&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
  &lt;br /&gt;
     return TMFeOk();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
&lt;br /&gt;
The [[Python|python]] interface is very similar to the C++ interface for registering RPC callbacks.&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;midas.client&amp;lt;/code&amp;gt; you call &amp;lt;code&amp;gt;register_jrpc_callback(callback, return_success_even_on_failure)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The callback function should be defined as &amp;lt;code&amp;gt;def rpc_handler(client, cmd, args, max_len)&amp;lt;/code&amp;gt;, where the arguments are:&lt;br /&gt;
* client (midas.client.MidasClient)&lt;br /&gt;
* cmd (str) - The command user wants to execute&lt;br /&gt;
* args (str) - Other arguments the user supplied&lt;br /&gt;
* max_len (int) - The maximum string length the user accepts in the return value&lt;br /&gt;
&lt;br /&gt;
The callback function should return either a tuple of (int, str) or just an int. The integer should be a status code from midas.status_codes. The string, if present, should be any text that should be returned to the caller.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;return_success_even_on_failure&amp;lt;/code&amp;gt; argument to &amp;lt;code&amp;gt;register_jrpc_callback&amp;lt;/code&amp;gt; deals with a subtlety regarding mjsonrpc (the web interface for calling JRPC functions), as it does not return any message if the status code isn&#039;t &amp;quot;SUCCESS&amp;quot;. This can be annoying if you want to show a specific error message to the user, and not have them trawl through the midas message log.  If you set this parameter to False, then you get the &amp;quot;normal&amp;quot; behaviour, where the returned status code and result string are exactly what is returned from the callback function. If you set this parameter to True, then the status code will always be &amp;quot;SUCCESS&amp;quot;, and the result string will be JSON-encoded text of the form &amp;lt;code&amp;gt;{&amp;quot;code&amp;quot;: 604, &amp;quot;msg&amp;quot;: &amp;quot;Some error message&amp;quot;}&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Example code is:&lt;br /&gt;
&lt;br /&gt;
 def my_rpc_callback(client, cmd, args, max_len):&lt;br /&gt;
    ret_int = midas.status_codes[&amp;quot;SUCCESS&amp;quot;]&lt;br /&gt;
    ret_str = &amp;quot;&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    if cmd == &amp;quot;add_one&amp;quot;:&lt;br /&gt;
        val = int(args)&lt;br /&gt;
        val = val + 1&lt;br /&gt;
        &lt;br /&gt;
        ret_str = str(val)&lt;br /&gt;
    else:&lt;br /&gt;
        ret_int = midas.status_codes[&amp;quot;FE_ERR_DRIVER&amp;quot;]&lt;br /&gt;
        ret_str = &amp;quot;err&amp;quot;    &lt;br /&gt;
        &lt;br /&gt;
    return (ret_int, ret_str)&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    client = midas.client.MidasClient(&amp;quot;pytest&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
    # Register our function.&lt;br /&gt;
    client.register_jrpc_callback(my_rpc_callback, True)&lt;br /&gt;
    &lt;br /&gt;
    # Spin forever. Program can be killed by Ctrl+C or&lt;br /&gt;
    # &amp;quot;Stop Program&amp;quot; through mhttpd.&lt;br /&gt;
    while True:&lt;br /&gt;
        client.communicate(10) &lt;br /&gt;
&lt;br /&gt;
== Calling JRPC on other clients ==&lt;br /&gt;
&lt;br /&gt;
In all languages, you specify which program you want to talk to, a command and some arguments. You will then be returned a status code and a message string. As you are the person writing both sides of the JRPC communication, it is up to you to define what format the arguments and returned message take (e.g. plain text or JSON).&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
&lt;br /&gt;
In C++ you use &amp;lt;code&amp;gt;cm_connect_client()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;rpc_client_call()&amp;lt;/code&amp;gt; to call JRPC on another program.&lt;br /&gt;
&lt;br /&gt;
 INT status = SUCCESS;&lt;br /&gt;
 &lt;br /&gt;
 // First connect to the other client&lt;br /&gt;
 HNDLE hConn = 0; &lt;br /&gt;
 status = cm_connect_client(&amp;quot;other_program&amp;quot;, &amp;amp;hConn);&lt;br /&gt;
 &lt;br /&gt;
 if (status != SUCCESS) {return;}&lt;br /&gt;
 &lt;br /&gt;
 // Then build arguments and call the RPC&lt;br /&gt;
 std::string cmd = &amp;quot;add_one&amp;quot;;&lt;br /&gt;
 std::string args = &amp;quot;6&amp;quot;;&lt;br /&gt;
 char buf[1000];&lt;br /&gt;
 int buf_length = 1000;&lt;br /&gt;
 &lt;br /&gt;
 status = rpc_client_call(hConn, RPC_JRPC, cmd.c_str(), args.c_str(), buf, buf_length);&lt;br /&gt;
 &lt;br /&gt;
 if (status != SUCCESS) {return;}&lt;br /&gt;
 &lt;br /&gt;
 // &#039;buf&#039; should contain &amp;quot;7&amp;quot; in our example.&lt;br /&gt;
&lt;br /&gt;
Note that [https://github.com/nlohmann/json/raw/develop/single_include/nlohmann/json.hpp json.hpp] is a very useful tool if you want to use JSON in your C++ code.&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
&lt;br /&gt;
In python you use the &amp;lt;code&amp;gt;connect_to_other_client()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;jrpc_client_call()&amp;lt;/code&amp;gt; functions in &amp;lt;code&amp;gt;midas.client&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
 client = midas.client.MidasClient(&amp;quot;my_program&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 # First connect to the other client&lt;br /&gt;
 other = client.connect_to_other_client(&amp;quot;other_program&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 # Then build arguments and call the RPC&lt;br /&gt;
 command = &amp;quot;add_one&amp;quot;&lt;br /&gt;
 args = &amp;quot;6&amp;quot;&lt;br /&gt;
 retval = client.jrpc_client_call(other, command, args)&lt;br /&gt;
 &lt;br /&gt;
 # retval should now contain &amp;quot;7&amp;quot;&lt;br /&gt;
 # An exception will be raised if the other program returned a non-SUCCESS status.&lt;br /&gt;
&lt;br /&gt;
=== Javascript ===&lt;br /&gt;
&lt;br /&gt;
In javascript you use the &amp;lt;code&amp;gt;mjsonrpc_call&amp;lt;/code&amp;gt; function.&lt;br /&gt;
&lt;br /&gt;
 // In JS, client to connect to is specified as a parameter&lt;br /&gt;
 let params = Object();&lt;br /&gt;
 params.client_name = &amp;quot;other_program&amp;quot;;&lt;br /&gt;
 params.cmd = &amp;quot;add_one&amp;quot;;&lt;br /&gt;
 params.args = &amp;quot;6&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // Call RPC asynchronously. Returns a Promise that you deal with using &amp;quot;then()&amp;quot;&lt;br /&gt;
 mjsonrpc_call(&amp;quot;jrpc&amp;quot;, params).then(function(rpc) {&lt;br /&gt;
    if (rpc.result.status != 1) {&lt;br /&gt;
       // Other program reported a problem&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    // rpc.result.reply should contain &amp;quot;7&amp;quot;&lt;br /&gt;
 }).catch(function(error) {&lt;br /&gt;
    mjsonrpc_error_alert(error);&lt;br /&gt;
 });&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3458</id>
		<title>Frontend user code</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3458"/>
		<updated>2025-01-09T22:41:32Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Compilation using CMake */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation]]&lt;br /&gt;
* [[Frontend Application]] &lt;br /&gt;
* [[Equipment List Parameters]]&lt;br /&gt;
* [[Slow Control System]]&lt;br /&gt;
* [[Equipment Flags]]&lt;br /&gt;
* [[Event Notification (Hot-Link)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This section describes the features of the user-written part of a &amp;quot;C-style&amp;quot; [[Frontend Operation#Frontend|Frontend]], referred to here as &#039;&#039;&#039;frontend.c&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
You can also write frontends using [[Frontend_user_code_(object_oriented_-_TMFE)|object-oriented C++ (TMFE)]] or [[Python]].&lt;br /&gt;
&lt;br /&gt;
The frontend system has evolved over the decades, and contains some legacy features that are not often used (e.g. interrupt handlers). For backwards-compatibility, these features are still present, but this does require a bit more boiler-plate code to be written for each frontend. We will first document every feature supported by the frontend system, then explain a slightly more user-friendly wrapper (mfed.cxx) that helps reduce the amount of boiler-plate needed for a new frontend.&lt;br /&gt;
&lt;br /&gt;
= Frontend Templates =&lt;br /&gt;
To make a user-written or custom frontend, users will usually modify one of the &#039;&#039;&#039;templates&#039;&#039;&#039; provided in the MIDAS package&lt;br /&gt;
under [[Environment Variables#MIDASSYS|$MIDASSYS]]/examples/ for their particular hardware and other requirements. Make sure to pick the closest template, e.g. if writing a [[Slow Control System|Slow Control]] frontend, pick a slow-control template. For each example frontend, a Makefile is also provided.&lt;br /&gt;
&lt;br /&gt;
A partial list of the templates provided is shown below:&lt;br /&gt;
{| style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;3&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Hardware &lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Filename&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Directory (MIDAS package)&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Purpose&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Language&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevmemodules.c&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevme.cxx&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c++/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|CAMAC &lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| Access to CAMAC modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|mtfe.c&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| &#039;&#039;&#039;Multithreaded&#039;&#039;&#039; using ring buffer&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Wiener CC-USB &lt;br /&gt;
|feccusb.cxx&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| CAMAC/USB demo &lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|RS485 bus &lt;br /&gt;
|mscb_fe.c&lt;br /&gt;
| $MIDASSYS/examples/slowcont/&lt;br /&gt;
| &#039;&#039;&#039;Slow control&#039;&#039;&#039; with [https://midas.psi.ch/mscb/ MSCB]&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|EPICS&lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/epics/&lt;br /&gt;
| &#039;&#039;&#039;Slow controls&#039;&#039;&#039;&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|Camac&lt;br /&gt;
|ebfe.c&lt;br /&gt;
| $MIDASSYS/examples/eventbuilder/&lt;br /&gt;
| &#039;&#039;&#039;Event Builder&#039;&#039;&#039; (with [[mevb]])&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|frontend.cxx&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| &#039;&#039;&#039;mfed&#039;&#039;&#039; (wrapper to simplify writing a frontend)&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The features of a typical frontend program are best explained by reference to examples of the user code &lt;br /&gt;
provided in the Midas Package. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Frontend code=&lt;br /&gt;
Note that there are several kinds of frontend used for different purposes, e.g.&lt;br /&gt;
* frontend to read VME or CAMAC hardware, using interrupt or polling (e.g. to read experimental data)&lt;br /&gt;
* multithreaded frontend where polling can be in a separate thread&lt;br /&gt;
* slow control frontend (can be multithreaded) see also [[Slow Control System]]&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Templates for many types of user frontend code are provided in the MIDAS packages (see [[#Frontend Templates]]). &lt;br /&gt;
&lt;br /&gt;
The following sections refer to these templates.&lt;br /&gt;
Most of the examples are taken from the [https://bitbucket.org/tmidas/midas/src/develop/examples/basic/largefe.cxx largefe.cxx example]. Documentation on the MIDAS library subroutines to access the ODB (some of which are used in the examples below) can be found in the [[ODB_Access_and_Use|ODB access page]].&lt;br /&gt;
&lt;br /&gt;
The user frontend code is then compiled and linked with the system part and the MIDAS library &lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Task]]).  Makefiles are provided with the templates that can be modified as needed.&lt;br /&gt;
&lt;br /&gt;
== Access to command line parameters ==&lt;br /&gt;
The function &#039;&#039;&#039;mfe_get_args&#039;&#039;&#039; gives access to the [[Frontend Application|Frontend]] command line parameters. &lt;br /&gt;
&lt;br /&gt;
This example shows how to use it in the frontend user code:&lt;br /&gt;
&lt;br /&gt;
  int argc;&lt;br /&gt;
  char **argv;&lt;br /&gt;
  mfe_get_args(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
  for (int i = 0; i &amp;lt; argc; i++) {&lt;br /&gt;
     // Use argv[i] &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
See [[Frontend_Application#Arguments|Frontend Application Arguments]] for the arguments that are handled automatically by the frontend system.&lt;br /&gt;
&lt;br /&gt;
==Include files==&lt;br /&gt;
The following example (from template frontend file &#039;&#039;$MIDASSYS/examples/basic/largefe.cxx&#039;&#039;) shows the standard include files needed for a frontend. The user may add any other include files as needed (e.g. those needed for VME access). &lt;br /&gt;
&lt;br /&gt;
Some legacy frontends may include the &#039;&#039;&#039;[[ODB#experim.h include file|experim.h]]&#039;&#039;&#039; file. This was an old way of accessing the ODB, but was not very user-friendly when the ODB structure needed to change.&lt;br /&gt;
&lt;br /&gt;
The example below shows a typical list of include files for a frontend:&lt;br /&gt;
 &lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
 #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;div id=&amp;quot;Frontend Name&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
==Global Declarations==&lt;br /&gt;
The following example (from template frontend file fevmemodules.c - see [[#Frontend Templates]]) shows the global declaration. The declarations are system wide.  Some may be changed to suit the user, but none should not be removed.&lt;br /&gt;
&lt;br /&gt;
; frontend_name  : This value can be modified to reflect the purpose of the code &lt;br /&gt;
; frontend_call_loop : If set to TRUE, the function frontend_loop() gets called very frequently. If FALSE, frontend_loop() does not run. The user can add suitable code to this routine if desired (e.g. to check for a condition).&lt;br /&gt;
; display_period : The time interval (defined in milliseconds) between the refresh of a frontend status display. The value of zero disables the display. If the frontend is started in the background with the display enabled, the stdout should be redirected to the null device to prevent the process from hanging.&lt;br /&gt;
; max_event_size : specifies the maximum size (in bytes) of the expected event.&lt;br /&gt;
; event_buffer_size : specifies the maximum size (in bytes) of the buffer to be allocated by the system.&lt;br /&gt;
; equipment_common_overwrite : whether the definitions in the EQUIPMENT struct override those in the /Equipment/largefe/Common section of the ODB. If FALSE, the values in the struct will be used the first time the program runs, then the ODB values will be used afterwards (e.g. allowing you to change the period of a [[#Event_Types_and_Triggers|periodic equipment]] via the ODB).&lt;br /&gt;
&lt;br /&gt;
See below for an example of global declarations from a frontend.&lt;br /&gt;
&lt;br /&gt;
 /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
 /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
 const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 /* The frontend file name, don&#039;t change it */&lt;br /&gt;
 const char *frontend_file_name = __FILE__;&lt;br /&gt;
 &lt;br /&gt;
 /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 &lt;br /&gt;
 /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
 INT display_period = 0;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size produced by this frontend */&lt;br /&gt;
 INT max_event_size = 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
 INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
 &lt;br /&gt;
 /* buffer size to hold events */&lt;br /&gt;
 INT event_buffer_size = 10 * 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* whether the values in EQUIPMENT struct override ODB values */&lt;br /&gt;
 BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==System Function Declarations==&lt;br /&gt;
&lt;br /&gt;
These lines declare the pre-defined system functions which should be present. &lt;br /&gt;
&lt;br /&gt;
 INT frontend_init();&lt;br /&gt;
 INT frontend_exit();&lt;br /&gt;
 INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
 INT end_of_run(INT run_number, char *error);&lt;br /&gt;
 INT pause_run(INT run_number, char *error);&lt;br /&gt;
 INT resume_run(INT run_number, char *error);&lt;br /&gt;
 INT frontend_loop();&lt;br /&gt;
&lt;br /&gt;
==Readout Function Declarations==&lt;br /&gt;
Following the previous group is a second group of declarations, which define the readout functions. These depend on the defined equipments, and run when the respective equipment is triggered. In this example, one equipment will be defined, so there is one declaration. The user functions will be described in detail in later sections. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off);&lt;br /&gt;
&lt;br /&gt;
If using an interrupt, callback function prototypes are also included&lt;br /&gt;
&lt;br /&gt;
 extern void interrupt_routine(void);&lt;br /&gt;
 void register_cnaf_callback(int debug);&lt;br /&gt;
&lt;br /&gt;
==Equipment List==&lt;br /&gt;
&lt;br /&gt;
This list of structs defines the behaviour of your frontend (e.g. [[#Event_Types_and_Triggers|periodic or polled equipment]]). See [[Equipment List Parameters|Equipment List]] for full documentation of the entries.&lt;br /&gt;
&lt;br /&gt;
Note that these are the default values for each equipment (used the very first time a frontend is run). If [[#Global declarations|equipment_common_overwrite]] is FALSE, then some of the values will subsequently be read from the ODB instead. If [[#Global declarations|equipment_common_overwrite]] is TRUE, then the ODB will be updated with the coded values each time the program runs.&lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;large&amp;quot;,                   /* equipment name */&lt;br /&gt;
     {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
      &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
      EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
      0,                       /* event source */&lt;br /&gt;
      &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
      TRUE,                    /* enabled */&lt;br /&gt;
      RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
      2000,                    /* read every 2 sec */&lt;br /&gt;
      0,                       /* stop run after this event limit */&lt;br /&gt;
      0,                       /* number of sub events */&lt;br /&gt;
      0,                       /* log history */&lt;br /&gt;
      &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
      read_large_event,        /* readout routine */&lt;br /&gt;
      NULL, NULL,              /* keep null */&lt;br /&gt;
      NULL,                    /* init string */&lt;br /&gt;
   },&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;&amp;quot;}&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the case of a polled equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Trigger&amp;quot;,            // equipment name&lt;br /&gt;
        {&lt;br /&gt;
          ...&lt;br /&gt;
          &amp;lt;b&amp;gt;EQ_POLLED&amp;lt;/b&amp;gt;,          // equipment type&lt;br /&gt;
          ...&lt;br /&gt;
          500,                // poll for 500ms &lt;br /&gt;
          ...&lt;br /&gt;
          &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
          read_my_event,    // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the case of a periodic equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Scaler&amp;quot;,           // equipment name&lt;br /&gt;
         {    &lt;br /&gt;
            ...&lt;br /&gt;
            EQ_PERIODIC     // equipment type&lt;br /&gt;
            ...&lt;br /&gt;
            10000,          // period (read every 10s)&lt;br /&gt;
            ...&lt;br /&gt;
            &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
       read_my_event,   // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sequence of Operations in the frontend==&lt;br /&gt;
&lt;br /&gt;
The following table shows the sequence of operations of the  [[Frontend Operation#Frontend|Frontend]] System functions.  These functions must be implemented in the user&#039;s code (but may be as simple as just returning &amp;lt;code&amp;gt;SUCCESS&amp;lt;/code&amp;gt; if the function is not relevant for your use case). These functions are called by &#039;&#039;mfe.cxx&#039;&#039; at the appropriate time. The System Transition functions are associated with a particular [[Run States and Transitions|Run Transition]] as shown below:&lt;br /&gt;
&lt;br /&gt;
{|  style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Transition Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Associated [[Run States and Transitions|Transition]]&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Action&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_init()&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| Runs once after system initialization, before Equipment registration. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| begin_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_START&lt;br /&gt;
| Runs after system statistics reset at each begin-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| pause_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_PAUSE&lt;br /&gt;
| Runs at each pause-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| resume_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_RESUME&lt;br /&gt;
| Runs at each resume-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| end_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_STOP&lt;br /&gt;
| Runs at each end-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_exit()&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Runs once before any Slow Control Equipment exit &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Each defined Equipment has the option to force itself to run at individual transition times (see Equipment [[Equipment List Parameters#ReadOn|ReadOn Flag]]), so that its equipment function will be called on a certain transition (or combination of transitions). &lt;br /&gt;
&lt;br /&gt;
The system transition functions all run &#039;&#039;&#039;prior to&#039;&#039;&#039; the equipment functions. This gives the system the chance to take basic action on the transition request (e.g. enable/disable interrupt) before the equipment runs. All the transition routines run with a [[Run States and Transitions#Default Transition Sequency Numbers|Transition Sequence number]] of 500 (the default). This allows users to add additional functions in the frontend that will run before or after any of the transitions (such as a prestart() or a poststop() function). See [[Run States and Transitions#Run Transition Priority|Run Transition Priority]] for more information.&lt;br /&gt;
&lt;br /&gt;
==Function frontend_init ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
No parameters.&lt;br /&gt;
&lt;br /&gt;
This function runs &#039;&#039;&#039;once only&#039;&#039;&#039; at the application startup. Users may perform hardware checking, loading/setting of global variables, mapping of required ODB structures (see [[ODB#experim.h include file|experim.h include file]]), &lt;br /&gt;
[[Event Notification (Hot-Link)#How to set up a Hot-Link|setting up hot-links]] etc. in frontend_init(), e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT frontend_init()&lt;br /&gt;
 {&lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;Initializing...&amp;quot;, &amp;quot;yellow&amp;quot;);&lt;br /&gt;
   ....                  &lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;OK&amp;quot;, &amp;quot;green&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Reporting Equipment Status===&lt;br /&gt;
If running with the webserver [[mhttpd]], a frontend can send an update to the [[Status Page]], to report on its progress, using the function set_equipment_status() (see above example). This is useful when hardware can take a long time to respond.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function begin_of_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being started&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called every time a run start transition occurs, i.e. at begin-of-run. It allows the updating of user parameters, and the loading/setup/clearing of hardware. At the exit of this function, the acquisition should be armed and ready to test the interrupt (if used), e.g. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT begin_of_run (INT runnumber, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   // Read/validate some settings from the ODB (and return FE_ERR_ODB if there&#039;s a problem).&lt;br /&gt;
   // Apply them to the hardware (and return FE_ERR_HW if there&#039;s a problem).&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Functions pause/resume_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being paused/resumed.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
These two functions are called upon &amp;quot;Pause&amp;quot; and &amp;quot;Resume&amp;quot; command respectively. Any code relevant to the upcoming run state can be included,e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT pause_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   disable_trigger();&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
                            //&lt;br /&gt;
 INT resume_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   enable_trigger();&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function end_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being ended.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called at every &amp;quot;stop run&amp;quot; transition. It provides the opportunity to disable the hardware, e.g.&lt;br /&gt;
&lt;br /&gt;
 INT end_of_run(INT run_number, char *error)&lt;br /&gt;
 {&lt;br /&gt;
   // Stop the hardware.&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_exit==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
The function runs when the frontend program is shut down. Can be used to release any locked resources like memory, communications ports etc. e.g.&lt;br /&gt;
&lt;br /&gt;
  function frontend_exit()&lt;br /&gt;
  {&lt;br /&gt;
     mvme_close(gVme);&lt;br /&gt;
     return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_loop==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
If [[#Global declarations|frontend_call_loop]] is set to TRUE, this routine is called when the frontend is idle and at least once between every event. You could use it for example to check if there has been a timeout from hardware.&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 ...&lt;br /&gt;
                                 &lt;br /&gt;
 INT frontend_loop()&lt;br /&gt;
 {&lt;br /&gt;
   // Implement any code that needs to be run very frequently&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Event Types and Triggers==&lt;br /&gt;
The frontend supports several different types of [[Frontend Operation#Frontend event triggers|event trigger]]. The event trigger type is specified by the [[Equipment Flags|Equipment Flag]]  in the  [[Equipment List Parameters|Equipment Declaration]]. Common event types are &amp;quot;polled events&amp;quot; where the Equipment Flag is  [[Equipment Flags#EQ_POLLED|EQ_POLLED]], &amp;quot;interrupts events&amp;quot; where the Flag is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]], and &amp;quot;periodic events&amp;quot; where the Flag is [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]. The name of the associated readout routine is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type.&lt;br /&gt;
&lt;br /&gt;
Polled  and interrupt events (see [[#Event Types|Event Types]]) require several extra functions to handle the hardware that periodic events do not require. These are described below.&lt;br /&gt;
&lt;br /&gt;
Note that each frontend may contain:&lt;br /&gt;
* zero or one polled equipments&lt;br /&gt;
* zero or one interrupt equipments&lt;br /&gt;
* any number of periodic equipments&lt;br /&gt;
&lt;br /&gt;
==Function poll_event==&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* INT &#039;&#039;&#039;count&#039;&#039;&#039;&lt;br /&gt;
* BOOL &#039;&#039;&#039;test&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment.&lt;br /&gt;
         &lt;br /&gt;
The user must provide suitable code in the routine poll_event(), e.g. reading a register from a VME module to see if any data is available&lt;br /&gt;
&lt;br /&gt;
 INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
 {&lt;br /&gt;
    /* Polling routine for events. Returns TRUE if event  is available. If test equals TRUE, don&#039;t return. &lt;br /&gt;
       The test flag is used to time the polling &lt;br /&gt;
     */&lt;br /&gt;
     int i;&lt;br /&gt;
     int lam = 0;&lt;br /&gt;
                                 //&lt;br /&gt;
     for (i = 0; i &amp;lt; count; i++, lam++) {&lt;br /&gt;
       lam = vmeio_CsrRead(myvme, VMEIO_BASE);&lt;br /&gt;
       if (lam)&lt;br /&gt;
         if (!test)&lt;br /&gt;
           return lam;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function interrupt_configure==&lt;br /&gt;
* INT &#039;&#039;&#039;cmd&#039;&#039;&#039; &lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* PTYPE &#039;&#039;&#039;adr&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_INTERRUPT, an interrupt configuration routine called interrupt_configure() must be provided by the user.&lt;br /&gt;
The interrupt configuration routine has the following declaration: &lt;br /&gt;
&lt;br /&gt;
 /*-- Interrupt configuration --------------------------*/&lt;br /&gt;
 INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
 {&lt;br /&gt;
  int vec = 0;&lt;br /&gt;
  switch (cmd) &lt;br /&gt;
  {&lt;br /&gt;
    case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x1);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
      mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
      mvme_interrupt_attach(myvme, INT_LEVEL, INT_VECTOR, &lt;br /&gt;
                (void *)adr, &amp;amp;myinfo);&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+0x10, INT_VECTOR);&lt;br /&gt;
      vec = mvme_read_value(myvme, VLAM_BASE+0x10);&lt;br /&gt;
      printf(&amp;quot;Interrupt Attached to 0x%x for vector:0x%x\n&amp;quot;,&lt;br /&gt;
                     adr, vec&amp;amp;0xFF);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DETACH:&lt;br /&gt;
      printf(&amp;quot;Interrupt Detach\n&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Under the four commands listed above, the user must implement the hardware operation needed to perform the requested action. In the Midas drivers directory examples can be found of such an interrupt code for CAMAC. See source code such as hyt1331.c,ces8210.c &lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user in the frontend.&lt;br /&gt;
&lt;br /&gt;
==Event Readout routine==&lt;br /&gt;
&lt;br /&gt;
An event readout routine is required for all equipment, and is responsible for sending the actual data to midas. The framework calls the event readout routine whenever an equipment has been triggered (e.g. periodicially, or because poll_event() returned TRUE for a polled equipment etc). The function is of the form&lt;br /&gt;
&lt;br /&gt;
 INT function_name ( char *pevent ... )&lt;br /&gt;
 {&lt;br /&gt;
   INT event_size;&lt;br /&gt;
   ........  // read data from hardware&lt;br /&gt;
   ........  // pack into banks depending on format&lt;br /&gt;
   ........&lt;br /&gt;
   return (event_size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
where the first argument of the readout function (pevent)  provides the pointer to the newly constructed event, and points to the first valid location for storing the data.&lt;br /&gt;
;NOTE &lt;br /&gt;
* &amp;lt;b&amp;gt;The return value is the event size&amp;lt;/b&amp;gt;, and must be the number of bytes collected in this function. This is different to almost every other function in midas (where the return value is a status code).&lt;br /&gt;
* You can return 0 if you&#039;ve decided you don&#039;t actually want to write this event.&lt;br /&gt;
* The event serial number will be incremented by one for every call to the readout routine, as long as the returned size is non-zero.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===General readout function===&lt;br /&gt;
&lt;br /&gt;
A readout function is needed to send out data. This is done using one of the supported [[Event Structure|event structures]] usually [[Event Structure#MIDAS Format Event|&amp;quot;MIDAS&amp;quot; format]] data banks.  The bank format (&amp;quot;MIDAS&amp;quot; in the example above) is declared in the [[Equipment List Parameters]]   [[Equipment List Parameters#Format|Format]] field.&lt;br /&gt;
&lt;br /&gt;
An example of a scaler readout routine read_scaler_event() where the data is read out into [[MIDAS Event Construction|MIDAS data banks]] is shown below. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
   DWORD *pddata;&lt;br /&gt;
&lt;br /&gt;
   /* init bank structure */&lt;br /&gt;
   bk_init32(pevent);&lt;br /&gt;
&lt;br /&gt;
   /* create bank (bank names must be 4 chars long) */&lt;br /&gt;
   bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
&lt;br /&gt;
   /* fill data (just dummy values in this case) */&lt;br /&gt;
   memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
   pddata += 1000000;&lt;br /&gt;
   memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
&lt;br /&gt;
   /* close the bank */&lt;br /&gt;
   bk_close(pevent, pddata);&lt;br /&gt;
&lt;br /&gt;
   /* return the number of bytes we wrote */&lt;br /&gt;
   return bk_size(pevent);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Some other examples of event readout routines in this document are&lt;br /&gt;
* [[Event Structure#FIXED Event Construction|FIXED Format event construction]]&lt;br /&gt;
* [[Super Event]] construction&lt;br /&gt;
* [[Slow Control System|Slow Control]]&lt;br /&gt;
&lt;br /&gt;
Many other examples of readout routines can be found in the [[#Frontend Templates|frontend templates]] in the MIDAS package.&lt;br /&gt;
&lt;br /&gt;
===Polled or Interrupt readout routine===&lt;br /&gt;
In the case of a [[#Polled and Interrupt Events|Polled or Interrupt event]], the content of the memory location pointed to by &#039;&#039;&#039;pevent&#039;&#039;&#039; (see [[#Event Readout routine|Event Readout routine]]) prior to its use in the readout function, contains the interrupt source bitwise register. This feature can be exploited in order to identify which hardware module has triggered the readout when multiple interrupts have been assigned to the same readout function.&lt;br /&gt;
&lt;br /&gt;
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism.&lt;br /&gt;
The  [[Equipment List Parameters|Equipment declaration]] is of the form: &lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
               //&lt;br /&gt;
    {&amp;quot;Trigger&amp;quot;,  /* equipment name */&lt;br /&gt;
       ...&lt;br /&gt;
 #ifdef USE_INT&lt;br /&gt;
       EQ_INTERRUPT, /* equipment type */&lt;br /&gt;
 #else&lt;br /&gt;
       EQ_POLLED,    /* equipment type */&lt;br /&gt;
 #endif&lt;br /&gt;
   /* interrupt source: crate 0, all stations */&lt;br /&gt;
       LAM_SOURCE(0, 0x0),&lt;br /&gt;
       ....&lt;br /&gt;
       &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,&lt;br /&gt;
     },&lt;br /&gt;
     read_trigger_event, /* readout routine */&lt;br /&gt;
     NULL, NULL,&lt;br /&gt;
     trigger_bank_list,&lt;br /&gt;
 }&lt;br /&gt;
  &lt;br /&gt;
Note that the [[Macros#CAMAC Macros|LAM_SOURCE macro]] simply codes the parameters into a bitwise register.&lt;br /&gt;
&lt;br /&gt;
The readout routine would contains code such as&lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   DWORD  *pdata;&lt;br /&gt;
 #endif&lt;br /&gt;
                        //&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   /* read ADC0 data */&lt;br /&gt;
   v792_EvtCntRead(myvme, VADC0_BASE, &amp;amp;evtcnt);&lt;br /&gt;
   ........&lt;br /&gt;
   /* Read Event */&lt;br /&gt;
   v792_EventRead(myvme, VADC0_BASE, pdata, &amp;amp;nentry);&lt;br /&gt;
   ........&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
 #endif&lt;br /&gt;
                       //&lt;br /&gt;
   ........&lt;br /&gt;
   return (size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The data will be packed into banks as described for the [[General readout function|general readout function]] above.&lt;br /&gt;
The example &amp;lt;code&amp;gt;$MIDASSYS/examples/Triumf/c/fevmemodules.c&amp;lt;/code&amp;gt; contains a complete example of read_trigger_event().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Manual Trigger==&lt;br /&gt;
Another type of [[Frontend Operation#Frontend event trigger|frontend event trigger]] supported is the &amp;quot;manual trigger&amp;quot;,  where the Equipment Flag is [[Equipment Flags#EQ_MANUAL_TRIGGER|EQ_MANUAL_TRIGGER]]. This flag means that an event can be triggered through the URL &amp;lt;code&amp;gt;?cmd=Trigger/&amp;lt;equipment_name&amp;gt;&amp;lt;/code&amp;gt; (e.g. &amp;lt;code&amp;gt;http://my.host:8080/?cmd=Trigger/MyEquipmentName&amp;lt;/code&amp;gt;). If you add this URL to the [[/Alias ODB tree|/Alias]] part of the ODB, then a link will appear on midas webpages that can be clicked to trigger an event.&lt;br /&gt;
&lt;br /&gt;
In some cases, the same readout code may be used for two types of event: a manual trigger and (say) a poll event. It is possible to determine whether the readout of an event was triggered by a manual trigger or a regular trigger by adding a call to the  [[MIDAS Event Header Macros|event header Macro]] DATA_SIZE in the readout routine:&lt;br /&gt;
&lt;br /&gt;
  flag = DATA_SIZE(pevent);&lt;br /&gt;
&lt;br /&gt;
If the result is&lt;br /&gt;
  *  flag = 0 normal call&lt;br /&gt;
  *  flag = 1 manual trigger&lt;br /&gt;
&lt;br /&gt;
It is also possible for a backend MIDAS client (such as an analyzer or custom data archiver) to trigger a manual trigger event. The client controls when an event is sent by means of&lt;br /&gt;
a  function that requests an event by triggering the event sending mechanism with a RPC call.&lt;br /&gt;
&lt;br /&gt;
With a frontend Equipment declaration of a manually triggered event of the form:&lt;br /&gt;
    &lt;br /&gt;
    { &amp;quot;Histo&amp;quot;,             /* equipment name */&lt;br /&gt;
       2, 0,                 /* event ID, trigger mask */&lt;br /&gt;
       &amp;quot;SYSTEM&amp;quot;,             /* event buffer */&lt;br /&gt;
       EQ_MANUAL_TRIG,     /* equipment type */&lt;br /&gt;
       0,    &lt;br /&gt;
       .......&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
the code fragment to manually trigger this event is:&lt;br /&gt;
&lt;br /&gt;
  int main(unsigned int argc,char **argv)&lt;br /&gt;
  {&lt;br /&gt;
      .......&lt;br /&gt;
      bm_request_event(hBufEvent, 2, TRIGGER_ALL, GET_ALL, &amp;amp;request_id, process_event_TD);&lt;br /&gt;
      .......&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
When it is time to save the data during the run, the function below is called:&lt;br /&gt;
&lt;br /&gt;
 BOOL trigger_histo_event(void)&lt;br /&gt;
 {&lt;br /&gt;
   HNDLE hconn;&lt;br /&gt;
   BOOL event_triggered;&lt;br /&gt;
                            //&lt;br /&gt;
   event_triggered = FALSE;&lt;br /&gt;
   ...................&lt;br /&gt;
   if (run_state == STATE_RUNNING) {   // Check the frontend client exists&lt;br /&gt;
        if( cm_exist(ClientName,TRUE))  {    &lt;br /&gt;
            status = cm_connect_client (ClientName, &amp;amp;hconn);&lt;br /&gt;
            if(status != RPC_SUCCESS)&lt;br /&gt;
                cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Cannot connect to frontend \&amp;quot;%s\&amp;quot; (%d)&amp;quot;,&lt;br /&gt;
                       ClientName,status);&lt;br /&gt;
            else  {     // successfully connected to frontend client&lt;br /&gt;
                status = rpc_client_call(hconn, RPC_MANUAL_TRIG, 2); // trigger a histo event&lt;br /&gt;
                if (status != CM_SUCCESS)&lt;br /&gt;
                    cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error triggering event from frontend (%d)&amp;quot;,status);&lt;br /&gt;
                else {  // successfully triggered event&lt;br /&gt;
                    event_triggered=TRUE;&lt;br /&gt;
                    status =cm_disconnect_client(hconn, FALSE);&lt;br /&gt;
                    if (status != CM_SUCCESS)&lt;br /&gt;
                        cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error disconnecting client (%d)&amp;quot;,status);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Frontend client %s not running (%d)&amp;quot;,&lt;br /&gt;
                   ClientName,status);&lt;br /&gt;
    } &lt;br /&gt;
    return(event_triggered);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Deferred Transition==&lt;br /&gt;
This option permits the user to postpone any transition issued by any requester until some condition is satisfied.&lt;br /&gt;
For example:&lt;br /&gt;
* It may not be advisable to pause or stop a run until some hardware has turned off a particular valve. &lt;br /&gt;
* The start of the acquisition system should be postponed until the beam rate has been stable for a given period of time.&lt;br /&gt;
* While active, a particular acquisition system should not be interrupted until the &amp;quot;cycle&amp;quot; is completed.&lt;br /&gt;
&lt;br /&gt;
In these examples, any application having access to the state of the hardware can register to be a &amp;quot;transition Deferred&amp;quot; client. The MIDAS system will then catch any transition request and postpone the trigger of such a transition until the condition  is satisfied.&lt;br /&gt;
&lt;br /&gt;
The Deferred transition requires 3 steps for setup&lt;br /&gt;
# Register for the deferred transition &lt;br /&gt;
# Provide a callback function to serve the deferred transition &lt;br /&gt;
# Implement the condition code &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note that you should only have ONE frontend that defines a deferred transition&amp;lt;/b&amp;gt; (as this niche feature that was implemented with this assumption in mind...).&lt;br /&gt;
&lt;br /&gt;
The following example demonstrates this process:&lt;br /&gt;
&lt;br /&gt;
  BOOL transition_PS_requested=FALSE; // global&lt;br /&gt;
                                              //&lt;br /&gt;
  INT frontend_init()&lt;br /&gt;
  {&lt;br /&gt;
    // register for deferred transition&lt;br /&gt;
                                              //&lt;br /&gt;
    cm_register_deferred_transition(TR_STOP, wait_end_cycle);&lt;br /&gt;
    cm_register_deferred_transition(TR_PAUSE, wait_end_cycle);&lt;br /&gt;
    ...  &lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */ &lt;br /&gt;
  //-- Deferred transition callback&lt;br /&gt;
  BOOL wait_end_cycle(int transition, BOOL first)&lt;br /&gt;
  {&lt;br /&gt;
    if (first) {&lt;br /&gt;
      transition_PS_requested = TRUE;&lt;br /&gt;
      return FALSE;&lt;br /&gt;
    }&lt;br /&gt;
                        //&lt;br /&gt;
    if (end_of_mcs_cycle){&lt;br /&gt;
      transition_PS_requested = FALSE;&lt;br /&gt;
      end_of_mcs_cycle = FALSE;&lt;br /&gt;
      return TRUE;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
      return FALSE;&lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */&lt;br /&gt;
  INT read_mcs_event(char *pevent, INT offset)&lt;br /&gt;
  {  &lt;br /&gt;
      ...     // read out data at end of cycle&lt;br /&gt;
      ...&lt;br /&gt;
      end_of_mcs_cycle = TRUE; // end of cycle &lt;br /&gt;
                               //&lt;br /&gt;
      if (!transition_PS_requested)&lt;br /&gt;
         start_cycle(); // start a new cycle &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the example above, &lt;br /&gt;
&lt;br /&gt;
* The frontend code is registered for PAUSE and STOP using &#039;&#039;cm_register_deferred_transition()&#039;&#039;. The second argument &#039;&#039;wait_end_cycle&#039;&#039;  is the declaration of the callback function. &lt;br /&gt;
* The callback function &#039;&#039;wait_end_cycle&#039;&#039; will be called as soon as the transition is requested with the Boolean flag &#039;&#039;first&#039;&#039; set to TRUE.&lt;br /&gt;
* By setting &#039;&#039;transition_PS_requested&#039;&#039;  TRUE , the user will be provided with the acknowledgment of the transition request.&lt;br /&gt;
* By returning FALSE from the callback, the transition is prevented from occurring. &lt;br /&gt;
* As soon as the user condition is satisfied (&#039;&#039;end_of_mcs_cycle&#039;&#039; = TRUE), the return code in the  callback will be set to TRUE and the requested transition will be issued.&lt;br /&gt;
&lt;br /&gt;
While the transition is Deferred, the odb key  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; will contain the transition code, and the d [[mhttpd]] webserver main status page will indicate that a deferred transition is in progress.&lt;br /&gt;
&lt;br /&gt;
Once in deferred state, an [[odbedit]] override command can be issued to force the transition to happen,&lt;br /&gt;
&lt;br /&gt;
 &amp;gt;odbedit&lt;br /&gt;
 odb&amp;gt; stop now    (or &amp;quot;start now&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
or  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; can be set to 0. The transition will then take place on the next stop or start command.&lt;br /&gt;
&lt;br /&gt;
= Compilation using CMake =&lt;br /&gt;
&lt;br /&gt;
Here is a &amp;quot;minimal&amp;quot; CMakeLists.txt file that can be used to compile a user-written frontend (called &amp;quot;myfe&amp;quot; in this case) that uses the mfe framework.&lt;br /&gt;
&lt;br /&gt;
  cmake_minimum_required(VERSION 3.14)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  find_package(Midas REQUIRED PATHS $ENV{MIDASSYS})&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  target_link_libraries(myfe.exe midas::mfe)&lt;br /&gt;
&lt;br /&gt;
One could then build the executable using the following commands:&lt;br /&gt;
&lt;br /&gt;
  mkdir build&lt;br /&gt;
  cd build&lt;br /&gt;
  cmake ..&lt;br /&gt;
  make&lt;br /&gt;
&lt;br /&gt;
Some older operating systems may require the &amp;quot;cmake3&amp;quot; command to be used instead of &amp;quot;cmake&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In the above example we specify the midas::mfe target as we&#039;re building an mfe-based frontend. More targets are available for other types of midas client:&lt;br /&gt;
&lt;br /&gt;
  midas::midas              - normal midas program&lt;br /&gt;
  midas::midas-shared       - normal midas programs using the shared midas library&lt;br /&gt;
  midas::mfe                - old style mfe.cxx frontend&lt;br /&gt;
  midas::mfed               - newer style frontend using mfed.cxx&lt;br /&gt;
  midas::mscb               - programs using MSCB system&lt;br /&gt;
  midas::drivers            - slow control program using any of the standard midas drivers&lt;br /&gt;
&lt;br /&gt;
Note, the above only works for midas versions from January 2025 onwards. For earlier versions of midas, you need to be more explicit about midas&#039; dependencies:&lt;br /&gt;
&lt;br /&gt;
  #&lt;br /&gt;
  # Old version for pre-2025 versions of midas!&lt;br /&gt;
  #&lt;br /&gt;
  &lt;br /&gt;
  cmake_minimum_required(VERSION 3.0)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  &lt;br /&gt;
  # Check for MIDASSYS environment variable&lt;br /&gt;
  if (NOT DEFINED ENV{MIDASSYS})&lt;br /&gt;
    message(SEND_ERROR &amp;quot;MIDASSYS environment variable not defined.&amp;quot;)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  set(CMAKE_CXX_STANDARD 11)&lt;br /&gt;
  set(MIDASSYS $ENV{MIDASSYS})&lt;br /&gt;
  &lt;br /&gt;
  if (${CMAKE_SYSTEM_NAME} MATCHES Linux)&lt;br /&gt;
    set(LIBS -lpthread -lutil -lrt)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  # Define the executable to be built, and the source code files&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  &lt;br /&gt;
  # Directories to search for &lt;br /&gt;
  target_include_directories(myfe.exe PRIVATE ${MIDASSYS}/include)&lt;br /&gt;
  &lt;br /&gt;
  # Libraries to link to&lt;br /&gt;
  target_link_libraries(myfe.exe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})&lt;br /&gt;
&lt;br /&gt;
= Using mfed to reduce the amount of boiler-plate code =&lt;br /&gt;
&lt;br /&gt;
You can include &amp;lt;code&amp;gt;mfed.h&amp;lt;/code&amp;gt; and compile against &amp;lt;code&amp;gt;mfed.cxx&amp;lt;/code&amp;gt; to reduce the amount of boiler-plate code that needs to be written. &lt;br /&gt;
&lt;br /&gt;
In particular:&lt;br /&gt;
* You only need to define the frontend_name and frontend_file_name [[#Global Declarations|globals]] (not display_period etc)&lt;br /&gt;
* You only need to declare your event readout functions (not the [[#System Function Declarations|system functions]])&lt;br /&gt;
* You still need to define your EQUIPMENT structs&lt;br /&gt;
* You need to define frontend_init(), from which you can call install_poll_event(), install_begin_of_run() etc to use your transition functions. If you don&#039;t need a pause_run() function, you don&#039;t need to declare/define it!&lt;br /&gt;
* It does not support interrupt routines&lt;br /&gt;
&lt;br /&gt;
An example can be found in &amp;lt;code&amp;gt;$MIDASSYS/examples/experiment/frontend.cxx&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The full list of functions you can call in your frontend_init() are:&lt;br /&gt;
&lt;br /&gt;
; install_poll_event : Install a function which gets called to check if a new event is available for equipment of type EQ_POLLED.&lt;br /&gt;
; install_frontend_exit : Install a function which gets called when the frontend program finishes.&lt;br /&gt;
; install_begin_of_run : Install a function which gets called when a new run gets started.&lt;br /&gt;
; install_end_of_run : Install a function which gets called when a new run gets stopped.&lt;br /&gt;
; install_pause_run : Install a function which gets called when a new run gets paused.&lt;br /&gt;
; install_resume_run : Install a function which gets called when a new run gets resumed.&lt;br /&gt;
; install_frontend_loop : Install a function which gets called inside the main event loop as often as possible. This function gets all available CPU cycles, so in order not to take 100% CPU, this function can use the ss_sleep(10) function to give up some CPU cycles.&lt;br /&gt;
&lt;br /&gt;
When compiling with cmake, your incantation for the executable would change:&lt;br /&gt;
&lt;br /&gt;
 # Without mfed (regular frontend):&lt;br /&gt;
 add_executable(frontend frontend.cxx)&lt;br /&gt;
 &lt;br /&gt;
 # With mfed:&lt;br /&gt;
 add_executable(frontend frontend.cxx ${MIDASSYS}/src/mfed.cxx)&lt;br /&gt;
&lt;br /&gt;
== Minimal example without mfed ==&lt;br /&gt;
&lt;br /&gt;
   #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
   #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
   #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
   #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
   &lt;br /&gt;
   /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
   /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
   const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
   &lt;br /&gt;
   /* The frontend file name, don&#039;t change it */&lt;br /&gt;
   const char *frontend_file_name = __FILE__;&lt;br /&gt;
   &lt;br /&gt;
   /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
   BOOL frontend_call_loop = TRUE;&lt;br /&gt;
   &lt;br /&gt;
   /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
   INT display_period = 0000;&lt;br /&gt;
   &lt;br /&gt;
   /* maximum event size produced by this frontend */&lt;br /&gt;
   INT max_event_size = 10000;&lt;br /&gt;
   &lt;br /&gt;
   /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
   INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
   &lt;br /&gt;
   /* buffer size to hold events */&lt;br /&gt;
   INT event_buffer_size = 10 * 10000;&lt;br /&gt;
   &lt;br /&gt;
   /*-- Function declarations -----------------------------------------*/&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_init();&lt;br /&gt;
   INT frontend_exit();&lt;br /&gt;
   INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
   INT end_of_run(INT run_number, char *error);&lt;br /&gt;
   INT pause_run(INT run_number, char *error);&lt;br /&gt;
   INT resume_run(INT run_number, char *error);&lt;br /&gt;
   INT frontend_loop();&lt;br /&gt;
   INT read_large_event(char *pevent, INT off);&lt;br /&gt;
   &lt;br /&gt;
   /*-- Equipment list ------------------------------------------------*/&lt;br /&gt;
   &lt;br /&gt;
   #undef USE_INT&lt;br /&gt;
   BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
   &lt;br /&gt;
   EQUIPMENT equipment[] = {&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;large&amp;quot;,                 /* equipment name */&lt;br /&gt;
         {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
         &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
         EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
         0,                       /* event source */&lt;br /&gt;
         &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
         TRUE,                    /* enabled */&lt;br /&gt;
         RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
         2000,                    /* read every 2 sec */&lt;br /&gt;
         0,                       /* stop run after this event limit */&lt;br /&gt;
         0,                       /* number of sub events */&lt;br /&gt;
         0,                       /* log history */&lt;br /&gt;
         &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
         read_large_event,        /* readout routine */&lt;br /&gt;
         NULL, NULL,              /* keep null */&lt;br /&gt;
         NULL,                    /* init string */&lt;br /&gt;
         },&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;&amp;quot;}&lt;br /&gt;
   };&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_init()&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_exit()&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT begin_of_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT end_of_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT pause_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT resume_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_loop()&lt;br /&gt;
   {&lt;br /&gt;
      // Do something very frequently&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
      return 0;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
   {&lt;br /&gt;
      switch (cmd) {&lt;br /&gt;
      case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
         break;&lt;br /&gt;
      case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
         break;&lt;br /&gt;
      case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
         break;&lt;br /&gt;
      case CMD_INTERRUPT_DETACH:&lt;br /&gt;
         break;&lt;br /&gt;
      }&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   &lt;br /&gt;
   INT read_large_event(char *pevent, INT off)&lt;br /&gt;
   {&lt;br /&gt;
      DWORD *pddata;&lt;br /&gt;
   &lt;br /&gt;
      /* init bank structure */&lt;br /&gt;
      bk_init32(pevent);&lt;br /&gt;
   &lt;br /&gt;
      bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
      memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
      pddata += 1000000;&lt;br /&gt;
      memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
      bk_close(pevent, pddata);&lt;br /&gt;
   &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
== Minimal example with mfed ==&lt;br /&gt;
&lt;br /&gt;
Note that you only need boiler-plate for the features you&#039;re actually going to use.&lt;br /&gt;
&lt;br /&gt;
   #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
   #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
   #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
   #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
   &lt;br /&gt;
   /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
   /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
   const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
   &lt;br /&gt;
   /* The frontend file name, don&#039;t change it */&lt;br /&gt;
   const char *frontend_file_name = __FILE__;&lt;br /&gt;
   &lt;br /&gt;
   &lt;br /&gt;
   /*-- Function declarations -----------------------------------------*/&lt;br /&gt;
   INT frontend_init();&lt;br /&gt;
   INT my_frontend_loop();&lt;br /&gt;
   INT read_large_event(char *pevent, INT off);&lt;br /&gt;
   &lt;br /&gt;
   /*-- Equipment list ------------------------------------------------*/&lt;br /&gt;
   &lt;br /&gt;
   #undef USE_INT&lt;br /&gt;
   BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
   &lt;br /&gt;
   EQUIPMENT equipment[] = {&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;large&amp;quot;,                 /* equipment name */&lt;br /&gt;
         {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
         &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
         EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
         0,                       /* event source */&lt;br /&gt;
         &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
         TRUE,                    /* enabled */&lt;br /&gt;
         RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
         2000,                    /* read every 2 sec */&lt;br /&gt;
         0,                       /* stop run after this event limit */&lt;br /&gt;
         0,                       /* number of sub events */&lt;br /&gt;
         0,                       /* log history */&lt;br /&gt;
         &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
         read_large_event,        /* readout routine */&lt;br /&gt;
         NULL, NULL,              /* keep null */&lt;br /&gt;
         NULL,                    /* init string */&lt;br /&gt;
         },&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;&amp;quot;}&lt;br /&gt;
   };&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_init()&lt;br /&gt;
   {&lt;br /&gt;
      install_frontend_loop(my_frontend_loop);&lt;br /&gt;
   &lt;br /&gt;
      // Can also do install_begin_of_run() etc...&lt;br /&gt;
   &lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT my_frontend_loop()&lt;br /&gt;
   {&lt;br /&gt;
      // Do something frequently&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT read_large_event(char *pevent, INT off)&lt;br /&gt;
   {&lt;br /&gt;
      DWORD *pddata;&lt;br /&gt;
   &lt;br /&gt;
      /* init bank structure */&lt;br /&gt;
      bk_init32(pevent);&lt;br /&gt;
   &lt;br /&gt;
      bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
      memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
      pddata += 1000000;&lt;br /&gt;
      memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
      bk_close(pevent, pddata);&lt;br /&gt;
   &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3457</id>
		<title>Frontend user code</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3457"/>
		<updated>2025-01-09T22:03:02Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Compilation using CMake */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation]]&lt;br /&gt;
* [[Frontend Application]] &lt;br /&gt;
* [[Equipment List Parameters]]&lt;br /&gt;
* [[Slow Control System]]&lt;br /&gt;
* [[Equipment Flags]]&lt;br /&gt;
* [[Event Notification (Hot-Link)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This section describes the features of the user-written part of a &amp;quot;C-style&amp;quot; [[Frontend Operation#Frontend|Frontend]], referred to here as &#039;&#039;&#039;frontend.c&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
You can also write frontends using [[Frontend_user_code_(object_oriented_-_TMFE)|object-oriented C++ (TMFE)]] or [[Python]].&lt;br /&gt;
&lt;br /&gt;
The frontend system has evolved over the decades, and contains some legacy features that are not often used (e.g. interrupt handlers). For backwards-compatibility, these features are still present, but this does require a bit more boiler-plate code to be written for each frontend. We will first document every feature supported by the frontend system, then explain a slightly more user-friendly wrapper (mfed.cxx) that helps reduce the amount of boiler-plate needed for a new frontend.&lt;br /&gt;
&lt;br /&gt;
= Frontend Templates =&lt;br /&gt;
To make a user-written or custom frontend, users will usually modify one of the &#039;&#039;&#039;templates&#039;&#039;&#039; provided in the MIDAS package&lt;br /&gt;
under [[Environment Variables#MIDASSYS|$MIDASSYS]]/examples/ for their particular hardware and other requirements. Make sure to pick the closest template, e.g. if writing a [[Slow Control System|Slow Control]] frontend, pick a slow-control template. For each example frontend, a Makefile is also provided.&lt;br /&gt;
&lt;br /&gt;
A partial list of the templates provided is shown below:&lt;br /&gt;
{| style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;3&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Hardware &lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Filename&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Directory (MIDAS package)&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Purpose&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Language&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevmemodules.c&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevme.cxx&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c++/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|CAMAC &lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| Access to CAMAC modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|mtfe.c&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| &#039;&#039;&#039;Multithreaded&#039;&#039;&#039; using ring buffer&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Wiener CC-USB &lt;br /&gt;
|feccusb.cxx&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| CAMAC/USB demo &lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|RS485 bus &lt;br /&gt;
|mscb_fe.c&lt;br /&gt;
| $MIDASSYS/examples/slowcont/&lt;br /&gt;
| &#039;&#039;&#039;Slow control&#039;&#039;&#039; with [https://midas.psi.ch/mscb/ MSCB]&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|EPICS&lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/epics/&lt;br /&gt;
| &#039;&#039;&#039;Slow controls&#039;&#039;&#039;&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|Camac&lt;br /&gt;
|ebfe.c&lt;br /&gt;
| $MIDASSYS/examples/eventbuilder/&lt;br /&gt;
| &#039;&#039;&#039;Event Builder&#039;&#039;&#039; (with [[mevb]])&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|frontend.cxx&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| &#039;&#039;&#039;mfed&#039;&#039;&#039; (wrapper to simplify writing a frontend)&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The features of a typical frontend program are best explained by reference to examples of the user code &lt;br /&gt;
provided in the Midas Package. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Frontend code=&lt;br /&gt;
Note that there are several kinds of frontend used for different purposes, e.g.&lt;br /&gt;
* frontend to read VME or CAMAC hardware, using interrupt or polling (e.g. to read experimental data)&lt;br /&gt;
* multithreaded frontend where polling can be in a separate thread&lt;br /&gt;
* slow control frontend (can be multithreaded) see also [[Slow Control System]]&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Templates for many types of user frontend code are provided in the MIDAS packages (see [[#Frontend Templates]]). &lt;br /&gt;
&lt;br /&gt;
The following sections refer to these templates.&lt;br /&gt;
Most of the examples are taken from the [https://bitbucket.org/tmidas/midas/src/develop/examples/basic/largefe.cxx largefe.cxx example]. Documentation on the MIDAS library subroutines to access the ODB (some of which are used in the examples below) can be found in the [[ODB_Access_and_Use|ODB access page]].&lt;br /&gt;
&lt;br /&gt;
The user frontend code is then compiled and linked with the system part and the MIDAS library &lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Task]]).  Makefiles are provided with the templates that can be modified as needed.&lt;br /&gt;
&lt;br /&gt;
== Access to command line parameters ==&lt;br /&gt;
The function &#039;&#039;&#039;mfe_get_args&#039;&#039;&#039; gives access to the [[Frontend Application|Frontend]] command line parameters. &lt;br /&gt;
&lt;br /&gt;
This example shows how to use it in the frontend user code:&lt;br /&gt;
&lt;br /&gt;
  int argc;&lt;br /&gt;
  char **argv;&lt;br /&gt;
  mfe_get_args(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
  for (int i = 0; i &amp;lt; argc; i++) {&lt;br /&gt;
     // Use argv[i] &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
See [[Frontend_Application#Arguments|Frontend Application Arguments]] for the arguments that are handled automatically by the frontend system.&lt;br /&gt;
&lt;br /&gt;
==Include files==&lt;br /&gt;
The following example (from template frontend file &#039;&#039;$MIDASSYS/examples/basic/largefe.cxx&#039;&#039;) shows the standard include files needed for a frontend. The user may add any other include files as needed (e.g. those needed for VME access). &lt;br /&gt;
&lt;br /&gt;
Some legacy frontends may include the &#039;&#039;&#039;[[ODB#experim.h include file|experim.h]]&#039;&#039;&#039; file. This was an old way of accessing the ODB, but was not very user-friendly when the ODB structure needed to change.&lt;br /&gt;
&lt;br /&gt;
The example below shows a typical list of include files for a frontend:&lt;br /&gt;
 &lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
 #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;div id=&amp;quot;Frontend Name&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
==Global Declarations==&lt;br /&gt;
The following example (from template frontend file fevmemodules.c - see [[#Frontend Templates]]) shows the global declaration. The declarations are system wide.  Some may be changed to suit the user, but none should not be removed.&lt;br /&gt;
&lt;br /&gt;
; frontend_name  : This value can be modified to reflect the purpose of the code &lt;br /&gt;
; frontend_call_loop : If set to TRUE, the function frontend_loop() gets called very frequently. If FALSE, frontend_loop() does not run. The user can add suitable code to this routine if desired (e.g. to check for a condition).&lt;br /&gt;
; display_period : The time interval (defined in milliseconds) between the refresh of a frontend status display. The value of zero disables the display. If the frontend is started in the background with the display enabled, the stdout should be redirected to the null device to prevent the process from hanging.&lt;br /&gt;
; max_event_size : specifies the maximum size (in bytes) of the expected event.&lt;br /&gt;
; event_buffer_size : specifies the maximum size (in bytes) of the buffer to be allocated by the system.&lt;br /&gt;
; equipment_common_overwrite : whether the definitions in the EQUIPMENT struct override those in the /Equipment/largefe/Common section of the ODB. If FALSE, the values in the struct will be used the first time the program runs, then the ODB values will be used afterwards (e.g. allowing you to change the period of a [[#Event_Types_and_Triggers|periodic equipment]] via the ODB).&lt;br /&gt;
&lt;br /&gt;
See below for an example of global declarations from a frontend.&lt;br /&gt;
&lt;br /&gt;
 /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
 /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
 const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 /* The frontend file name, don&#039;t change it */&lt;br /&gt;
 const char *frontend_file_name = __FILE__;&lt;br /&gt;
 &lt;br /&gt;
 /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 &lt;br /&gt;
 /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
 INT display_period = 0;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size produced by this frontend */&lt;br /&gt;
 INT max_event_size = 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
 INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
 &lt;br /&gt;
 /* buffer size to hold events */&lt;br /&gt;
 INT event_buffer_size = 10 * 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* whether the values in EQUIPMENT struct override ODB values */&lt;br /&gt;
 BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==System Function Declarations==&lt;br /&gt;
&lt;br /&gt;
These lines declare the pre-defined system functions which should be present. &lt;br /&gt;
&lt;br /&gt;
 INT frontend_init();&lt;br /&gt;
 INT frontend_exit();&lt;br /&gt;
 INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
 INT end_of_run(INT run_number, char *error);&lt;br /&gt;
 INT pause_run(INT run_number, char *error);&lt;br /&gt;
 INT resume_run(INT run_number, char *error);&lt;br /&gt;
 INT frontend_loop();&lt;br /&gt;
&lt;br /&gt;
==Readout Function Declarations==&lt;br /&gt;
Following the previous group is a second group of declarations, which define the readout functions. These depend on the defined equipments, and run when the respective equipment is triggered. In this example, one equipment will be defined, so there is one declaration. The user functions will be described in detail in later sections. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off);&lt;br /&gt;
&lt;br /&gt;
If using an interrupt, callback function prototypes are also included&lt;br /&gt;
&lt;br /&gt;
 extern void interrupt_routine(void);&lt;br /&gt;
 void register_cnaf_callback(int debug);&lt;br /&gt;
&lt;br /&gt;
==Equipment List==&lt;br /&gt;
&lt;br /&gt;
This list of structs defines the behaviour of your frontend (e.g. [[#Event_Types_and_Triggers|periodic or polled equipment]]). See [[Equipment List Parameters|Equipment List]] for full documentation of the entries.&lt;br /&gt;
&lt;br /&gt;
Note that these are the default values for each equipment (used the very first time a frontend is run). If [[#Global declarations|equipment_common_overwrite]] is FALSE, then some of the values will subsequently be read from the ODB instead. If [[#Global declarations|equipment_common_overwrite]] is TRUE, then the ODB will be updated with the coded values each time the program runs.&lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;large&amp;quot;,                   /* equipment name */&lt;br /&gt;
     {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
      &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
      EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
      0,                       /* event source */&lt;br /&gt;
      &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
      TRUE,                    /* enabled */&lt;br /&gt;
      RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
      2000,                    /* read every 2 sec */&lt;br /&gt;
      0,                       /* stop run after this event limit */&lt;br /&gt;
      0,                       /* number of sub events */&lt;br /&gt;
      0,                       /* log history */&lt;br /&gt;
      &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
      read_large_event,        /* readout routine */&lt;br /&gt;
      NULL, NULL,              /* keep null */&lt;br /&gt;
      NULL,                    /* init string */&lt;br /&gt;
   },&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;&amp;quot;}&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the case of a polled equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Trigger&amp;quot;,            // equipment name&lt;br /&gt;
        {&lt;br /&gt;
          ...&lt;br /&gt;
          &amp;lt;b&amp;gt;EQ_POLLED&amp;lt;/b&amp;gt;,          // equipment type&lt;br /&gt;
          ...&lt;br /&gt;
          500,                // poll for 500ms &lt;br /&gt;
          ...&lt;br /&gt;
          &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
          read_my_event,    // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the case of a periodic equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Scaler&amp;quot;,           // equipment name&lt;br /&gt;
         {    &lt;br /&gt;
            ...&lt;br /&gt;
            EQ_PERIODIC     // equipment type&lt;br /&gt;
            ...&lt;br /&gt;
            10000,          // period (read every 10s)&lt;br /&gt;
            ...&lt;br /&gt;
            &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
       read_my_event,   // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sequence of Operations in the frontend==&lt;br /&gt;
&lt;br /&gt;
The following table shows the sequence of operations of the  [[Frontend Operation#Frontend|Frontend]] System functions.  These functions must be implemented in the user&#039;s code (but may be as simple as just returning &amp;lt;code&amp;gt;SUCCESS&amp;lt;/code&amp;gt; if the function is not relevant for your use case). These functions are called by &#039;&#039;mfe.cxx&#039;&#039; at the appropriate time. The System Transition functions are associated with a particular [[Run States and Transitions|Run Transition]] as shown below:&lt;br /&gt;
&lt;br /&gt;
{|  style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Transition Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Associated [[Run States and Transitions|Transition]]&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Action&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_init()&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| Runs once after system initialization, before Equipment registration. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| begin_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_START&lt;br /&gt;
| Runs after system statistics reset at each begin-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| pause_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_PAUSE&lt;br /&gt;
| Runs at each pause-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| resume_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_RESUME&lt;br /&gt;
| Runs at each resume-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| end_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_STOP&lt;br /&gt;
| Runs at each end-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_exit()&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Runs once before any Slow Control Equipment exit &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Each defined Equipment has the option to force itself to run at individual transition times (see Equipment [[Equipment List Parameters#ReadOn|ReadOn Flag]]), so that its equipment function will be called on a certain transition (or combination of transitions). &lt;br /&gt;
&lt;br /&gt;
The system transition functions all run &#039;&#039;&#039;prior to&#039;&#039;&#039; the equipment functions. This gives the system the chance to take basic action on the transition request (e.g. enable/disable interrupt) before the equipment runs. All the transition routines run with a [[Run States and Transitions#Default Transition Sequency Numbers|Transition Sequence number]] of 500 (the default). This allows users to add additional functions in the frontend that will run before or after any of the transitions (such as a prestart() or a poststop() function). See [[Run States and Transitions#Run Transition Priority|Run Transition Priority]] for more information.&lt;br /&gt;
&lt;br /&gt;
==Function frontend_init ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
No parameters.&lt;br /&gt;
&lt;br /&gt;
This function runs &#039;&#039;&#039;once only&#039;&#039;&#039; at the application startup. Users may perform hardware checking, loading/setting of global variables, mapping of required ODB structures (see [[ODB#experim.h include file|experim.h include file]]), &lt;br /&gt;
[[Event Notification (Hot-Link)#How to set up a Hot-Link|setting up hot-links]] etc. in frontend_init(), e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT frontend_init()&lt;br /&gt;
 {&lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;Initializing...&amp;quot;, &amp;quot;yellow&amp;quot;);&lt;br /&gt;
   ....                  &lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;OK&amp;quot;, &amp;quot;green&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Reporting Equipment Status===&lt;br /&gt;
If running with the webserver [[mhttpd]], a frontend can send an update to the [[Status Page]], to report on its progress, using the function set_equipment_status() (see above example). This is useful when hardware can take a long time to respond.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function begin_of_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being started&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called every time a run start transition occurs, i.e. at begin-of-run. It allows the updating of user parameters, and the loading/setup/clearing of hardware. At the exit of this function, the acquisition should be armed and ready to test the interrupt (if used), e.g. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT begin_of_run (INT runnumber, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   // Read/validate some settings from the ODB (and return FE_ERR_ODB if there&#039;s a problem).&lt;br /&gt;
   // Apply them to the hardware (and return FE_ERR_HW if there&#039;s a problem).&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Functions pause/resume_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being paused/resumed.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
These two functions are called upon &amp;quot;Pause&amp;quot; and &amp;quot;Resume&amp;quot; command respectively. Any code relevant to the upcoming run state can be included,e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT pause_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   disable_trigger();&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
                            //&lt;br /&gt;
 INT resume_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   enable_trigger();&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function end_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being ended.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called at every &amp;quot;stop run&amp;quot; transition. It provides the opportunity to disable the hardware, e.g.&lt;br /&gt;
&lt;br /&gt;
 INT end_of_run(INT run_number, char *error)&lt;br /&gt;
 {&lt;br /&gt;
   // Stop the hardware.&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_exit==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
The function runs when the frontend program is shut down. Can be used to release any locked resources like memory, communications ports etc. e.g.&lt;br /&gt;
&lt;br /&gt;
  function frontend_exit()&lt;br /&gt;
  {&lt;br /&gt;
     mvme_close(gVme);&lt;br /&gt;
     return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_loop==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
If [[#Global declarations|frontend_call_loop]] is set to TRUE, this routine is called when the frontend is idle and at least once between every event. You could use it for example to check if there has been a timeout from hardware.&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 ...&lt;br /&gt;
                                 &lt;br /&gt;
 INT frontend_loop()&lt;br /&gt;
 {&lt;br /&gt;
   // Implement any code that needs to be run very frequently&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Event Types and Triggers==&lt;br /&gt;
The frontend supports several different types of [[Frontend Operation#Frontend event triggers|event trigger]]. The event trigger type is specified by the [[Equipment Flags|Equipment Flag]]  in the  [[Equipment List Parameters|Equipment Declaration]]. Common event types are &amp;quot;polled events&amp;quot; where the Equipment Flag is  [[Equipment Flags#EQ_POLLED|EQ_POLLED]], &amp;quot;interrupts events&amp;quot; where the Flag is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]], and &amp;quot;periodic events&amp;quot; where the Flag is [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]. The name of the associated readout routine is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type.&lt;br /&gt;
&lt;br /&gt;
Polled  and interrupt events (see [[#Event Types|Event Types]]) require several extra functions to handle the hardware that periodic events do not require. These are described below.&lt;br /&gt;
&lt;br /&gt;
Note that each frontend may contain:&lt;br /&gt;
* zero or one polled equipments&lt;br /&gt;
* zero or one interrupt equipments&lt;br /&gt;
* any number of periodic equipments&lt;br /&gt;
&lt;br /&gt;
==Function poll_event==&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* INT &#039;&#039;&#039;count&#039;&#039;&#039;&lt;br /&gt;
* BOOL &#039;&#039;&#039;test&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment.&lt;br /&gt;
         &lt;br /&gt;
The user must provide suitable code in the routine poll_event(), e.g. reading a register from a VME module to see if any data is available&lt;br /&gt;
&lt;br /&gt;
 INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
 {&lt;br /&gt;
    /* Polling routine for events. Returns TRUE if event  is available. If test equals TRUE, don&#039;t return. &lt;br /&gt;
       The test flag is used to time the polling &lt;br /&gt;
     */&lt;br /&gt;
     int i;&lt;br /&gt;
     int lam = 0;&lt;br /&gt;
                                 //&lt;br /&gt;
     for (i = 0; i &amp;lt; count; i++, lam++) {&lt;br /&gt;
       lam = vmeio_CsrRead(myvme, VMEIO_BASE);&lt;br /&gt;
       if (lam)&lt;br /&gt;
         if (!test)&lt;br /&gt;
           return lam;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function interrupt_configure==&lt;br /&gt;
* INT &#039;&#039;&#039;cmd&#039;&#039;&#039; &lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* PTYPE &#039;&#039;&#039;adr&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_INTERRUPT, an interrupt configuration routine called interrupt_configure() must be provided by the user.&lt;br /&gt;
The interrupt configuration routine has the following declaration: &lt;br /&gt;
&lt;br /&gt;
 /*-- Interrupt configuration --------------------------*/&lt;br /&gt;
 INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
 {&lt;br /&gt;
  int vec = 0;&lt;br /&gt;
  switch (cmd) &lt;br /&gt;
  {&lt;br /&gt;
    case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x1);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
      mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
      mvme_interrupt_attach(myvme, INT_LEVEL, INT_VECTOR, &lt;br /&gt;
                (void *)adr, &amp;amp;myinfo);&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+0x10, INT_VECTOR);&lt;br /&gt;
      vec = mvme_read_value(myvme, VLAM_BASE+0x10);&lt;br /&gt;
      printf(&amp;quot;Interrupt Attached to 0x%x for vector:0x%x\n&amp;quot;,&lt;br /&gt;
                     adr, vec&amp;amp;0xFF);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DETACH:&lt;br /&gt;
      printf(&amp;quot;Interrupt Detach\n&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Under the four commands listed above, the user must implement the hardware operation needed to perform the requested action. In the Midas drivers directory examples can be found of such an interrupt code for CAMAC. See source code such as hyt1331.c,ces8210.c &lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user in the frontend.&lt;br /&gt;
&lt;br /&gt;
==Event Readout routine==&lt;br /&gt;
&lt;br /&gt;
An event readout routine is required for all equipment, and is responsible for sending the actual data to midas. The framework calls the event readout routine whenever an equipment has been triggered (e.g. periodicially, or because poll_event() returned TRUE for a polled equipment etc). The function is of the form&lt;br /&gt;
&lt;br /&gt;
 INT function_name ( char *pevent ... )&lt;br /&gt;
 {&lt;br /&gt;
   INT event_size;&lt;br /&gt;
   ........  // read data from hardware&lt;br /&gt;
   ........  // pack into banks depending on format&lt;br /&gt;
   ........&lt;br /&gt;
   return (event_size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
where the first argument of the readout function (pevent)  provides the pointer to the newly constructed event, and points to the first valid location for storing the data.&lt;br /&gt;
;NOTE &lt;br /&gt;
* &amp;lt;b&amp;gt;The return value is the event size&amp;lt;/b&amp;gt;, and must be the number of bytes collected in this function. This is different to almost every other function in midas (where the return value is a status code).&lt;br /&gt;
* You can return 0 if you&#039;ve decided you don&#039;t actually want to write this event.&lt;br /&gt;
* The event serial number will be incremented by one for every call to the readout routine, as long as the returned size is non-zero.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===General readout function===&lt;br /&gt;
&lt;br /&gt;
A readout function is needed to send out data. This is done using one of the supported [[Event Structure|event structures]] usually [[Event Structure#MIDAS Format Event|&amp;quot;MIDAS&amp;quot; format]] data banks.  The bank format (&amp;quot;MIDAS&amp;quot; in the example above) is declared in the [[Equipment List Parameters]]   [[Equipment List Parameters#Format|Format]] field.&lt;br /&gt;
&lt;br /&gt;
An example of a scaler readout routine read_scaler_event() where the data is read out into [[MIDAS Event Construction|MIDAS data banks]] is shown below. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
   DWORD *pddata;&lt;br /&gt;
&lt;br /&gt;
   /* init bank structure */&lt;br /&gt;
   bk_init32(pevent);&lt;br /&gt;
&lt;br /&gt;
   /* create bank (bank names must be 4 chars long) */&lt;br /&gt;
   bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
&lt;br /&gt;
   /* fill data (just dummy values in this case) */&lt;br /&gt;
   memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
   pddata += 1000000;&lt;br /&gt;
   memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
&lt;br /&gt;
   /* close the bank */&lt;br /&gt;
   bk_close(pevent, pddata);&lt;br /&gt;
&lt;br /&gt;
   /* return the number of bytes we wrote */&lt;br /&gt;
   return bk_size(pevent);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Some other examples of event readout routines in this document are&lt;br /&gt;
* [[Event Structure#FIXED Event Construction|FIXED Format event construction]]&lt;br /&gt;
* [[Super Event]] construction&lt;br /&gt;
* [[Slow Control System|Slow Control]]&lt;br /&gt;
&lt;br /&gt;
Many other examples of readout routines can be found in the [[#Frontend Templates|frontend templates]] in the MIDAS package.&lt;br /&gt;
&lt;br /&gt;
===Polled or Interrupt readout routine===&lt;br /&gt;
In the case of a [[#Polled and Interrupt Events|Polled or Interrupt event]], the content of the memory location pointed to by &#039;&#039;&#039;pevent&#039;&#039;&#039; (see [[#Event Readout routine|Event Readout routine]]) prior to its use in the readout function, contains the interrupt source bitwise register. This feature can be exploited in order to identify which hardware module has triggered the readout when multiple interrupts have been assigned to the same readout function.&lt;br /&gt;
&lt;br /&gt;
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism.&lt;br /&gt;
The  [[Equipment List Parameters|Equipment declaration]] is of the form: &lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
               //&lt;br /&gt;
    {&amp;quot;Trigger&amp;quot;,  /* equipment name */&lt;br /&gt;
       ...&lt;br /&gt;
 #ifdef USE_INT&lt;br /&gt;
       EQ_INTERRUPT, /* equipment type */&lt;br /&gt;
 #else&lt;br /&gt;
       EQ_POLLED,    /* equipment type */&lt;br /&gt;
 #endif&lt;br /&gt;
   /* interrupt source: crate 0, all stations */&lt;br /&gt;
       LAM_SOURCE(0, 0x0),&lt;br /&gt;
       ....&lt;br /&gt;
       &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,&lt;br /&gt;
     },&lt;br /&gt;
     read_trigger_event, /* readout routine */&lt;br /&gt;
     NULL, NULL,&lt;br /&gt;
     trigger_bank_list,&lt;br /&gt;
 }&lt;br /&gt;
  &lt;br /&gt;
Note that the [[Macros#CAMAC Macros|LAM_SOURCE macro]] simply codes the parameters into a bitwise register.&lt;br /&gt;
&lt;br /&gt;
The readout routine would contains code such as&lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   DWORD  *pdata;&lt;br /&gt;
 #endif&lt;br /&gt;
                        //&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   /* read ADC0 data */&lt;br /&gt;
   v792_EvtCntRead(myvme, VADC0_BASE, &amp;amp;evtcnt);&lt;br /&gt;
   ........&lt;br /&gt;
   /* Read Event */&lt;br /&gt;
   v792_EventRead(myvme, VADC0_BASE, pdata, &amp;amp;nentry);&lt;br /&gt;
   ........&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
 #endif&lt;br /&gt;
                       //&lt;br /&gt;
   ........&lt;br /&gt;
   return (size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The data will be packed into banks as described for the [[General readout function|general readout function]] above.&lt;br /&gt;
The example &amp;lt;code&amp;gt;$MIDASSYS/examples/Triumf/c/fevmemodules.c&amp;lt;/code&amp;gt; contains a complete example of read_trigger_event().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Manual Trigger==&lt;br /&gt;
Another type of [[Frontend Operation#Frontend event trigger|frontend event trigger]] supported is the &amp;quot;manual trigger&amp;quot;,  where the Equipment Flag is [[Equipment Flags#EQ_MANUAL_TRIGGER|EQ_MANUAL_TRIGGER]]. This flag means that an event can be triggered through the URL &amp;lt;code&amp;gt;?cmd=Trigger/&amp;lt;equipment_name&amp;gt;&amp;lt;/code&amp;gt; (e.g. &amp;lt;code&amp;gt;http://my.host:8080/?cmd=Trigger/MyEquipmentName&amp;lt;/code&amp;gt;). If you add this URL to the [[/Alias ODB tree|/Alias]] part of the ODB, then a link will appear on midas webpages that can be clicked to trigger an event.&lt;br /&gt;
&lt;br /&gt;
In some cases, the same readout code may be used for two types of event: a manual trigger and (say) a poll event. It is possible to determine whether the readout of an event was triggered by a manual trigger or a regular trigger by adding a call to the  [[MIDAS Event Header Macros|event header Macro]] DATA_SIZE in the readout routine:&lt;br /&gt;
&lt;br /&gt;
  flag = DATA_SIZE(pevent);&lt;br /&gt;
&lt;br /&gt;
If the result is&lt;br /&gt;
  *  flag = 0 normal call&lt;br /&gt;
  *  flag = 1 manual trigger&lt;br /&gt;
&lt;br /&gt;
It is also possible for a backend MIDAS client (such as an analyzer or custom data archiver) to trigger a manual trigger event. The client controls when an event is sent by means of&lt;br /&gt;
a  function that requests an event by triggering the event sending mechanism with a RPC call.&lt;br /&gt;
&lt;br /&gt;
With a frontend Equipment declaration of a manually triggered event of the form:&lt;br /&gt;
    &lt;br /&gt;
    { &amp;quot;Histo&amp;quot;,             /* equipment name */&lt;br /&gt;
       2, 0,                 /* event ID, trigger mask */&lt;br /&gt;
       &amp;quot;SYSTEM&amp;quot;,             /* event buffer */&lt;br /&gt;
       EQ_MANUAL_TRIG,     /* equipment type */&lt;br /&gt;
       0,    &lt;br /&gt;
       .......&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
the code fragment to manually trigger this event is:&lt;br /&gt;
&lt;br /&gt;
  int main(unsigned int argc,char **argv)&lt;br /&gt;
  {&lt;br /&gt;
      .......&lt;br /&gt;
      bm_request_event(hBufEvent, 2, TRIGGER_ALL, GET_ALL, &amp;amp;request_id, process_event_TD);&lt;br /&gt;
      .......&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
When it is time to save the data during the run, the function below is called:&lt;br /&gt;
&lt;br /&gt;
 BOOL trigger_histo_event(void)&lt;br /&gt;
 {&lt;br /&gt;
   HNDLE hconn;&lt;br /&gt;
   BOOL event_triggered;&lt;br /&gt;
                            //&lt;br /&gt;
   event_triggered = FALSE;&lt;br /&gt;
   ...................&lt;br /&gt;
   if (run_state == STATE_RUNNING) {   // Check the frontend client exists&lt;br /&gt;
        if( cm_exist(ClientName,TRUE))  {    &lt;br /&gt;
            status = cm_connect_client (ClientName, &amp;amp;hconn);&lt;br /&gt;
            if(status != RPC_SUCCESS)&lt;br /&gt;
                cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Cannot connect to frontend \&amp;quot;%s\&amp;quot; (%d)&amp;quot;,&lt;br /&gt;
                       ClientName,status);&lt;br /&gt;
            else  {     // successfully connected to frontend client&lt;br /&gt;
                status = rpc_client_call(hconn, RPC_MANUAL_TRIG, 2); // trigger a histo event&lt;br /&gt;
                if (status != CM_SUCCESS)&lt;br /&gt;
                    cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error triggering event from frontend (%d)&amp;quot;,status);&lt;br /&gt;
                else {  // successfully triggered event&lt;br /&gt;
                    event_triggered=TRUE;&lt;br /&gt;
                    status =cm_disconnect_client(hconn, FALSE);&lt;br /&gt;
                    if (status != CM_SUCCESS)&lt;br /&gt;
                        cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error disconnecting client (%d)&amp;quot;,status);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Frontend client %s not running (%d)&amp;quot;,&lt;br /&gt;
                   ClientName,status);&lt;br /&gt;
    } &lt;br /&gt;
    return(event_triggered);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Deferred Transition==&lt;br /&gt;
This option permits the user to postpone any transition issued by any requester until some condition is satisfied.&lt;br /&gt;
For example:&lt;br /&gt;
* It may not be advisable to pause or stop a run until some hardware has turned off a particular valve. &lt;br /&gt;
* The start of the acquisition system should be postponed until the beam rate has been stable for a given period of time.&lt;br /&gt;
* While active, a particular acquisition system should not be interrupted until the &amp;quot;cycle&amp;quot; is completed.&lt;br /&gt;
&lt;br /&gt;
In these examples, any application having access to the state of the hardware can register to be a &amp;quot;transition Deferred&amp;quot; client. The MIDAS system will then catch any transition request and postpone the trigger of such a transition until the condition  is satisfied.&lt;br /&gt;
&lt;br /&gt;
The Deferred transition requires 3 steps for setup&lt;br /&gt;
# Register for the deferred transition &lt;br /&gt;
# Provide a callback function to serve the deferred transition &lt;br /&gt;
# Implement the condition code &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note that you should only have ONE frontend that defines a deferred transition&amp;lt;/b&amp;gt; (as this niche feature that was implemented with this assumption in mind...).&lt;br /&gt;
&lt;br /&gt;
The following example demonstrates this process:&lt;br /&gt;
&lt;br /&gt;
  BOOL transition_PS_requested=FALSE; // global&lt;br /&gt;
                                              //&lt;br /&gt;
  INT frontend_init()&lt;br /&gt;
  {&lt;br /&gt;
    // register for deferred transition&lt;br /&gt;
                                              //&lt;br /&gt;
    cm_register_deferred_transition(TR_STOP, wait_end_cycle);&lt;br /&gt;
    cm_register_deferred_transition(TR_PAUSE, wait_end_cycle);&lt;br /&gt;
    ...  &lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */ &lt;br /&gt;
  //-- Deferred transition callback&lt;br /&gt;
  BOOL wait_end_cycle(int transition, BOOL first)&lt;br /&gt;
  {&lt;br /&gt;
    if (first) {&lt;br /&gt;
      transition_PS_requested = TRUE;&lt;br /&gt;
      return FALSE;&lt;br /&gt;
    }&lt;br /&gt;
                        //&lt;br /&gt;
    if (end_of_mcs_cycle){&lt;br /&gt;
      transition_PS_requested = FALSE;&lt;br /&gt;
      end_of_mcs_cycle = FALSE;&lt;br /&gt;
      return TRUE;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
      return FALSE;&lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */&lt;br /&gt;
  INT read_mcs_event(char *pevent, INT offset)&lt;br /&gt;
  {  &lt;br /&gt;
      ...     // read out data at end of cycle&lt;br /&gt;
      ...&lt;br /&gt;
      end_of_mcs_cycle = TRUE; // end of cycle &lt;br /&gt;
                               //&lt;br /&gt;
      if (!transition_PS_requested)&lt;br /&gt;
         start_cycle(); // start a new cycle &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the example above, &lt;br /&gt;
&lt;br /&gt;
* The frontend code is registered for PAUSE and STOP using &#039;&#039;cm_register_deferred_transition()&#039;&#039;. The second argument &#039;&#039;wait_end_cycle&#039;&#039;  is the declaration of the callback function. &lt;br /&gt;
* The callback function &#039;&#039;wait_end_cycle&#039;&#039; will be called as soon as the transition is requested with the Boolean flag &#039;&#039;first&#039;&#039; set to TRUE.&lt;br /&gt;
* By setting &#039;&#039;transition_PS_requested&#039;&#039;  TRUE , the user will be provided with the acknowledgment of the transition request.&lt;br /&gt;
* By returning FALSE from the callback, the transition is prevented from occurring. &lt;br /&gt;
* As soon as the user condition is satisfied (&#039;&#039;end_of_mcs_cycle&#039;&#039; = TRUE), the return code in the  callback will be set to TRUE and the requested transition will be issued.&lt;br /&gt;
&lt;br /&gt;
While the transition is Deferred, the odb key  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; will contain the transition code, and the d [[mhttpd]] webserver main status page will indicate that a deferred transition is in progress.&lt;br /&gt;
&lt;br /&gt;
Once in deferred state, an [[odbedit]] override command can be issued to force the transition to happen,&lt;br /&gt;
&lt;br /&gt;
 &amp;gt;odbedit&lt;br /&gt;
 odb&amp;gt; stop now    (or &amp;quot;start now&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
or  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; can be set to 0. The transition will then take place on the next stop or start command.&lt;br /&gt;
&lt;br /&gt;
= Compilation using CMake =&lt;br /&gt;
&lt;br /&gt;
Here is a &amp;quot;minimal&amp;quot; CMakeLists.txt file that can be used to compile a user-written frontend (called &amp;quot;myfe&amp;quot; in this case) that uses the mfe framework.&lt;br /&gt;
&lt;br /&gt;
  cmake_minimum_required(VERSION 3.10)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  find_package(Midas REQUIRED PATHS $ENV{MIDASSYS})&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  target_link_libraries(myfe.exe midas::mfe)&lt;br /&gt;
&lt;br /&gt;
One could then build the executable using the following commands:&lt;br /&gt;
&lt;br /&gt;
  mkdir build&lt;br /&gt;
  cd build&lt;br /&gt;
  cmake ..&lt;br /&gt;
  make&lt;br /&gt;
&lt;br /&gt;
Some older operating systems may require the &amp;quot;cmake3&amp;quot; command to be used instead of &amp;quot;cmake&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In the above example we specify the midas::mfe target as we&#039;re building an mfe-based frontend. More targets are available for other types of midas client:&lt;br /&gt;
&lt;br /&gt;
  midas::midas              - normal midas program&lt;br /&gt;
  midas::midas-shared       - normal midas programs using the shared midas library&lt;br /&gt;
  midas::mfe                - old style mfe.cxx frontend&lt;br /&gt;
  midas::mfed               - newer style frontend using mfed.cxx&lt;br /&gt;
  midas::mscb               - programs using MSCB system&lt;br /&gt;
  midas::drivers            - slow control program using any of the standard midas drivers&lt;br /&gt;
&lt;br /&gt;
Note, the above only works for midas versions from January 2025 onwards. For earlier versions of midas, you need to be more explicit about midas&#039; dependencies:&lt;br /&gt;
&lt;br /&gt;
  #&lt;br /&gt;
  # Old version for pre-2025 versions of midas!&lt;br /&gt;
  #&lt;br /&gt;
  &lt;br /&gt;
  cmake_minimum_required(VERSION 3.0)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  &lt;br /&gt;
  # Check for MIDASSYS environment variable&lt;br /&gt;
  if (NOT DEFINED ENV{MIDASSYS})&lt;br /&gt;
    message(SEND_ERROR &amp;quot;MIDASSYS environment variable not defined.&amp;quot;)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  set(CMAKE_CXX_STANDARD 11)&lt;br /&gt;
  set(MIDASSYS $ENV{MIDASSYS})&lt;br /&gt;
  &lt;br /&gt;
  if (${CMAKE_SYSTEM_NAME} MATCHES Linux)&lt;br /&gt;
    set(LIBS -lpthread -lutil -lrt)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  # Define the executable to be built, and the source code files&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  &lt;br /&gt;
  # Directories to search for &lt;br /&gt;
  target_include_directories(myfe.exe PRIVATE ${MIDASSYS}/include)&lt;br /&gt;
  &lt;br /&gt;
  # Libraries to link to&lt;br /&gt;
  target_link_libraries(myfe.exe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})&lt;br /&gt;
&lt;br /&gt;
= Using mfed to reduce the amount of boiler-plate code =&lt;br /&gt;
&lt;br /&gt;
You can include &amp;lt;code&amp;gt;mfed.h&amp;lt;/code&amp;gt; and compile against &amp;lt;code&amp;gt;mfed.cxx&amp;lt;/code&amp;gt; to reduce the amount of boiler-plate code that needs to be written. &lt;br /&gt;
&lt;br /&gt;
In particular:&lt;br /&gt;
* You only need to define the frontend_name and frontend_file_name [[#Global Declarations|globals]] (not display_period etc)&lt;br /&gt;
* You only need to declare your event readout functions (not the [[#System Function Declarations|system functions]])&lt;br /&gt;
* You still need to define your EQUIPMENT structs&lt;br /&gt;
* You need to define frontend_init(), from which you can call install_poll_event(), install_begin_of_run() etc to use your transition functions. If you don&#039;t need a pause_run() function, you don&#039;t need to declare/define it!&lt;br /&gt;
* It does not support interrupt routines&lt;br /&gt;
&lt;br /&gt;
An example can be found in &amp;lt;code&amp;gt;$MIDASSYS/examples/experiment/frontend.cxx&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The full list of functions you can call in your frontend_init() are:&lt;br /&gt;
&lt;br /&gt;
; install_poll_event : Install a function which gets called to check if a new event is available for equipment of type EQ_POLLED.&lt;br /&gt;
; install_frontend_exit : Install a function which gets called when the frontend program finishes.&lt;br /&gt;
; install_begin_of_run : Install a function which gets called when a new run gets started.&lt;br /&gt;
; install_end_of_run : Install a function which gets called when a new run gets stopped.&lt;br /&gt;
; install_pause_run : Install a function which gets called when a new run gets paused.&lt;br /&gt;
; install_resume_run : Install a function which gets called when a new run gets resumed.&lt;br /&gt;
; install_frontend_loop : Install a function which gets called inside the main event loop as often as possible. This function gets all available CPU cycles, so in order not to take 100% CPU, this function can use the ss_sleep(10) function to give up some CPU cycles.&lt;br /&gt;
&lt;br /&gt;
When compiling with cmake, your incantation for the executable would change:&lt;br /&gt;
&lt;br /&gt;
 # Without mfed (regular frontend):&lt;br /&gt;
 add_executable(frontend frontend.cxx)&lt;br /&gt;
 &lt;br /&gt;
 # With mfed:&lt;br /&gt;
 add_executable(frontend frontend.cxx ${MIDASSYS}/src/mfed.cxx)&lt;br /&gt;
&lt;br /&gt;
== Minimal example without mfed ==&lt;br /&gt;
&lt;br /&gt;
   #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
   #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
   #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
   #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
   &lt;br /&gt;
   /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
   /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
   const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
   &lt;br /&gt;
   /* The frontend file name, don&#039;t change it */&lt;br /&gt;
   const char *frontend_file_name = __FILE__;&lt;br /&gt;
   &lt;br /&gt;
   /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
   BOOL frontend_call_loop = TRUE;&lt;br /&gt;
   &lt;br /&gt;
   /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
   INT display_period = 0000;&lt;br /&gt;
   &lt;br /&gt;
   /* maximum event size produced by this frontend */&lt;br /&gt;
   INT max_event_size = 10000;&lt;br /&gt;
   &lt;br /&gt;
   /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
   INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
   &lt;br /&gt;
   /* buffer size to hold events */&lt;br /&gt;
   INT event_buffer_size = 10 * 10000;&lt;br /&gt;
   &lt;br /&gt;
   /*-- Function declarations -----------------------------------------*/&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_init();&lt;br /&gt;
   INT frontend_exit();&lt;br /&gt;
   INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
   INT end_of_run(INT run_number, char *error);&lt;br /&gt;
   INT pause_run(INT run_number, char *error);&lt;br /&gt;
   INT resume_run(INT run_number, char *error);&lt;br /&gt;
   INT frontend_loop();&lt;br /&gt;
   INT read_large_event(char *pevent, INT off);&lt;br /&gt;
   &lt;br /&gt;
   /*-- Equipment list ------------------------------------------------*/&lt;br /&gt;
   &lt;br /&gt;
   #undef USE_INT&lt;br /&gt;
   BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
   &lt;br /&gt;
   EQUIPMENT equipment[] = {&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;large&amp;quot;,                 /* equipment name */&lt;br /&gt;
         {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
         &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
         EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
         0,                       /* event source */&lt;br /&gt;
         &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
         TRUE,                    /* enabled */&lt;br /&gt;
         RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
         2000,                    /* read every 2 sec */&lt;br /&gt;
         0,                       /* stop run after this event limit */&lt;br /&gt;
         0,                       /* number of sub events */&lt;br /&gt;
         0,                       /* log history */&lt;br /&gt;
         &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
         read_large_event,        /* readout routine */&lt;br /&gt;
         NULL, NULL,              /* keep null */&lt;br /&gt;
         NULL,                    /* init string */&lt;br /&gt;
         },&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;&amp;quot;}&lt;br /&gt;
   };&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_init()&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_exit()&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT begin_of_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT end_of_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT pause_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT resume_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_loop()&lt;br /&gt;
   {&lt;br /&gt;
      // Do something very frequently&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
      return 0;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
   {&lt;br /&gt;
      switch (cmd) {&lt;br /&gt;
      case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
         break;&lt;br /&gt;
      case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
         break;&lt;br /&gt;
      case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
         break;&lt;br /&gt;
      case CMD_INTERRUPT_DETACH:&lt;br /&gt;
         break;&lt;br /&gt;
      }&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   &lt;br /&gt;
   INT read_large_event(char *pevent, INT off)&lt;br /&gt;
   {&lt;br /&gt;
      DWORD *pddata;&lt;br /&gt;
   &lt;br /&gt;
      /* init bank structure */&lt;br /&gt;
      bk_init32(pevent);&lt;br /&gt;
   &lt;br /&gt;
      bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
      memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
      pddata += 1000000;&lt;br /&gt;
      memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
      bk_close(pevent, pddata);&lt;br /&gt;
   &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
== Minimal example with mfed ==&lt;br /&gt;
&lt;br /&gt;
Note that you only need boiler-plate for the features you&#039;re actually going to use.&lt;br /&gt;
&lt;br /&gt;
   #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
   #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
   #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
   #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
   &lt;br /&gt;
   /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
   /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
   const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
   &lt;br /&gt;
   /* The frontend file name, don&#039;t change it */&lt;br /&gt;
   const char *frontend_file_name = __FILE__;&lt;br /&gt;
   &lt;br /&gt;
   &lt;br /&gt;
   /*-- Function declarations -----------------------------------------*/&lt;br /&gt;
   INT frontend_init();&lt;br /&gt;
   INT my_frontend_loop();&lt;br /&gt;
   INT read_large_event(char *pevent, INT off);&lt;br /&gt;
   &lt;br /&gt;
   /*-- Equipment list ------------------------------------------------*/&lt;br /&gt;
   &lt;br /&gt;
   #undef USE_INT&lt;br /&gt;
   BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
   &lt;br /&gt;
   EQUIPMENT equipment[] = {&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;large&amp;quot;,                 /* equipment name */&lt;br /&gt;
         {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
         &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
         EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
         0,                       /* event source */&lt;br /&gt;
         &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
         TRUE,                    /* enabled */&lt;br /&gt;
         RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
         2000,                    /* read every 2 sec */&lt;br /&gt;
         0,                       /* stop run after this event limit */&lt;br /&gt;
         0,                       /* number of sub events */&lt;br /&gt;
         0,                       /* log history */&lt;br /&gt;
         &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
         read_large_event,        /* readout routine */&lt;br /&gt;
         NULL, NULL,              /* keep null */&lt;br /&gt;
         NULL,                    /* init string */&lt;br /&gt;
         },&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;&amp;quot;}&lt;br /&gt;
   };&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_init()&lt;br /&gt;
   {&lt;br /&gt;
      install_frontend_loop(my_frontend_loop);&lt;br /&gt;
   &lt;br /&gt;
      // Can also do install_begin_of_run() etc...&lt;br /&gt;
   &lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT my_frontend_loop()&lt;br /&gt;
   {&lt;br /&gt;
      // Do something frequently&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT read_large_event(char *pevent, INT off)&lt;br /&gt;
   {&lt;br /&gt;
      DWORD *pddata;&lt;br /&gt;
   &lt;br /&gt;
      /* init bank structure */&lt;br /&gt;
      bk_init32(pevent);&lt;br /&gt;
   &lt;br /&gt;
      bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
      memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
      pddata += 1000000;&lt;br /&gt;
      memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
      bk_close(pevent, pddata);&lt;br /&gt;
   &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=/Alarms_ODB_tree&amp;diff=3456</id>
		<title>/Alarms ODB tree</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=/Alarms_ODB_tree&amp;diff=3456"/>
		<updated>2025-01-07T20:04:58Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: Alarm sound and trigger count&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Alarms Page]]&lt;br /&gt;
* [[Alarm System]]&lt;br /&gt;
* [[Mhttpd|mhttpd MIDAS web server]]&lt;br /&gt;
* [[odbedit]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
= Purpose =&lt;br /&gt;
The ODB /Alarms tree contains user and system information related to alarms (see [[Alarm System]]). The information from this tree is displayed in the [[Alarms Page]].&lt;br /&gt;
&lt;br /&gt;
= Creating the /Alarms tree =&lt;br /&gt;
When the ODB is created, the /Alarms tree is automatically created, with &lt;br /&gt;
* two Alarms with  [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm name&amp;gt;]] &amp;quot;Demo ODB&amp;quot; and &amp;quot;Demo periodic&amp;quot; and &lt;br /&gt;
* two Classes with [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class name&amp;gt;]] &amp;quot;Alarm&amp;quot; and &amp;quot;Warning&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
These are shown in the [[#Example of an /Alarms tree|example below]].&lt;br /&gt;
&lt;br /&gt;
The user may create other alarms and classes by copying an existing &amp;lt;alarm-name&amp;gt; or &amp;lt;alarm-class&amp;gt; subtree and editing (via [[Alarms Page]] or [[odbedit]]) as required.&lt;br /&gt;
&lt;br /&gt;
By default, the [[Alarm System]] is NOT active. When it is active,&lt;br /&gt;
the overall alarm is checked once every minute. Once the alarm has been triggered, the message associated with the alarm can be repeated at a different rate. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Example =&lt;br /&gt;
This example (using [[odbedit]]) shows the  default &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/Alarms&amp;lt;/span&amp;gt; tree. See also [[Alarms Page]].&lt;br /&gt;
&lt;br /&gt;
 $ odbedit&lt;br /&gt;
 [local:pol:S]/&amp;gt;cd /alarms&lt;br /&gt;
 [local:pol:S]/Alarms&amp;gt;ls&lt;br /&gt;
 Alarm system active             y&lt;br /&gt;
 Alarms&lt;br /&gt;
 Classes&lt;br /&gt;
&lt;br /&gt;
 [local:pol:S]/Alarms&amp;gt;ls -r -lt&lt;br /&gt;
 Key name                        Type    #Val  Size  Last Opn Mode Value&lt;br /&gt;
 ---------------------------------------------------------------------------&lt;br /&gt;
 Alarms                          DIR&lt;br /&gt;
    Alarm system active         BOOL    1     4     4h   0   RWD  y&lt;br /&gt;
    Alarms                      DIR&lt;br /&gt;
        Demo ODB                DIR&lt;br /&gt;
            Active              BOOL    1     4     &amp;gt;99d 0   RWD  n&lt;br /&gt;
            Triggered           INT     1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Type                INT     1     4     &amp;gt;99d 0   RWD  3&lt;br /&gt;
            Check interval      INT     1     4     &amp;gt;99d 0   RWD  60&lt;br /&gt;
            Checked last        DWORD   1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Trigger count       DWORD   1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Trigger count requirDWORD   1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Time triggered firstSTRING  1     32    &amp;gt;99d 0   RWD&lt;br /&gt;
            Time triggered last STRING  1     32    &amp;gt;99d 0   RWD&lt;br /&gt;
            Condition           STRING  1     256   &amp;gt;99d 0   RWD  /Runinfo/Run number &amp;gt; 100&lt;br /&gt;
            Alarm Class         STRING  1     32    &amp;gt;99d 0   RWD  Alarm&lt;br /&gt;
            Alarm Message       STRING  1     80    &amp;gt;99d 0   RWD  Run number became too large&lt;br /&gt;
        Demo periodic           DIR&lt;br /&gt;
            Active              BOOL    1     4     &amp;gt;99d 0   RWD  n&lt;br /&gt;
            Triggered           INT     1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Type                INT     1     4     &amp;gt;99d 0   RWD  4&lt;br /&gt;
            Check interval      INT     1     4     &amp;gt;99d 0   RWD  28800&lt;br /&gt;
            Checked last        DWORD   1     4     &amp;gt;99d 0   RWD  1058817867&lt;br /&gt;
            Trigger count       DWORD   1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Trigger count requirDWORD   1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Time triggered firstSTRING  1     32    &amp;gt;99d 0   RWD&lt;br /&gt;
            Time triggered last STRING  1     32    &amp;gt;99d 0   RWD&lt;br /&gt;
            Condition           STRING  1     256   &amp;gt;99d 0   RWD&lt;br /&gt;
            Alarm Class         STRING  1     32    &amp;gt;99d 0   RWD  Warning&lt;br /&gt;
            Alarm Message       STRING  1     80    &amp;gt;99d 0   RWD  Please do your shift checks&lt;br /&gt;
      fePOL                   DIR&lt;br /&gt;
            Active              BOOL    1     4     19s  0   RWD  y&lt;br /&gt;
            Triggered           INT     1     4     19s  0   RWD  205&lt;br /&gt;
            Type                INT     1     4     3s   0   RWD  2&lt;br /&gt;
            Check interval      INT     1     4     19s  0   RWD  60&lt;br /&gt;
            Checked last        DWORD   1     4     19s  0   RWD  1259196026&lt;br /&gt;
            Trigger count       DWORD   1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Trigger count requirDWORD   1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Time triggered firstSTRING  1     32    19s  0   RWD  Wed Nov 25 12:59:33 2009&lt;br /&gt;
            Time triggered last STRING  1     32    19s  0   RWD  Wed Nov 25 16:40:26 2009&lt;br /&gt;
            Condition           STRING  1     256   3s   0   RWD  Program not running&lt;br /&gt;
            Alarm Class         STRING  1     32    19s  0   RWD  Caution&lt;br /&gt;
            Alarm Message       STRING  1     80    19s  0   RWD  Program fePOL is not running&lt;br /&gt;
  Classes                      DIR&lt;br /&gt;
        Alarm                   DIR&lt;br /&gt;
            Write system messageBOOL    1     4     27h  0   RWD  y&lt;br /&gt;
            Write Elog message  BOOL    1     4     27h  0   RWD  n&lt;br /&gt;
            System message interINT     1     4     27h  0   RWD  60&lt;br /&gt;
            System message last DWORD   1     4     27h  0   RWD  0&lt;br /&gt;
            Execute command     STRING  1     256   27h  0   RWD&lt;br /&gt;
            Execute interval    INT     1     4     27h  0   RWD  0&lt;br /&gt;
            Execute last        DWORD   1     4     27h  0   RWD  0&lt;br /&gt;
            Stop run            BOOL    1     4     27h  0   RWD  n&lt;br /&gt;
            Display BGColor     STRING  1     32    27h  0   RWD  red&lt;br /&gt;
            Display FGColor     STRING  1     32    27h  0   RWD  black&lt;br /&gt;
            Alarm sound         BOOL    1     4     27h  0   RWD  y&lt;br /&gt;
        Warning                 DIR&lt;br /&gt;
            Write system messageBOOL    1     4     &amp;gt;99d 0   RWD  y&lt;br /&gt;
            Write Elog message  BOOL    1     4     &amp;gt;99d 0   RWD  n&lt;br /&gt;
            System message interINT     1     4     &amp;gt;99d 0   RWD  60&lt;br /&gt;
            System message last DWORD   1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Execute command     STRING  1     256   &amp;gt;99d 0   RWD&lt;br /&gt;
            Execute interval    INT     1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Execute last        DWORD   1     4     &amp;gt;99d 0   RWD  0&lt;br /&gt;
            Stop run            BOOL    1     4     &amp;gt;99d 0   RWD  n&lt;br /&gt;
            Display BGColor     STRING  1     32    &amp;gt;99d 0   RWD  red&lt;br /&gt;
            Display FGColor     STRING  1     32    &amp;gt;99d 0   RWD  black&lt;br /&gt;
            Alarm sound         BOOL    1     4     27h  0   RWD  y&lt;br /&gt;
&lt;br /&gt;
= /Alarms tree structure =&lt;br /&gt;
The &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;/Alarms&amp;lt;/span&amp;gt; ODB tree is split into 2 subtrees:&lt;br /&gt;
* [[#Alarms subtree|Alarms subtree]] containing as many alarms (i.e. &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&amp;lt;alarm-name&amp;gt;&amp;lt;/span&amp;gt; subtrees) as the user desires. Each alarm must be one of the four defined [[Alarm System#Alarm Types|Alarm Types]]. These also define the conditions that trigger the alarm. &lt;br /&gt;
* [[#Classes subtree|Classes subtree]] containing as many classes (i.e. &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&amp;lt;class-name&amp;gt;&amp;lt;/span&amp;gt; subtrees)  as the user desires. Each class defines the action to be taken when the alarm occurs. Two Classes (Alarm and Warning) are defined by default (see [[#Example|Example]]).&lt;br /&gt;
&lt;br /&gt;
See also [[Alarm System]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Keys in the  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;/Alarms&#039;&#039;&amp;lt;/span&amp;gt; ODB tree  =&lt;br /&gt;
&lt;br /&gt;
==  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Alarm system active&#039;&#039;&amp;lt;/span&amp;gt;  ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;n&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
If this key in the [[#top|/Alarms ODB tree]]&lt;br /&gt;
is set to &amp;quot;y&amp;quot;, the alarm system is active. Set to &amp;quot;n&amp;quot; to deactivate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Alarms&#039;&#039;&amp;lt;/span&amp;gt; subtree  ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sub-tree in the [[#top|/Alarms ODB tree]] defining each individual alarm condition.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;&amp;lt;alarm-name&amp;gt;&#039;&#039;&amp;lt;/span&amp;gt; subtree  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This subtree in the [[#Alarms subtree|/Alarms/alarms subtree]] specifies the &#039;&#039;name&#039;&#039; of one of the defined alarms, where &#039;&#039;name&#039;&#039; is substituted for  &amp;lt;alarm-name&amp;gt;.  This subtree will be repeated with a different name for each defined alarm.&lt;br /&gt;
In the [[#Example|example]] two &#039;&#039;&amp;lt;alarm-name&amp;gt;&#039;&#039; subtrees are shown, called &amp;quot;&#039;&#039;demo odb&#039;&#039;&amp;quot; and  &amp;quot;&#039;&#039;demo periodic&#039;&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Active&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;n&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
If this key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]] is set to &amp;quot;y&amp;quot; , this particular alarm is active.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Triggered&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  0&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
If this key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]] is non-zero, this alarm has been triggered. Filled by System.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Type&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]] must be one of the listed [[#Alarm Type|Alarm Types]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Check interval&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  60&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]] contains the&lt;br /&gt;
frequency in seconds at which this alarm condition is to be checked by the alarm system.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Checked last&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  0&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]]  is written by the Alarm System. It notes the UNIX timestamp when the alarm condition was last checked.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Trigger count&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  0&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]]  is written by the Alarm System. It notes how many consecutive checks of the alarm condition have failed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Trigger count required&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  0&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]]  can be set by the user. &lt;br /&gt;
&lt;br /&gt;
If it is set to 0 or 1 then the alarm will be triggered as soon as the failure condition is met.&lt;br /&gt;
&lt;br /&gt;
If it is set to a number greater than 1 (N), then the alarm will only be triggered if the failure condition is met for N consecutive periods. The period is defined by the [[#Check interval|Check interval]] parameter for this alarm. This feature may be useful if transient failures are to be tolerated, but persistent failures indicate a real problem (e.g. if a brief excess voltage is okay when a device is ramping up, but a consistent excess voltage indicates a hardware problem).&lt;br /&gt;
&lt;br /&gt;
If &#039;Check interval&#039; is 10 and &#039;Trigger count required&#039; is 3, then the alarm will only be triggered if the failure condition is met for 30s in total.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Time triggered first&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]]  is written by the Alarm System.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Time triggered last&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]]  is written by the Alarm System.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Condition&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]]  contains&lt;br /&gt;
the condition on which alarm should trigger for [[#/Alarms tree structure|evaluated alarms]]. See [[#Evaluated Alarm conditions]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Alarm class&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]] is set to one of the existing Alarm classes, e.g. the defaults &amp;quot;Alarm&amp;quot;,&amp;quot;Warning&amp;quot; or a user-defined class, e.g. &amp;quot;Caution&amp;quot;.&lt;br /&gt;
The Alarm class must be defined in the [[#&amp;lt;class-name&amp;gt; subtree|Classes subtree]]. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Alarm message&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;alarm-name&amp;gt; subtree|&amp;lt;alarm-name&amp;gt; subtree]] constains&lt;br /&gt;
the message to be written when alarm triggers. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Classes&#039;&#039;&amp;lt;/span&amp;gt; subtree ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Sub-tree in the /Alarms ODB tree defining each individual alarm class. &lt;br /&gt;
Each class defines the individual action to be performed by a predefined and requested alarm. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;&amp;lt;class-name&amp;gt;&#039;&#039;&amp;lt;/span&amp;gt; subtree ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This subtree in the [[#Classes subtree|Classes subtree]] defines one of the alarm classes of name &#039;&#039;name&#039;&#039;, where &#039;&#039;name&#039;&#039; is substituted for &amp;lt;class-name&amp;gt;. This subtree will be repeated with a different name for each defined class. In the [[#Example of an /Alarms tree|example above]], two &amp;lt;class-name&amp;gt; subtrees are shown, named &amp;quot;Alarm&amp;quot; and &amp;quot;Warning&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Write System Message&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;y&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If this key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]] is set to &amp;quot;y&amp;quot;, a message will be sent to the [[System log]] when an alarm with this [[#Alarm class|alarm class]] is triggered.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Write Elog Message&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;n&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If this key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]] is &lt;br /&gt;
set to &amp;quot;y&amp;quot;, a message will be written to the Elog when an alarm with this  [[#Alarm class|alarm class]] is triggered.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;System message interval&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  60&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
This key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]] contains the &lt;br /&gt;
interval in seconds between successive system messages when an alarm with this   [[#Alarm class|alarm class]] is triggered. &lt;br /&gt;
&lt;br /&gt;
Set this key to 0 to ensure that every alarm is recorded into the [[Message System#MIDAS Log file|MIDAS log file]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;See important note &#039;&#039;&#039; on the [[Alarm System#Implementation of the MIDAS Alarm System|implementation of this key]] by the alarm system.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;System message last&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  0&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]] contains the time when the last alarm system&lt;br /&gt;
message was written. It is filled by the alarm system.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Execute command&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]] may contain a&lt;br /&gt;
command to be executed when an alarm with this   [[#Alarm class|alarm class]] is triggered. &lt;br /&gt;
This parameter can be used to trigger [[#Alarm triggers Email or SMS|Email or SMS]] alerts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Execute interval&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  0&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
When an alarm with this   [[#Alarm class|alarm class]] is triggered, if a valid&lt;br /&gt;
[[#Execute command|Execute command]] is supplied, the command will be repeated at an interval in seconds designated by this key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]], providing the interval set is greater than 0.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Execute last&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  0&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]] &lt;br /&gt;
contains the time when the alarm system last executed the command in the key [[#Execute command|Execute command]].&lt;br /&gt;
It is filled by the alarm system.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Stop run&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;n&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]]  if set to &amp;quot;y&amp;quot;&lt;br /&gt;
will cause the run to stop when an alarm with this [[#Alarm class|alarm class]] is triggered.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Display BGColor&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;red&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]] contains the&lt;br /&gt;
background colour of the alarm banner on the [[mhttpd]]  [[Status Page]]. The alarm banner&lt;br /&gt;
appears if the alarm is triggered.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Display FGColor&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;black&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]] contains the&lt;br /&gt;
foreground colour of the alarm banner on the [[mhttpd]] [[Status Page]].  The alarm banner&lt;br /&gt;
appears if the alarm is triggered.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Alarm sound&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  y&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#&amp;lt;class-name&amp;gt; subtree|&amp;lt;class-name&amp;gt; subtree]] states whether this alarm&lt;br /&gt;
should produce a sound in a user&#039;s web browser or not. (Historically all alarms produced a &lt;br /&gt;
sound, but some experiments want to distinguish between &amp;quot;really bad&amp;quot; and &amp;quot;not so bad&amp;quot; alarms).&lt;br /&gt;
&lt;br /&gt;
Note that all the following conditions must be met for the alarm sound to be heard:&lt;br /&gt;
* This key is set to &#039;y&#039; for the alarm that gets triggered.&lt;br /&gt;
* The ODB key [[#Alarm_system_active|/Alarms/Alarm system active]] is &#039;y&#039;.&lt;br /&gt;
* On the [[Config_Page|Config]] webpage, the &#039;Enable alarm sound&#039; checkbox is ticked and the &#039;Alarm volume&#039; is &amp;gt; 0 (this is a per-user setting).&lt;br /&gt;
* The user hasn&#039;t muted their speakers.&lt;br /&gt;
&lt;br /&gt;
[[Category:ODB Tree]]&lt;br /&gt;
[[Category:Alarms]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Message_Page&amp;diff=3452</id>
		<title>Message Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Message_Page&amp;diff=3452"/>
		<updated>2024-12-18T00:32:30Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages1|[[Message System]]}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
The purpose of the [[mhttpd]] Message page is to&lt;br /&gt;
display the contents of the MIDAS [[Message System|Messages log file]] in blocks of 100 lines starting with the most recent messages. This log file contains system and user messages generated by applications connected to the experiment. See [[Message System]] for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Access the Message page =&lt;br /&gt;
The Message Page is accessed from the [[Status Page]] (or one of the other Pages) by clicking on the menu-button&lt;br /&gt;
&amp;lt;span style=&amp;quot;color: #444444; background-color: #CCCCCC; font-style:italic; font-size: 90; padding:0.25em;&lt;br /&gt;
	padding-left: 0.5em;padding-right: 0.5em;border:1px solid #808080;border-radius: 5px;margin-bottom:1px;&amp;quot;&amp;gt;Messages&amp;lt;/span&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
:If &amp;quot;Messages&amp;quot; button is not present on the Status Page, it may have been [[Status Page#page-switch-buttons|suppressed]].&lt;br /&gt;
&lt;br /&gt;
= Example =&lt;br /&gt;
[[File:message_page.png|500px|left|Figure 1 Example of Messages page for an experiment, Click to enlarge]]&lt;br /&gt;
An example of the Message page is shown in Figure 1.&lt;br /&gt;
&lt;br /&gt;
Error messages (i.e. those of [[Message System#Message Types|Message Type]] MERROR) are shown in red. The other messages shown are information messages of [[Message System#Message Types|Message Type]] MINFO.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear:both&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/div&amp;gt;&lt;br /&gt;
= Filtering and searching for messages =&lt;br /&gt;
&lt;br /&gt;
[[File:message_page_filters.png|500px|left|Figure 2 Filters available, Click to enlarge]]&lt;br /&gt;
As of December 2024, MIDAS messages can be filtered using multiple queries. There are 5 filters available, and you can apply multiple filters simultaneously if desired. &lt;br /&gt;
&lt;br /&gt;
After typing the filters, one needs to &#039;&#039;&#039;press enter or press the &#039;filter&#039; button&#039;&#039;&#039; to start the filtering process. If the filtering takes a long time, the process can be interrupting by clicking the mouse anywhere in the webpage.&lt;br /&gt;
&lt;br /&gt;
Messages that match the filters will then appear at the top of the message list, highlighted in green. All the non-matching messages then follow with the regular background colours.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear:both&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/div&amp;gt;&lt;br /&gt;
The different filters are:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#eb463d; font-weight:bold;&amp;quot;&amp;gt;Program Name&amp;lt;/span&amp;gt;: Filter messages based on the name of the program (e.g. Sequencer, mhttpd)&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#4295ae; font-weight:bold;&amp;quot;&amp;gt;Status&amp;lt;/span&amp;gt;: Show only info/error messages etc&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#56bb6c; font-weight:bold;&amp;quot;&amp;gt;Time&amp;lt;/span&amp;gt;: Find messages at a specific timestamp. It searches from the start of the timestamp associated with the message, so &amp;quot;09:45&amp;quot; will find all messages at 9:45am, &amp;quot;9:45&amp;quot; will not match anything (as the timestamps start with 09), and &amp;quot;09:45:56.237&amp;quot; will match only messages at that specific timestamp. Only the time portion can be searched, not the date.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#f3be69; font-weight:bold;&amp;quot;&amp;gt;Search Text&amp;lt;/span&amp;gt;: Search the actual message content&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#98bdf9; font-weight:bold;&amp;quot;&amp;gt;Hour&amp;lt;/span&amp;gt;: Show only the last x hours/days&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]]&lt;br /&gt;
[[Category:Messages]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Message_page_filters.png&amp;diff=3451</id>
		<title>File:Message page filters.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Message_page_filters.png&amp;diff=3451"/>
		<updated>2024-12-17T23:49:37Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Message_page.png&amp;diff=3450</id>
		<title>File:Message page.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Message_page.png&amp;diff=3450"/>
		<updated>2024-12-17T23:48:27Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: Bsmith uploaded a new version of File:Message page.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Example of mhttpd messages page&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3441</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3441"/>
		<updated>2024-09-23T20:37:12Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: Document main user-facing control functions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. 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).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
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 &amp;quot;virtual&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
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.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&lt;br /&gt;
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.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: Without the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* 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.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; 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 &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;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.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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 &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || 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.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; 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.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; 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 &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
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 &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| 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/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). 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 &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3440</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3440"/>
		<updated>2024-09-23T19:50:27Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Mouse event function */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram if several histograms are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram if several are used&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| 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 &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, 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.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=/Experiment_ODB_tree&amp;diff=3435</id>
		<title>/Experiment ODB tree</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=/Experiment_ODB_tree&amp;diff=3435"/>
		<updated>2024-08-30T17:46:55Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Transition timeout */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[odbedit]]&lt;br /&gt;
* &lt;br /&gt;
*&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Creating the &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;/Experiment&#039;&#039;&amp;lt;/span&amp;gt;  tree ==&lt;br /&gt;
The &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;/Experiment&#039;&#039;&amp;lt;/span&amp;gt; ODB tree is created automatically when the &#039;&#039;&#039;[[odbedit #Creating the ODB|ODB is first created]]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
The &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;/Experiment&#039;&#039;&amp;lt;/span&amp;gt; ODB tree contains information relevent to the experiment. Other optional keys are added by &#039;&#039;&#039;[[mhttpd]]&#039;&#039;&#039; or by &lt;br /&gt;
the user to customize their experiment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
When initially created, the &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;/Experiment&#039;&#039;&amp;lt;/span&amp;gt; tree contains the following keys:&lt;br /&gt;
 [local:midas:S]/&amp;gt;ls -lrt /experiment&lt;br /&gt;
 Key name                        Type    #Val  Size  Last Opn Mode Value&lt;br /&gt;
 ---------------------------------------------------------------------------&lt;br /&gt;
 Experiment                      DIR&lt;br /&gt;
    Name                         STRING  1     32    14s  0   RWD  midas&lt;br /&gt;
    Buffer sizes                 DIR&lt;br /&gt;
        SYSMSG                   DWORD   1     4     11h  0   RWD  100000&lt;br /&gt;
    Security                     DIR&lt;br /&gt;
        Enable non-localhost RPC BOOL    1     4     46s  0   RWD  n&lt;br /&gt;
        RPC ports                DIR&lt;br /&gt;
           ODBEdit               DWORD   1     4     38m  0   RWD  0&lt;br /&gt;
        RPC hosts                DIR&lt;br /&gt;
           Allowed hosts         STRING  10    256   38m  3   RWD&lt;br /&gt;
                                         [0]             localhost&lt;br /&gt;
                                         [1]&lt;br /&gt;
                                         [2]&lt;br /&gt;
                                         [3]&lt;br /&gt;
                                         [4]&lt;br /&gt;
                                         [5]&lt;br /&gt;
                                         [6]&lt;br /&gt;
                                         [7]&lt;br /&gt;
                                         [8]&lt;br /&gt;
                                         [9]&lt;br /&gt;
        Disable RPC hosts check  BOOL    1     4     46s  0   RWD  n&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following example shows the &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;/Experiment&#039;&#039;&amp;lt;/span&amp;gt; tree for a typical experiment: &lt;br /&gt;
&lt;br /&gt;
 [local:midas:R]/&amp;gt;ls -lrt /experiment&lt;br /&gt;
 Key name                        Type    #Val  Size  Last Opn Mode Value&lt;br /&gt;
 ---------------------------------------------------------------------------&lt;br /&gt;
 Experiment                      DIR&lt;br /&gt;
    Name                         STRING  1     32    7s   0   RWD  midas&lt;br /&gt;
    &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;Buffer sizes&amp;lt;/span&amp;gt;                 DIR&lt;br /&gt;
        SYSMSG                   DWORD   1     4     23h  0   RWD  100000&lt;br /&gt;
        SYSTEM                   DWORD   1     4     23h  0   RWD  640000000&lt;br /&gt;
        BUF0                     DWORD   1     4     23h  0   RWD  80000000&lt;br /&gt;
        BUF1                     DWORD   1     4     23h  0   RWD  80000000&lt;br /&gt;
        .......    &amp;lt;span style=&amp;quot;color: green;&amp;quot;&amp;gt;&#039;&#039;other user-defined buffers not shown &#039;&#039;&amp;lt;/span&amp;gt; &lt;br /&gt;
    &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;Security&amp;lt;/span&amp;gt;                     DIR&lt;br /&gt;
        Enable non-localhost RPC BOOL    1     4     38m  0   RWD  n&lt;br /&gt;
        &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;RPC ports&amp;lt;/span&amp;gt;                DIR&lt;br /&gt;
            ODBEdit              DWORD   1     4     38m  0   RWD  0&lt;br /&gt;
            Logger               DWORD   1     4     35m  0   RWD  0&lt;br /&gt;
            mhttpd               DWORD   1     4     23m  0   RWD  0&lt;br /&gt;
        &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;RPC hosts&amp;lt;/span&amp;gt;                DIR&lt;br /&gt;
            Allowed hosts        STRING  10    256   38m  3   RWD&lt;br /&gt;
                                         [0]             localhost&lt;br /&gt;
                                         [1]&lt;br /&gt;
                                         [2]&lt;br /&gt;
                                         [3]&lt;br /&gt;
                                         [4]&lt;br /&gt;
                                         [5]&lt;br /&gt;
                                         [6]&lt;br /&gt;
                                         [7]&lt;br /&gt;
                                         [8]&lt;br /&gt;
                                         [9]&lt;br /&gt;
        Disable RPC hosts check  BOOL    1     4     38m  0   RWD  n&lt;br /&gt;
    Start-Stop Buttons           BOOL    1     4     8h   0   RWD  y&lt;br /&gt;
    Pause-Resume Buttons         BOOL    1     4     8h   0   RWD  n&lt;br /&gt;
   &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;Status items&amp;lt;/span&amp;gt;                DIR&lt;br /&gt;
        Experiment Name -&amp;gt; /Experiment/Name&lt;br /&gt;
    MAX_EVENT_SIZE               DWORD   1     4     23h  0   RWD  4194304&lt;br /&gt;
    Midas server port            DWORD   1     4     3m   0   RWD  1175&lt;br /&gt;
    Transition debug flag        INT     1     4     23h  0   RWD  0&lt;br /&gt;
    Transition connect timeout   INT     1     4     23h  0   RWD  10000&lt;br /&gt;
    Transition timeout           INT     1     4     23h  0   RWD  120000&lt;br /&gt;
    &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;edit on start&amp;lt;/span&amp;gt;                DIR&lt;br /&gt;
        experiment number        DWORD   1     4     2h   0   RWD  9499&lt;br /&gt;
        field                    STRING  1     32    2h   0   RWD  19000.2(0.0)G&lt;br /&gt;
        comment-&amp;gt; /Experiment/run parameters/comment&lt;br /&gt;
                                 STRING  1     80    2h   0   RWD  Testing with low beam&lt;br /&gt;
        Number of channels -&amp;gt; /Run Parameters/number of channels&lt;br /&gt;
                                 DWORD   1     4     2h   0   RWD  20&lt;br /&gt;
        Write Data -&amp;gt; /Logger/Write data&lt;br /&gt;
                                 BOOL    1     4     2h   0   RWD  n&lt;br /&gt;
        Number of cycles -&amp;gt; /Equipment/FIFO_acq/frontend/hardware/num cycles&lt;br /&gt;
                                 DWORD   1     4     2h   0   RWD  0&lt;br /&gt;
    &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;Parameter Comments&amp;lt;/span&amp;gt;           DIR                   &lt;br /&gt;
        field                    STRING  1     32    &amp;gt;99d 0   RWD  &amp;lt;i&amp;gt;Entered in Tesla unit&amp;lt;/i&amp;gt;&lt;br /&gt;
        Num cycles               STRING  1     80    &amp;gt;99d 0   RWD  &amp;lt;i&amp;gt;Stop run after num cycles is reached. Enter 0 to disable (free running)&amp;lt;/i&amp;gt;&lt;br /&gt;
    &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;Run Parameters&amp;lt;/span&amp;gt;               DIR&lt;br /&gt;
        Comment                  STRING  1     80    2h   0   RWD  Testing with low beam&lt;br /&gt;
        Run Description          STRING  1     256   7h   0   RWD  Sequencer Tests&lt;br /&gt;
        Number of channels       DWORD   1     4     2h   0   RWD  20&lt;br /&gt;
    &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;Lock when running&amp;lt;/span&amp;gt;            DIR&lt;br /&gt;
        Num channels -&amp;gt; /Run Parameters/number of channels&lt;br /&gt;
                                 DWORD   1     4     2h   0   RWD  20&lt;br /&gt;
    &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;edit on sequence&amp;lt;/span&amp;gt;             DIR&lt;br /&gt;
        title                    STRING  1     128   2h   0   RWD  none&lt;br /&gt;
        experiment number        DWORD   1     4     2h   0   RWD  9438&lt;br /&gt;
        experimenter             STRING  1     32    2h   0   RWD  gls&lt;br /&gt;
        sample                   STRING  1     36    2h   0   RWD  NA&lt;br /&gt;
        run description -&amp;gt; /Experiment/run parameters/run description&lt;br /&gt;
                                 STRING  1     256   7h   0   RWD  Sequencer Tests&lt;br /&gt;
        Write Data -&amp;gt; /Logger/Write data&lt;br /&gt;
                                 BOOL    1     4     2h   0   RWD  n&lt;br /&gt;
        Number of cycles -&amp;gt; /Equipment/FIFO_acq/frontend/hardware/num cycles&lt;br /&gt;
                                 DWORD   1     4     2h   0   RWD  0&lt;br /&gt;
    Prevent start on alarms      BOOL    1     4     22h  0   RWD  n&lt;br /&gt;
    Prevent start on required    BOOL    1     4     22h  0   RWD  n&lt;br /&gt;
    &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;Status items&amp;lt;/span&amp;gt;                 DIR&lt;br /&gt;
        Experiment Name -&amp;gt; /Experiment/Name&lt;br /&gt;
                                 STRING  1     32    7s   0   RWD  midas&lt;br /&gt;
    Start-Stop Buttons           BOOL    1     4     5h   0   RWD  y&lt;br /&gt;
    Pause-Resume Buttons         BOOL    1     4     5h   0   RWD  n&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Keys in &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;/Experiment&#039;&#039;&amp;lt;/span&amp;gt; tree==&lt;br /&gt;
&lt;br /&gt;
The keys in the ODB &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;/Experiment tree&#039;&#039;&amp;lt;/span&amp;gt; &lt;br /&gt;
are described in the following sections.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Name &#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]]&lt;br /&gt;
contains the name of the experiment. It is created by the MIDAS system when the ODB is created.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Buffer Sizes &#039;&#039;&amp;lt;/span&amp;gt; subtree ===&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&lt;br /&gt;
This key in the [[#top|/Experiment tree]] is a subtree to contain the sizes of the &lt;br /&gt;
Midas Buffers for the experiment. Created by the MIDAS system with default values. The sizes can be changed to optimize the memory usage. See &#039;&#039;&#039;[[Event Buffer]]&#039;&#039;&#039; Size(s) for details.  Other user-defined&lt;br /&gt;
buffers may be present (e.g. for &#039;&#039;&#039;[[event filtering]]&#039;&#039;&#039;).&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;SYSMSG &#039;&#039;&amp;lt;/span&amp;gt;  ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  100000 Bytes&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#Buffer Sizes|/Experiment/Buffer Sizes subtree]]&lt;br /&gt;
contains the size of SYSMSG buffer. This buffer is used for MIDAS messages.&lt;br /&gt;
The default value of this key is defined by MESSAGE_BUFFER_SIZE in $MIDASSYS/include/msystem.h .&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;SYSTEM &#039;&#039;&amp;lt;/span&amp;gt;  ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  32MiBytes&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#Buffer Sizes|/Experiment/Buffer Sizes subtree]]&lt;br /&gt;
contains the size of SYSTEM buffer.  The default value of this key is&lt;br /&gt;
DEFAULT_BUFFER_SIZE = 32 MiB (defined in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/midas_8h_source.html midas.h]). &lt;br /&gt;
The actual SYSTEM buffer size is set by this key. To increase the SYSTEM buffer size (e.g. for very large events), see [[Event Buffer]].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;MAX_EVENT_SIZE &#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  4MiBytes&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]]&lt;br /&gt;
specifies the maximum event size that can be acquired. The default value of this key is&lt;br /&gt;
DEFAULT_MAX_EVENT_SIZE =  4 MiB  (defined in [http://ladd00.triumf.ca/~daqweb/doc/midas-devel/doc/html/midas_8h_source.html midas.h]). &lt;br /&gt;
The actual maximum event size is set by this key, and can be increased for larger events if needed (see  [[Event Buffer]] for more information).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;midas server port&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  1175&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key is created when [[mserver]] is started for the first time. It contains the default value of the port used by [[mserver]]. This is set to MIDAS_TCP_PORT = 1175 ( midas.h).  A different port can be used by starting [[mserver]] with the -p argument.&lt;br /&gt;
&lt;br /&gt;
;NOTE&lt;br /&gt;
: This key was added in May 2015 (see [[Security]]). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Start-Stop Buttons&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; y&lt;br /&gt;
 &amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] is added automatically by &#039;&#039;&#039;[[mhttpd]]&#039;&#039;&#039; to allow the user to suppress the Start or Stop buttons from appearing on the [[Status Page]]. By default, Start/Stop buttons are shown.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Pause-Resume Buttons&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; (no default, must be manually created)&lt;br /&gt;
 &amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] can be created and set to &amp;quot;n&amp;quot; to hide the Pause/Resume buttons on the [[Status Page]] (so that only Start/Stop buttons will be visible). If the key doesn&#039;t exist, or is set to &amp;quot;y&amp;quot;, the Pause/Resume buttons will be shown.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Transition debug flag &#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  0&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] contains a flag that, if set to 1, causes messages reporting transition progress to be output.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Transition connect timeout&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  10000&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] contains the&lt;br /&gt;
value of timeout for remote rpc connect&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Transition timeout&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  120000&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] contains the&lt;br /&gt;
value of timeout for transition (in ms)&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Prevent start on alarms&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;n&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt;  &lt;br /&gt;
This key in the [[#top|/Experiment tree]] if set true &lt;br /&gt;
will prevent the run from starting if an [[Alarm System|alarm]] is true, i.e.&lt;br /&gt;
the run start procedure will fail if an alarm has been [[/Alarms ODB tree#Triggered|Triggered]] for a client, provided a valid [[/Alarms ODB tree#alarms class|alarms class]] has&lt;br /&gt;
been entered in the client&#039;s [[/Programs ODB tree#Alarm class|Alarm class]] key.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Prevent start on required program&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;n&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] if set true (&amp;quot;y&amp;quot;)&lt;br /&gt;
will prevent the run from starting if one of the &#039;&#039;&#039;required&#039;&#039;&#039; clients is not running. A client is flagged as &amp;quot;required&amp;quot; by setting the ODB key &lt;br /&gt;
[[/Programs ODB tree#Required|Required]] to &amp;quot;y&amp;quot;.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Edit on Sequence &#039;&#039;&amp;lt;/span&amp;gt; subtree ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
This  optional subdirectory  in the [[#top|/Experiment tree]] may&lt;br /&gt;
contain user-defined parameters which will be displayed for editing at the start of each [[Sequencer|Sequence]]. See [[Edit-on-Sequence Parameters]] for details. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Edit on Start &#039;&#039;&amp;lt;/span&amp;gt; subtree ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
This  optional subdirectory  in the [[#top|/Experiment tree]] may&lt;br /&gt;
contain user-defined parameters which will be displayed for editing at the beginning of each run. See &#039;&#039;&#039;[[Edit-on-start Parameters]]&#039;&#039;&#039; for details. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Lock when running &#039;&#039;&amp;lt;/span&amp;gt; subtree ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This  optional subdirectory  in the [[#top|/Experiment tree]] contains user-defined links to ODB parameters to prevent them being changed when the run is in progress. See &#039;&#039;&#039;[[Lock when running]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Parameter Comments &#039;&#039;&amp;lt;/span&amp;gt; subtree ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This  optional subdirectory   in the [[#top|/Experiment tree]] may contain user-defined parameter comments that give more information about&lt;br /&gt;
the &#039;&#039;&#039;[[Edit-on-start Parameters]]&#039;&#039;&#039;.  See [[Edit-on-start Parameters#edit-on-start parameter comments|creating parameter comments]] for details.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Run Parameters &#039;&#039;&amp;lt;/span&amp;gt; subtree ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This  optional subdirectory   in the [[#top|/Experiment tree]] may contain user-defined parameters or parameter(s)&lt;br /&gt;
with reserved names (i.e. [[#Run Description|Run Description]]).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Run Description&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
This ODB key is used by the [[Sequencer]] [[Sequencer#RUNDESCRIPTION|RUNDESCRIPTION]] command to store the&lt;br /&gt;
run description.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;&amp;lt;parameter name&amp;gt;&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
The user may define parameters here e.g. for linking as  &#039;&#039;&#039;[[Edit-on-start Parameters|Edit-on-start]]&#039;&#039;&#039; or &#039;&#039;&#039;[[Edit-on-Sequence Parameters|Edit-on-Sequence]]&#039;&#039;&#039; parameters.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Status items&#039;&#039;&amp;lt;/span&amp;gt; subtree ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOTE:&#039;&#039;&#039; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] is a subtree  which by &amp;lt;b&amp;gt;default (Hmmm no!)&amp;lt;/b&amp;gt; contains a link to the [[#Name|experiment name]]. Any links or keys&lt;br /&gt;
created by the user in this optional subdirectory will be displayed on the [[mhttpd]] main status page. &lt;br /&gt;
&lt;br /&gt;
This subtree may not be present by default.&lt;br /&gt;
In order to make any ODB parameters visible in the status page, create first the subtree and a link to the experiment name inside this newly subtree such as: &lt;br /&gt;
&lt;br /&gt;
 cd /experiment&lt;br /&gt;
 mkdir &amp;quot;Status items&amp;quot;&lt;br /&gt;
 cd &amp;quot;Status items&amp;quot;&lt;br /&gt;
 ln /experiment/name &amp;quot;Experiment Name&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
Any following link to any ODB parameters will be displayed in the status page above the Equipment list.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Security&#039;&#039;&amp;lt;/span&amp;gt; subtree ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This optional subtree   in the [[#top|/Experiment tree]] is created when the &#039;&#039;&#039;[[odbedit]]&#039;&#039;&#039; commands  &amp;lt;span style=&amp;quot;color:saddlebrown; font-style:bold; &amp;quot;&amp;gt;passwd&amp;lt;/span&amp;gt; or  &amp;lt;span style=&amp;quot;color:saddlebrown; font-style:bold; &amp;quot;&amp;gt;webpasswd&amp;lt;/span&amp;gt; are issued. It enables a user to set up security features. See &#039;&#039;&#039;[[Security]]&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Disable RPC hosts check&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;n&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Setting the key to &amp;quot;n&amp;quot; (the default) causes access by unauthorized hosts to be prevented by the system checking the RPC access control list (see [[#RPC hosts subtree|RPC hosts/Allowed hosts]]).&lt;br /&gt;
&lt;br /&gt;
If MIDAS clients have to connect from random hosts (i.e. dynamically assigned random DHCP addresses), one can disable the host name checks by &lt;br /&gt;
setting this key to &amp;quot;y&amp;quot;. This configuration is insecure and should only be done on a private network behind a firewall. See [https://midas.triumf.ca/elog/Midas/1080 Note 1080].&lt;br /&gt;
&lt;br /&gt;
; NOTE&lt;br /&gt;
: This key was added August 2015. Used by [[mserver]] to improve network [[Security]] for the MIDAS experiment. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Enable non-localhost RPC&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;n&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
The default value of this key is &amp;quot;n&amp;quot;, denying access by external network connections.  If running an experiment that requires external network connections, this key must be set to &amp;quot;y&amp;quot; and the key [[#RPC hosts|RPC hosts]] must be filled. See [https://midas.triumf.ca/elog/Midas/1080 Note 1080].&lt;br /&gt;
&lt;br /&gt;
; NOTE&lt;br /&gt;
: This key was added August 2015. Used by [[mserver]] to improve network [[Security]] for the MIDAS experiment. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;RPC ports&#039;&#039;&amp;lt;/span&amp;gt; subtree ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This subtree in the [[#Security subtree|/Experiment/Security subtree]] is new as of August 2015. It is created by the MIDAS system. &lt;br /&gt;
It is part of the improved security features of MIDAS (See &#039;&#039;&#039;[[Security]]&#039;&#039;&#039;). This subdirectory contains the names of MIDAS clients and their fixed TCP port numbers. When a client is started for the first time, an entry named for the client will be created in this subtree.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;&amp;lt;client-name&amp;gt;&#039;&#039;&amp;lt;/span&amp;gt; =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  0&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When a MIDAS client is started, a key will be created named for that client. It will contain the fixed TCP port number that the MIDAS client is using.&lt;br /&gt;
Clients started on the local host will have TCP port numbers of 0 (default).&lt;br /&gt;
&lt;br /&gt;
Once a remote [[Frontend Operation|frontend]] is bound to a fixed port, appropriate openings can be made in the firewall, etc. Default port number value &lt;br /&gt;
will be 0 meaning &amp;quot;use random port&amp;quot;, same as now. See [https://midas.triumf.ca/elog/Midas/1079].&lt;br /&gt;
&lt;br /&gt;
;NOTE&lt;br /&gt;
: This feature was added  August 2015. It is part of the improved security features of MIDAS (See &#039;&#039;&#039;[[Security]]&#039;&#039;&#039;). &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;RPC hosts&#039;&#039;&amp;lt;/span&amp;gt; subtree ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This subtree in the [[#Security subtree|/Experiment/Security subtree]] is new as of August 2015. It is created by the MIDAS system. &lt;br /&gt;
It is part of the improved security features of MIDAS (See &#039;&#039;&#039;[[Security]]&#039;&#039;&#039;).&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;Allowed hosts (rpc hosts)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &amp;lt;!-- need this because of duplicate &amp;quot;Allowed hosts&amp;quot; in &amp;quot;mhttpd hosts/&amp;quot; subtree --&amp;gt;&lt;br /&gt;
===== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Allowed hosts&#039;&#039;&amp;lt;/span&amp;gt; =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING array&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;localhost&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
This key in the [[#RPC hosts subtree|/Experiment/Security/RPC hosts subtree]] is new as of August 2015 and is created by the system (i.e. midas.c).&lt;br /&gt;
&lt;br /&gt;
It is part of the improved security features of MIDAS (See &#039;&#039;&#039;[[Security]]&#039;&#039;&#039;) and is used to maintain a Network Access Control list. &lt;br /&gt;
The access control list array is self-growing - it will have at least 10 empty entries at the end at all times.&lt;br /&gt;
&lt;br /&gt;
The default value for {{Odbpath|path=Allowed hosts[0]}} is &amp;quot;localhost&amp;quot;, which will reject all external connections, even when permitted by [[#Enable external RPC connections|Enable external RPC connections]].  The user will be required to enter the names of all machines that will run midas clients in this array. See [https://midas.triumf.ca/elog/Midas/1090].&lt;br /&gt;
&lt;br /&gt;
All clients&#039; db_watch() routines watch the access control list and automatically reload it when it is changed, so there is no need to restart clients.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color:silver&amp;quot;&amp;gt;&lt;br /&gt;
== &amp;lt;span style=&amp;quot;font-size:120%;&amp;quot;&amp;gt;The following keys in the /Experiment tree are OBSOLETE:&amp;lt;/span&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;midas http port&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  8080&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] is created when [[mhttpd]] is run for the first time. It contains the listening port for the HTTP server (default 8080). This will be redirected to the secure HTTPS port given by [[#midas https port|midas https port]] if the key [[#http redirect to https|http redirect to https]] is set to &amp;quot;y&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If the http port are supplied with the &amp;quot;--http&amp;quot; option when starting  {{Utility|name=mhttpd}}, the port supplied will overwrite the default value stored in this key. See [[mhttpd]] for details. &lt;br /&gt;
&lt;br /&gt;
;NOTE&lt;br /&gt;
: This [[Security]] feature was added to  [[mhttpd]] in August 2015&lt;br /&gt;
: As of March 2020, this key has been deprecated in favour of the [[Webserver ODB tree]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;midas https port&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  8443&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] is created when [[mhttpd]] is run for the first time. It contains the listening port (default 8443) for the secure &lt;br /&gt;
HTTPS/SSL server ([https://bitbucket.org/tmidas/midas/src/ecb9a8537448a8a43f7f9a2bfdb82e578208cde3/doc/mongoose/?at=develop Mongoose]). &lt;br /&gt;
&lt;br /&gt;
If the https port is supplied with the &amp;quot;--https&amp;quot; option when starting  {{Utility|name=mhttpd}}, the port supplied will overwrite the default value stored in this key. See [[mhttpd]] for details.&lt;br /&gt;
&lt;br /&gt;
;NOTE&lt;br /&gt;
: This [[Security]] feature was added to [[mhttpd]] in August 2015&lt;br /&gt;
: As of March 2020, this key has been deprecated in favour of the [[Webserver ODB tree]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;http redirect to https&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  y&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] is created when [[mhttpd]] is run for the first time.  If set to &amp;quot;y&amp;quot;, connections to the http port (specified by the key [[#midas http port|midas http port]] will be redirected to hte https port (specified by the key [[#midas https port|midas https port]], i.e. the listening port for the secure &lt;br /&gt;
HTTPS/SSL server ([https://bitbucket.org/tmidas/midas/src/ecb9a8537448a8a43f7f9a2bfdb82e578208cde3/doc/mongoose/?at=develop Mongoose]). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;NOTE&lt;br /&gt;
: This [[Security]] feature was added to [[mhttpd]] in August 2015&lt;br /&gt;
: As of March 2020, this key has been deprecated in favour of the [[Webserver ODB tree]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;mhttpd hosts&#039;&#039;&amp;lt;/span&amp;gt; subtree ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This subtree in the [[#Security subtree|/Experiment/Security subtree]] is new as of August 2015. It is created by the MIDAS system. &lt;br /&gt;
It is part of the improved security features of MIDAS (See &#039;&#039;&#039;[[Security]]&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
;NOTE&lt;br /&gt;
: As of March 2020, this key has been deprecated in favour of the [[Webserver ODB tree]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;Allowed hosts (mhttpd hosts)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &amp;lt;!-- need this because of duplicate &amp;quot;Allowed hosts&amp;quot; in &amp;quot;rpc hosts/&amp;quot; subtree --&amp;gt;&lt;br /&gt;
===== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Allowed hosts&#039;&#039;&amp;lt;/span&amp;gt; =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING array&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;localhost&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
This key in the [[#RPC hosts subtree|/Experiment/Security/mhttpd hosts subtree]] is new as of August 2015 and is created by the system (i.e. midas.c).&lt;br /&gt;
&lt;br /&gt;
It is part of the improved security features of MIDAS (See &#039;&#039;&#039;[[Security]]&#039;&#039;&#039;) and is used to maintain an access control list for [[mhttpd]].  An empty list means free access from everywhere. Access control is also controlled by the [[mhttpd]] &amp;quot;-a&amp;quot; command line arguments. Hosts supplied by the &amp;quot;-a&amp;quot; command line arguments are not added to the access control list by the system.&lt;br /&gt;
&lt;br /&gt;
The access control list is watched by  {{Utility|name=mhttpd}}, therefore there is no need to restart it after updating the list.&lt;br /&gt;
&lt;br /&gt;
;NOTE&lt;br /&gt;
: As of March 2020, this key has been deprecated in favour of the [[Webserver ODB tree]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;CSS File&#039;&#039;&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;mhttpd.css&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] contains the name of the [[Custom Page Features#MIDAS stylesheet|MIDAS stylesheet]] file for the use of those writing [[Custom Page|Custom Web Pages]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;NOTE&lt;br /&gt;
: Serving resource file is now done differently.  See [[Custom Page Features#MIDAS resource files|serving MIDAS resource file]]. Also mhttpd.css has be replaced by midas.css.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;JS File&#039;&#039;&amp;lt;/span&amp;gt; ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;mhttpd.js&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]] contains the name of the [[mhttpd.js|Javascript library]] file for the use of those writing [[Custom Page|Custom Web Pages]].&lt;br /&gt;
&lt;br /&gt;
;NOTE&lt;br /&gt;
: Serving resource file is now done differently. See [[Custom Page Features#MIDAS resource files|serving MIDAS resource files]]. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Menu Buttons&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:2;-webkit-column-count:2&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;Status, ODB, Messages, ELog, Alarms, Programs, History, Sequencer, Chat, Config, Help&amp;quot;&lt;br /&gt;
 &amp;lt;/div&amp;gt; &lt;br /&gt;
This key in the [[#top|/Experiment tree]]&lt;br /&gt;
is added automatically by &#039;&#039;&#039;[[mhttpd]]&#039;&#039;&#039; to allow the Menu buttons that appear on the &#039;&#039;&#039;[[mhttpd|Main Status Page]]&#039;&#039;&#039; to be customized by &lt;br /&gt;
removing unnecessary buttons or by changing their order. &lt;br /&gt;
&lt;br /&gt;
The Start/Stop/Pause/Resume buttons are not now included in  &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Menu Buttons&#039;&#039;&amp;lt;/span&amp;gt;. &lt;br /&gt;
* To suppress/display the Start/Stop buttons use key [[#Start-Stop Buttons|Start-Stop Buttons]].&lt;br /&gt;
* To display/suppress the Pause/Resume buttons, use key [[#Pause-Resume Buttons|Pause-Resume Buttons]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; Note&lt;br /&gt;
:If [[MSCB Page#MIDAS with MSCB support|MSCB support]] is built into MIDAS, the default will also include the MSCB Menu button (see [[MSCB Page]]).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Mongoose listening_port&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;8080r,8443s&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:This key in the [[#top|/Experiment tree]] existed for a short time in midas versions May-August 2015. &lt;br /&gt;
: It has been &#039;&#039;&#039;replaced by the ODB keys [[#midas http port|midas http port]] and [[#midas https port|midas https port]].&#039;&#039;&#039; &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;span style=&amp;quot;font-size:80%&amp;quot;&amp;gt;&lt;br /&gt;
This key in the [[#top|/Experiment tree]] was created when [[mhttpd]] was run for the first time. It contained the listening ports for the secure &lt;br /&gt;
HTTPS/SSL server ([https://bitbucket.org/tmidas/midas/src/ecb9a8537448a8a43f7f9a2bfdb82e578208cde3/doc/mongoose/?at=develop Mongoose]). The ports are the HTTP port (default 8080) which is to be redirected to the secure HTTPS port (default 8443). &lt;br /&gt;
If ports were supplied with the &amp;quot;--mg&amp;quot; option when starting  {{Utility|name=mhttpd}}, their values will overwrite the default values stored in this key. See [[mhttpd]] for details. &lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Mongoose access_control_list&#039;&#039;&amp;lt;/span&amp;gt;  ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:This key in the [[#top|/Experiment tree]] existed for a short time in midas versions May-August 2015. &lt;br /&gt;
:&#039;&#039;&#039;Use the [[#mhttpd hosts subtree|mhttpd hosts/Allowed hosts]] access control list or the [[mhttpd]] &amp;quot;-a hostname&amp;quot; parameter to restrict access.&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;span style=&amp;quot;font-size:80%&amp;quot;&amp;gt;&lt;br /&gt;
This key in the [[#top|/Experiment tree]] is created when [[mhttpd]] is run for the first time. It contains the access control list (ACL) for the [https://bitbucket.org/tmidas/midas/src/ecb9a8537448a8a43f7f9a2bfdb82e578208cde3/doc/mongoose/?at=develop Mongoose] web server.  By default, this key is empty and there is no access control.  The format of the ACL is described under access_control_list at [https://bitbucket.org/tmidas/midas/src/ecb9a8537448a8a43f7f9a2bfdb82e578208cde3/doc/mongoose/Options.md?at=develop Mongoose Configuration Options].&lt;br /&gt;
&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span style=&amp;quot;font-size:120%; font-style:bold&amp;quot;&amp;gt;The following keys in the /Experiment/Security subdirectory are OBSOLETE - replaced by the [[Security|security features]] added Aug 2015&amp;lt;/span&amp;gt; ===&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Password &#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
;Notes&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;This feature pre-dates the improved [[Security]] features (August 2015)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Do not set this key except through the {{Odbedit cmd|cmd=passwd}}. Setting an unencrypted password will lock you out of the ODB unless {{Utility|name=odbedit}} is listed in the [[#Allowed programs subtree|allowed programs subtree]].&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;This security feature is not proof against malicious access. See [[Security]] for details.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This optional key in the [[#Security|/Experiment/Security subtree]]&lt;br /&gt;
contains the encrypted password. This Key is created when the {{Odbedit cmd|cmd=passwd}} is issued. See [[Security#Restrict user access|Restrict user access]] for details.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Allowed Hosts&#039;&#039;&amp;lt;/span&amp;gt; subtree ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Notes&lt;br /&gt;
* This subtree pre-dates the improved [[Security]] features (August 2015) and is not to be confused with  [[#RPC hosts subtree|RPC hosts/Allowed hosts]] array.&lt;br /&gt;
* This security feature is not proof against malicious access. See [[Security]] for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This subtree in the  [[#Security|/Experiment/Security subtree]] is created when the {{Odbedit cmd|cmd=passwd}} is issued. When created, this subtree is empty.&lt;br /&gt;
Optionally, it may contain  user-defined names of remote hosts allowed to have free access (i.e. without password) to the current experiment. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Allowed programs &#039;&#039;&amp;lt;/span&amp;gt; subtree ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Notes&lt;br /&gt;
* This subtree pre-dates the improved [[Security]] features (August 2015)&lt;br /&gt;
* This feature is not proof against malicious access. See [[Security]] for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This optional subtree  in the  [[#Security|/Experiment/Security subtree]] is created when the {{Odbedit cmd|cmd=passwd}} is issued. When created, this subtree is empty.&lt;br /&gt;
Optionally, it may  contain user-defined names of clients allowed to have free access (i.e. without password) to the current experiment. See [[Security #Allowed programs]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;span style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Web Password&#039;&#039;&amp;lt;/span&amp;gt; ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Status: OBSOLETE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
;Notes&lt;br /&gt;
* This key pre-dates (and has been superceded by)  the improved [[Security]] features (August 2015)&lt;br /&gt;
* This feature is not proof against malicious access. See [[Security]] for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If [[Security#Restrict user access|restriction on user web access]] has been set up, this key in the  [[#Security|/Experiment/Security subtree]]&lt;br /&gt;
will contain an encrypted password for Web server access. This key is created by using the {{Odbedit cmd|cmd=webpasswd}}. &lt;br /&gt;
&lt;br /&gt;
If this key is present, the user will be requested to provide the &amp;quot;Web Password&amp;quot; when accessing the requested experiment in &amp;quot;Write Access&amp;quot; mode. The &amp;quot;Read Only Access&amp;quot; mode is still available (without a password) to all users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt; &amp;lt;!-- obsolete ..  silver --&amp;gt;&lt;br /&gt;
[[Category:ODB Tree]] [[Category:Experiment]] [[Category:Security]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Event_Structure&amp;diff=3434</id>
		<title>Event Structure</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Event_Structure&amp;diff=3434"/>
		<updated>2024-08-30T17:34:18Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
== Links ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:4;-moz-column-count:4;-webkit-column-count:4&amp;quot;&amp;gt;&lt;br /&gt;
* [[MIDAS Event Construction]] &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
A midas data file contains midas events. The structure of these of events is detailed on this page. The same structure is used both for passing events between different programs (using [[Event_Buffer|event buffers]]) and for writing the data to disk.&lt;br /&gt;
&lt;br /&gt;
Midas data files are written by the [[Mlogger|mlogger]] program and end in the .mid extension. Files can optionally be [[Keys_in_the_ODB_/Logger/Channels_subtree#Compress|compressed]] using gzip, lz4, bzip2 or pbzip2 (thus ending in .mid.gz etc).&lt;br /&gt;
&lt;br /&gt;
Two event formats are supported by the frontends, although the vast majority of cases use the &amp;quot;MIDAS&amp;quot; format: &lt;br /&gt;
* [[#MIDAS Format Event|&amp;quot;MIDAS&amp;quot;]]  &lt;br /&gt;
* [[#FIXED Format Event|&amp;quot;FIXED&amp;quot;]] &lt;br /&gt;
&lt;br /&gt;
Note that a frontend cannot write data directly into ROOT format. A conversion to ROOT may be done (e.g. by the data logger [[Mlogger]]) from one of the [[Equipment List Parameters#Format|supported formats]].&lt;br /&gt;
&lt;br /&gt;
== Overall File Format ==&lt;br /&gt;
&lt;br /&gt;
A midas file is simply a concatenation of midas events. There are no special headers or lookup tables, but it may contain a couple of special events (e.g. a dump of the ODB content). See [[#Special Events|special events]] for more details.&lt;br /&gt;
&lt;br /&gt;
== MIDAS Format Event ==&lt;br /&gt;
The MIDAS event format is a variable length event format. It uses &amp;quot;banks&amp;quot; as subsets of an event. A bank is composed of a bank header followed by the data. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span  style=&amp;quot;color: black; background-color: mintcream; font-size: 110%; font-style:bold;&amp;gt;&lt;br /&gt;
Figure 1:  Structure of MIDAS event showing Event and Bank headers with data banks.&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:MEHBH.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Event Header ===&lt;br /&gt;
Each event carries a 16-byte header, generated by the  [[Frontend_Operation#Frontend|Frontend]] with the&lt;br /&gt;
&#039;&#039;bm_compose_event()&#039;&#039; routine and is used by consumers to distinguish between&lt;br /&gt;
different events.&lt;br /&gt;
&lt;br /&gt;
The event header is defined in the EVENT_HEADER structure in the file &#039;&#039;midas.h&#039;&#039; in the midas package. &lt;br /&gt;
It has following structure:&lt;br /&gt;
&lt;br /&gt;
; EventID : the [[Equipment_List_Parameters#EventID|EventID]] identifies the event by number. Usually 1 is used for triggered events, 2 for scaler events, 3 for HV events etc.&lt;br /&gt;
; TriggerMask : the  [[Equipment List Parameters# TriggerMask|trigger mask]] can be used to describe the sub-type of an event. A trigger event can have different trigger sources like &amp;quot;physics event&amp;quot;, &amp;quot;calibration event&amp;quot;, &amp;quot;clock event&amp;quot;. These trigger sources are usually read in by the front-end in a pattern unit. Consumers can request events with a specific triggering mask.&lt;br /&gt;
; Serial number : The serial number starts at 0 and is incremented by the front-end for each event.&lt;br /&gt;
;Time Stamp : the time stamp is written by the front-end before an event is read out. It uses the time() function which returns the time in seconds since 1.1.1970 00:00:00 UTC.&lt;br /&gt;
;Event Data Size : The event data size contains the size of the event in bytes excluding the header. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;byte-ordering&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Event headers are always kept in the &amp;lt;b&amp;gt;byte ordering&amp;lt;/b&amp;gt; of the local machine. If events are sent over the network between computers with different byte&lt;br /&gt;
ordering, the event header is swapped automatically, but not the event contents.&lt;br /&gt;
&lt;br /&gt;
If the byte ordering of the contents of a complete event has to be swapped,&lt;br /&gt;
the routine &#039;&#039;bk_swap()&#039;&#039; can be used.&lt;br /&gt;
&lt;br /&gt;
=== Data Area ===&lt;br /&gt;
The &amp;lt;b&amp;gt;data area&amp;lt;/b&amp;gt; of the event can contain information in any user format (integer, real etc.), although only certain formats are supported when events are copied to the ODB or written by the logger in ASCII format. &lt;br /&gt;
&lt;br /&gt;
The Data Area of a MIDAS event consists of a global Bank Header followed by one or more MIDAS Data Banks.&lt;br /&gt;
&lt;br /&gt;
A Data Bank is a substructure of an event and can contain only one type of data, either a single value or an array of values.&lt;br /&gt;
Each Data Bank has an individual bank header containing a name of exactly four characters, which is treated as a bank ID.&lt;br /&gt;
&lt;br /&gt;
==== Bank Header (global) ====&lt;br /&gt;
There is one global Bank Header per event, defined in the BANK_HEADER structure in the file &#039;&#039;midas.h&#039;&#039;. &lt;br /&gt;
It has the following structure: &lt;br /&gt;
&lt;br /&gt;
; All Bank Size : Size in bytes of the following data banks including their bank names&lt;br /&gt;
; Flags : The four LSB 0:3 indicate the version of the bank structures. This is currently &amp;quot;1&amp;quot; and used for endian detection (byte ordering). The 5th bit indicates that the banks are 32-bit banks (Bank Size being 4 bytes long) and the 6th bit indicates that the banks are 64-bit aligned using the BANK32A bank header.&lt;br /&gt;
&lt;br /&gt;
The global Bank Header is initialized by the [[MIDAS_Event_Construction|bk_init()]], [[MIDAS_Event_Construction|bk_init32()]] or [[MIDAS_Event_Construction|bk_init32a()]] calls.&lt;br /&gt;
&lt;br /&gt;
==== MIDAS Data Bank ====&lt;br /&gt;
Each data bank contains a header defined by the BANK (or BANK32) structures in the file &#039;&#039;midas.h&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
It has the following structure: &lt;br /&gt;
; Bank name :  four characters for the name of each bank. Each bank in an event must have a unique name.&lt;br /&gt;
; Bank type :  one of the  [[Midas Data Types]] TID_xxx values  to encode the data type &lt;br /&gt;
; Bank length :  size in bytes of the following data.&lt;br /&gt;
&lt;br /&gt;
Following the header is the data in the format designated by the Bank type parameter (e.g. integer or float).&lt;br /&gt;
A separate MIDAS bank must be created for each data type needed.  &lt;br /&gt;
&lt;br /&gt;
==== Bank Alignment ====&lt;br /&gt;
&lt;br /&gt;
The data area of a bank must have a size of multiples of 8 Bytes. This is required to align the next bank header on a 8-Byte boundary in memory which allows faster access by the CPU. If a bank size is a few bytes short of a multiple of 8 bytes, the size is rounded up to the next multiple of 8 bytes and the remaining bytes in the bank may contain random data. Since midas event headers, bank headers (except the BANK32 bank header) and banks are all multiples of 8 bytes long, all data structures inside a midas event is 8-byte aligned for optimal CPU access. The BANK32 header is 12 Bytes long, causing the following Data area not being 8-byte aligned. If 8-byte alignment is important, the BANK32A header must be used instead. This can be done by initializing the bank structure with the [[MIDAS_Event_Construction|bk_init32a()]] call.&lt;br /&gt;
&lt;br /&gt;
=== Example of MIDAS event ===&lt;br /&gt;
&lt;br /&gt;
Figure 2 shows a MIDAS event containing banks coloured to match the structure in Figure 1. This has been&lt;br /&gt;
obtained from a MIDAS data file using the [[mdump]] application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span  style=&amp;quot;color: black; background-color: mintcream;   font-size: 110%; font-style:bold;&amp;gt; Figure  2: Example of MIDAS banks dumped by   &amp;lt;span style=&amp;quot;color:darkcyan;&amp;quot;&amp;gt;&#039;&#039;mdump&#039;&#039;&amp;lt;/span&amp;gt;.&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 ------------------------ Event# 2 --------------------------------&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: rgb(204, 204, 255);&amp;quot;&amp;gt;Evid:000d- Mask:0000- Serial:0- Time:0x4c7a6869- Dsize:48/0x30&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: lightgreen;&amp;quot;&amp;gt;\#banks:1 - Bank list:-SDAS-&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: orange;&amp;quot;&amp;gt;Bank:SDAS Length: 32(I*1)/8(I*4)/8(Type)Type:Real*4 (FMT machine dependent)&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: pink;&amp;quot;&amp;gt;  1-&amp;gt; 4.000e+00 1.000e+01 1.000e+00 3.400e+00 3.400e+00 3.400e+00 3.400e+00 3.400e+00&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 ------------------------ Event# 3 --------------------------------&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: rgb(204, 204, 255);&amp;quot;&amp;gt;Evid:0001- Mask:0000- Serial:0- Time:0x4c7a686b- Dsize:344/0x158&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: lightgreen;&amp;quot;&amp;gt;\#banks:2 - Bank list:-MPETMCPP-&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: orange;&amp;quot;&amp;gt;Bank:MPET Length: 304(I*1)/76(I*4)/76(Type) Type:Unsigned Integer*4&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: pink;&amp;quot;&amp;gt;    1-&amp;gt; 0x80010000 0x00000002 0x10010000 0x00004e21 0x80020000 0x00000002 0x20020000 0x000015f4&amp;lt;br&amp;gt;&lt;br /&gt;
   9-&amp;gt; 0x20020000 0x00001660 0x20020000 0x0000185f 0x20020000 0x0000191e 0x20020000 0x000019d6&amp;lt;br&amp;gt;&lt;br /&gt;
  17-&amp;gt; 0x40020000 0x00001a37 0x20020000 0x00001a77 0x20020000 0x00001ba2 0x10020000 0x00004e22&amp;lt;br&amp;gt;&lt;br /&gt;
  25-&amp;gt; 0x80030000 0x00000002 0x20030000 0x00001637 0x20030000 0x000018d1 0x20030000 0x000019bc&amp;lt;br&amp;gt;&lt;br /&gt;
  33-&amp;gt; 0x20030000 0x00001b35 0x20030000 0x00001bb2 0x10030000 0x00004e21 0x80040000 0x00000002&amp;lt;br&amp;gt;&lt;br /&gt;
  41-&amp;gt; 0x10040000 0x00004e22 0x80050000 0x00000002 0x20050000 0x000013c5 0x20050000 0x000017f2&amp;lt;br&amp;gt;&lt;br /&gt;
  49-&amp;gt; 0x20050000 0x0000185f 0x20050000 0x00001976 0x20050000 0x00001aa8 0x10050000 0x00004e21&amp;lt;br&amp;gt;&lt;br /&gt;
  57-&amp;gt; 0x80060000 0x00000002 0x20060000 0x000015c3 0x20060000 0x000018d8 0x20060000 0x0000198d&amp;lt;br&amp;gt;&lt;br /&gt;
  65-&amp;gt; 0x20060000 0x00001ac4 0x10060000 0x00004e22 0x80070000 0x00000002 0x20070000 0x00001747&amp;lt;br&amp;gt;&lt;br /&gt;
  73-&amp;gt; 0x20070000 0x000019ae 0x10070000 0x00004e21&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: orange;&amp;quot;&amp;gt;Bank:MCPP Length: 16(I*1)/4(I*4)/4(Type) Type:Unsigned Integer*4&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color: pink;&amp;quot;&amp;gt;   1-&amp;gt; 0x00005e4c 0x0000352d 0x00006453 0x00006d5b&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
 &amp;lt;br&amp;gt;&lt;br /&gt;
 --------------------------------------End of MIDAS Format ---------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FIXED Format Event ==&lt;br /&gt;
The  &amp;quot;FIXED&amp;quot; format event is the simplest event format. The event length is fixed and is mapped to a C structure that is filled by the readout routine. Since the standard MIDAS analyzer cannot work with this format, it is only recommended for an experiment which uses its own analyzer and wants to avoid the overhead of a bank structure, or for monitoring purposes in the ODB.&lt;br /&gt;
&lt;br /&gt;
The structure has to be defined twice: once for the compiler in the form of a C structure, and once for the ODB in form of an ASCII representation. There are several ways of doing this. The ASCII string may be supplied to the system as the [[Equipment List Parameters#Bank Definition/Init String|init string]] in the equipment list as follows:&lt;br /&gt;
 &lt;br /&gt;
[[File:FixedFormat.png|Example of definition of FIXED format using Equipment field &amp;quot;init string&amp;quot; ]]&lt;br /&gt;
&lt;br /&gt;
Alternatively, the structure may be defined first in the ODB, under &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/Equipment/&amp;lt;eqp_name&amp;gt;/Variables&amp;lt;/span&amp;gt;,&lt;br /&gt;
and an &#039;&#039;experim.h&#039;&#039; file generated (see [[odbedit|experim.h]]. The structure is then supplied to the readout routine by the frontend program including &amp;quot;experim.h&amp;quot;, as follows:&lt;br /&gt;
&lt;br /&gt;
[[File:FixedFormatExperim.png|Example of structure from experim.h for a fixed event]]&lt;br /&gt;
&lt;br /&gt;
To select &amp;quot;FIXED&amp;quot; format, the [[Equipment List Parameters|Equipment Definition]] must have the [[Equipment List Parameters#Format|format parameter]] set to &amp;quot;FIXED&amp;quot;.&lt;br /&gt;
The Equipment definition for this fixed event might be:&lt;br /&gt;
&lt;br /&gt;
 { &amp;quot;Info ODB&amp;quot;,     /* equipment name */&lt;br /&gt;
    10, 0,         /* event ID, trigger mask */&lt;br /&gt;
    &amp;quot;&amp;quot;,            /* no banks sent */&lt;br /&gt;
    EQ_PERIODIC,   /* equipment type */&lt;br /&gt;
    0,             /* interrupt source */&lt;br /&gt;
    &amp;quot;FIXED&amp;quot;,       /* format */&lt;br /&gt;
    TRUE,          /* enabled */&lt;br /&gt;
    RO_RUNNING | RO_ODB | &lt;br /&gt;
          RO_EOR,  /* read when running; &lt;br /&gt;
                      send to odb */&lt;br /&gt;
    500,           /* polling period */&lt;br /&gt;
    0,             /* event limit */&lt;br /&gt;
    0,             /* number of sub-events */&lt;br /&gt;
    0,             /* log history */&lt;br /&gt;
    &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,&lt;br /&gt;
    info_odb,      /* readout routine */&lt;br /&gt;
    NULL,NULL,NULL,&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
It is a good idea to check the record size and/or create the record in the ODB when using C structures from experim.h, to make sure that the structures in the ODB and in experim.h&lt;br /&gt;
are identical.  The frontend code might look like this:&lt;br /&gt;
&lt;br /&gt;
 /* frontend.c */&lt;br /&gt;
 ....&lt;br /&gt;
 #include &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;experim.h&amp;lt;/span&amp;gt;&lt;br /&gt;
 //&lt;br /&gt;
 // &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;INFO_ODB_EVENT&amp;lt;/span&amp;gt; and  &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;INFO_ODB_EVENT_STR&amp;lt;/span&amp;gt; are defined in experim.h&lt;br /&gt;
 INFO_ODB_EVENT cyinfo;&lt;br /&gt;
 INFO_ODB_EVENT_STR(info_odb_event_str);&lt;br /&gt;
 HNDLE hInfo;&lt;br /&gt;
 INT status, size;&lt;br /&gt;
 char   str_set[256];&lt;br /&gt;
 ....&lt;br /&gt;
 sprintf(str_set,&amp;quot;/Equipment/INFO ODB/Variables&amp;quot;);&lt;br /&gt;
 //&lt;br /&gt;
 /* create record /Equipment/INFO ODB/Variables to make sure it exists  */&lt;br /&gt;
 /* find the key for info odb */&lt;br /&gt;
  status = db_find_key(hDB, 0, str_set, &amp;amp;hInfo);&lt;br /&gt;
  if (status != DB_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      printf( &amp;quot;Key %s not found; creating record for info odb\n&amp;quot;,str_set);&lt;br /&gt;
      status = db_create_record(hDB, 0, str_set, strcomb(info_odb_event_str));&lt;br /&gt;
    }&lt;br /&gt;
  /* check the record size */&lt;br /&gt;
  status = db_get_record_size(hDB, hInfo, 0, &amp;amp;size);&lt;br /&gt;
  if (sizeof(INFO_ODB_EVENT) != size)&lt;br /&gt;
     {&lt;br /&gt;
        cm_msg(MERROR, &amp;quot;bnmr_init&amp;quot;, &amp;quot;error; record sizes do not match&amp;quot;);&lt;br /&gt;
        return DB_TYPE_MISMATCH;&lt;br /&gt;
     }&lt;br /&gt;
  ....... &lt;br /&gt;
&lt;br /&gt;
A &#039;&#039;&#039;readout routine&#039;&#039;&#039; for this fixed event is as follows:&lt;br /&gt;
&lt;br /&gt;
 INT info_odb(char * pevent, INT off)&lt;br /&gt;
 /* - periodic equipment updating the ODB ONLY&lt;br /&gt;
   - no event generation for the data stream.&lt;br /&gt;
 */&lt;br /&gt;
 {&lt;br /&gt;
  //&lt;br /&gt;
  /* fill various values */&lt;br /&gt;
  cyinfo.helicity = gbl_ppg_hel;&lt;br /&gt;
  cyinfo.current_cycle = gbl_CYCLE_N;&lt;br /&gt;
  cyinfo.current_scan = gbl_SCAN_N;&lt;br /&gt;
  cyinfo.epicsdev_set_v_ = epics_params.Epics_val;&lt;br /&gt;
  cyinfo.epicsdev_read_v_ = epics_params.Epics_read;&lt;br /&gt;
  cyinfo.campdev_set = 0;   &lt;br /&gt;
  cyinfo.campdev_read = 0; &lt;br /&gt;
  //  &lt;br /&gt;
  memcpy(pevent, (char *)&amp;amp;(cyinfo.helicity), sizeof(cyinfo));&lt;br /&gt;
  pevent += sizeof(cyinfo);&lt;br /&gt;
  logMsg (&amp;quot;info_odb %d size:%d\n&amp;quot;,gbl_CYCLE_N,sizeof(cyinfo),0,0,0,0);&lt;br /&gt;
  return sizeof(cyinfo);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
More examples of FIXED events can be found in the slow controls device drivers, for example &lt;br /&gt;
&#039;&#039;&#039;../examples/slowcont/frontend.c&#039;&#039;&#039; and &#039;&#039;&#039;../drivers/class/hv.c&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Special Events ==&lt;br /&gt;
&lt;br /&gt;
The three types of special events are begin-of-run, end-of-run and message events.&lt;br /&gt;
&lt;br /&gt;
In all cases, the events contain an EVENT_HEADER, and then proceed directly to the data payload. There are no banks or BANK_HEADER.&lt;br /&gt;
&lt;br /&gt;
=== Begin-of-run and End-of-run Events ===&lt;br /&gt;
&lt;br /&gt;
If [[Keys_in_the_ODB_/Logger/Channels_subtree#ODB_Dump|file-level ODB dumps]] are enabled, two extra events will be added to each midas file (as the first and last events, respectively):&lt;br /&gt;
&lt;br /&gt;
* The begin-of-run (BOR) event has an EventID of 0x8000.&lt;br /&gt;
* The end-of-run (EOR) event has an EventID of 0x8001.&lt;br /&gt;
&lt;br /&gt;
In both cases, the data area of the event contains a JSON or XML representation of the ODB at the time the event was written. The format (json or xml) is specified using the [[Keys_in_the_ODB_/Logger/Channels_subtree#ODB_dump_format|ODB dump format]] setting.&lt;br /&gt;
&lt;br /&gt;
Note that although the events are called &amp;quot;begin-of-run&amp;quot; and &amp;quot;end-of-run&amp;quot;, they are actually written for every [[Subruns|subrun]]! (Subruns let you split the data for a run across multiple files, e.g. each of 1GB in size). This is so that analysis code can find any relevant ODB settings in the same midas file as the data it is analysing.&lt;br /&gt;
&lt;br /&gt;
The BOR event has a trigger mask equal to 0x494d (aka &#039;MI&#039;), and a serial number equal to the current run number.&lt;br /&gt;
&lt;br /&gt;
=== Message Events ===&lt;br /&gt;
&lt;br /&gt;
If [[Keys_in_the_ODB_/Logger/Channels_subtree#Log_messages|log messages]] is enabled, any messages written using cm_msg() will be stored in the file.&lt;br /&gt;
&lt;br /&gt;
A message event has an EventID of 0x8002, and the message is contained in the data area as an ASCII string.&lt;br /&gt;
&lt;br /&gt;
== Tape Format ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color:mistyrose;&amp;quot;&amp;gt;&lt;br /&gt;
;Warning : Writing to tape as described below is not maintained. Files are usually written to disk, and may later be archived onto tape or other storage devices using normal file transfer methods.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Events are written to disk files without any reformatting.&lt;br /&gt;
For tapes, however, a fixed block size is used. The block size TAPE_BUFFER_SIZE is defined in &#039;&#039;midas.h&#039;&#039; and usually 32kB. &lt;br /&gt;
&lt;br /&gt;
A tape can therefore be identified as a MIDAS formatted tape.&lt;br /&gt;
&lt;br /&gt;
The routine &#039;&#039;tape_copy()&#039;&#039; in the utility mtape.c is an example of how to read a tape in MIDAS format.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Event]] [[Category:Appendices]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3422</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3422"/>
		<updated>2024-03-26T01:22:29Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* modb* Javascript scheme */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. 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).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
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 &amp;quot;virtual&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
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.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&lt;br /&gt;
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.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: Without the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* 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.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; 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 &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;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.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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 &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || 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.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; 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.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; 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 &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
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 &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
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&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| 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/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
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 &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). 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 &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=MSCB_Page&amp;diff=3421</id>
		<title>MSCB Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=MSCB_Page&amp;diff=3421"/>
		<updated>2024-02-04T05:49:59Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages2|[[/MSCB ODB tree]]|[https://elog.psi.ch/mscb/  MSCB (MIDAS Slow Control Bus)]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
The purpose of a [[mhttpd]] MSCB Page is to visually present the devices in the&lt;br /&gt;
[https://elog.psi.ch/mscb/  MSCB (MIDAS Slow Control Bus)] system and their variables.&lt;br /&gt;
&lt;br /&gt;
= Access to the MSCB Page =&lt;br /&gt;
The MSCB Page is displayed by clicking on the  {{Button|name=MSCB}}  button on the mhttpd [[Status Page]] or other mhttpd web page. &lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: If the MSCB button is not present on the Status Page, either&lt;br /&gt;
:* MIDAS has been built without MSCB support ([[#MIDAS with MSCB support|see below]]) or&lt;br /&gt;
:* it may have been [[Status Page#page-switch-buttons|suppressed]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== MIDAS with MSCB support ==&lt;br /&gt;
&lt;br /&gt;
In order to create the MSCB page, the flag&lt;br /&gt;
; -DHAVE_MSCB&lt;br /&gt;
must be present in the MIDAS Makefile  so that mhttpd is linked against ../mscb/mscb.c and has direct access to all mscb ethernet submasters (USB access is currently disabled on purpose there). See [[Compilation &amp;amp; Build]] for more information.&lt;br /&gt;
&lt;br /&gt;
The presence of the flag &#039;&#039;&#039;-DHAVE_MSCB&#039;&#039;&#039; will cause the MSCB button to appear on the [[Status Page]] by default, unless the menu button has been [[Status Page#page-switch-buttons|suppressed]].&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: The current MIDAS distribution contains a file &amp;lt;span style=&amp;quot;color:#686868; font-weight:bold; font-style:italic &amp;quot;&amp;gt;../midas/examples/slowcont/mscb_fe.c&amp;lt;/span&amp;gt; which contains example code of how to read some MSCB devices.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Features of the MSCB page =&lt;br /&gt;
Click thumbnail to enlarge&lt;br /&gt;
[[File:Deap_mscb.png|thumb|left|Figure 1: Example of a MSCB page]]&lt;br /&gt;
The MSCB page  (see Figure 1) displays information from the [[/MSCB ODB tree]]. It obtains a list of all available submasters from the [[/MSCB ODB tree#Submaster subtree|Submaster subtree]]. &lt;br /&gt;
&lt;br /&gt;
The &amp;lt;span style=&amp;quot;color: #444444; background-color: #CCCCCC; font-style:italic; font-size: 90; padding:0.25em;&lt;br /&gt;
padding-left: 0.5em;padding-right: 0.5em;border:1px solid #808080;border-radius: 5px;margin-bottom:1px;&amp;quot;&amp;gt;Rescan&amp;lt;/span&amp;gt; button will rescan the MSCB devices, the  &amp;lt;span style=&amp;quot;color: #444444; background-color: #CCCCCC; font-style:italic; font-size: 90; padding:0.25em;&lt;br /&gt;
padding-left: 0.5em;padding-right: 0.5em;border:1px solid #808080;border-radius: 5px;margin-bottom:1px;&amp;quot;&amp;gt;Reload&amp;lt;/span&amp;gt; button&lt;br /&gt;
refreshes the page.&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category: MSCB]] [[Category:mhttpd Pages]] [[Category:Slow Control]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=MSCB_Page&amp;diff=3420</id>
		<title>MSCB Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=MSCB_Page&amp;diff=3420"/>
		<updated>2024-02-04T05:49:33Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages2|[[/MSCB ODB tree]]|[https://elog.psi.ch/mscb/  MSCB (MIDAS Slow Control Bus)]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
The purpose of a [[mhttpd]] MSCB Page is to visually present the devices in the&lt;br /&gt;
[https://midas.psi.ch/mscb/  MSCB (MIDAS Slow Control Bus)] system and their variables.&lt;br /&gt;
&lt;br /&gt;
= Access to the MSCB Page =&lt;br /&gt;
The MSCB Page is displayed by clicking on the  {{Button|name=MSCB}}  button on the mhttpd [[Status Page]] or other mhttpd web page. &lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: If the MSCB button is not present on the Status Page, either&lt;br /&gt;
:* MIDAS has been built without MSCB support ([[#MIDAS with MSCB support|see below]]) or&lt;br /&gt;
:* it may have been [[Status Page#page-switch-buttons|suppressed]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== MIDAS with MSCB support ==&lt;br /&gt;
&lt;br /&gt;
In order to create the MSCB page, the flag&lt;br /&gt;
; -DHAVE_MSCB&lt;br /&gt;
must be present in the MIDAS Makefile  so that mhttpd is linked against ../mscb/mscb.c and has direct access to all mscb ethernet submasters (USB access is currently disabled on purpose there). See [[Compilation &amp;amp; Build]] for more information.&lt;br /&gt;
&lt;br /&gt;
The presence of the flag &#039;&#039;&#039;-DHAVE_MSCB&#039;&#039;&#039; will cause the MSCB button to appear on the [[Status Page]] by default, unless the menu button has been [[Status Page#page-switch-buttons|suppressed]].&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: The current MIDAS distribution contains a file &amp;lt;span style=&amp;quot;color:#686868; font-weight:bold; font-style:italic &amp;quot;&amp;gt;../midas/examples/slowcont/mscb_fe.c&amp;lt;/span&amp;gt; which contains example code of how to read some MSCB devices.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Features of the MSCB page =&lt;br /&gt;
Click thumbnail to enlarge&lt;br /&gt;
[[File:Deap_mscb.png|thumb|left|Figure 1: Example of a MSCB page]]&lt;br /&gt;
The MSCB page  (see Figure 1) displays information from the [[/MSCB ODB tree]]. It obtains a list of all available submasters from the [[/MSCB ODB tree#Submaster subtree|Submaster subtree]]. &lt;br /&gt;
&lt;br /&gt;
The &amp;lt;span style=&amp;quot;color: #444444; background-color: #CCCCCC; font-style:italic; font-size: 90; padding:0.25em;&lt;br /&gt;
padding-left: 0.5em;padding-right: 0.5em;border:1px solid #808080;border-radius: 5px;margin-bottom:1px;&amp;quot;&amp;gt;Rescan&amp;lt;/span&amp;gt; button will rescan the MSCB devices, the  &amp;lt;span style=&amp;quot;color: #444444; background-color: #CCCCCC; font-style:italic; font-size: 90; padding:0.25em;&lt;br /&gt;
padding-left: 0.5em;padding-right: 0.5em;border:1px solid #808080;border-radius: 5px;margin-bottom:1px;&amp;quot;&amp;gt;Reload&amp;lt;/span&amp;gt; button&lt;br /&gt;
refreshes the page.&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category: MSCB]] [[Category:mhttpd Pages]] [[Category:Slow Control]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Electronic_Logbook_(ELOG)&amp;diff=3418</id>
		<title>Electronic Logbook (ELOG)</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Electronic_Logbook_(ELOG)&amp;diff=3418"/>
		<updated>2024-01-13T01:24:20Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Built-in Elog */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; {{Pagelinks}}&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[/Elog ODB tree]]&lt;br /&gt;
* [[mhttpd]]&lt;br /&gt;
* mhttpd [[Elog Page]]&lt;br /&gt;
* [https://midas.psi.ch/elog/ &#039;&#039;&#039;Elog&#039;&#039;&#039; ]&lt;br /&gt;
* [[Melog]] Utility&lt;br /&gt;
* [[Elog|Elog Utility]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
The &#039;&#039;&#039;Electronic LogBook&#039;&#039;&#039; (ELOG) is a utility built-in the [[mhttpd|web server]]. It is available and operational once the web-server is running. A dedicated button &amp;quot;ELOG&amp;quot; will be present on the main &lt;br /&gt;
Midas status page for creating new entries. This mode is referred as the [[#Built-in Elog|Built-in Elog]].&lt;br /&gt;
&lt;br /&gt;
This tool can replace the experimental logbook for daily entries. The main advantage of the Elog over a paper logbook is the possiblity to access it remotely, and provide a general knowledge of the experiment.&lt;br /&gt;
&lt;br /&gt;
More recently, the independent [https://elog.psi.ch/elog/ &#039;&#039;&#039;Elog&#039;&#039;&#039; ] package can be linked to a Midas experiment. Its advantage over the built-in version is its flexibility. This package is used worldwide and improvements are constantly being made. A full-features documentation and standalone installation can be found at the Elog web site.&lt;br /&gt;
&lt;br /&gt;
== Built-in Elog ==&lt;br /&gt;
The ELOG page (accessed by clicking the Elog button if present on the main status page ) provides access to an electronic logbook.  the Elog is not limited strictly to experiments. Worldwide Elog implementations can be found on the internet. This internal implementation doesn&#039;t requires any setup to be operational. The Elog is customized through the ODB [[/Elog_ODB_tree|/Elog]] tree.&lt;br /&gt;
&lt;br /&gt;
Data files for the built-in elog are stored in the directory defined by the &amp;lt;code&amp;gt;/Logger/Elog dir&amp;lt;/code&amp;gt; ODB key. If that ODB key is empty, then the directory specified in &amp;lt;code&amp;gt;/Logger/Data dir&amp;lt;/code&amp;gt; is used instead.&lt;br /&gt;
&lt;br /&gt;
== External Elog ==&lt;br /&gt;
The external implementation requires a proper Elog installation which is fully described on the Elog web site. The External Elog implementation also requires a dedicated entry in the [[/Elog ODB tree]] as shown in the code below. It also requires the package Elog to be already installed, and properly configured. Once the ODB entry is present, the internal ELOG is disabled. &lt;br /&gt;
&lt;br /&gt;
The External Elog is customized through the configuration file elogd.cfg (see [[#Installation|Installation]] below).&lt;br /&gt;
&lt;br /&gt;
= Installation of External Elog =&lt;br /&gt;
Installation requires requires several steps described below.&lt;br /&gt;
&lt;br /&gt;
== Build Elog package ==&lt;br /&gt;
&lt;br /&gt;
On Linux and Mac, we recommend building elog from git sources:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir -p $HOME/packages&lt;br /&gt;
cd $HOME/packages&lt;br /&gt;
git clone https://bitbucket.org/ritt/elog --recursive&lt;br /&gt;
cd elog&lt;br /&gt;
git pull ### update to latest version&lt;br /&gt;
make -j&lt;br /&gt;
ls -l elogd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
Elog will run from it&#039;s build directory, an &amp;quot;install&amp;quot; step is not needed.&lt;br /&gt;
&lt;br /&gt;
We will use Elog in conjunction with MIDAS mhttpd and Apache httpd. mhttpd will run on TCP port 8080, elogd on port 8082, apache httpd will provide HTTPS encryption and password protection, see https://daq00.triumf.ca/DaqWiki/index.php/Ubuntu#Install_apache_httpd_proxy_for_midas_and_elog&lt;br /&gt;
&lt;br /&gt;
Make sure &amp;quot;ProxyPass /elog/&amp;quot; is uncommented in apache config file, restart apache after making this change.&lt;br /&gt;
&lt;br /&gt;
Try https://yourhostname/elog/, after entering correct password, error should be: &amp;quot;Service Unavailable&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Configuration file elogd.cfg ==&lt;br /&gt;
* copy the default midas/src/elogd.cfg from the MIDAS distrbution to your operating directory.&lt;br /&gt;
* modify the elogd.cfg to reflect your configuration&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # This is a simple elogd configuration file to work with MIDAS&lt;br /&gt;
  # &lt;br /&gt;
  [global]&lt;br /&gt;
  ; port under which elogd should run&lt;br /&gt;
  port = 8082                           &lt;br /&gt;
  ; password file, created under &#039;logbook dir&#039;&lt;br /&gt;
  password file = elog.pwd                &lt;br /&gt;
  ; directory under which elog was installed (themes etc.)&lt;br /&gt;
  resource dir = /home/exptuser/packages/elog&lt;br /&gt;
  ; directory where the password file will end up&lt;br /&gt;
  logbook dir = /myexpt/logbook     &lt;br /&gt;
  ; anyone can create it&#039;s own account&lt;br /&gt;
  self register = 1                       &lt;br /&gt;
  ; this is the URL of the elogd https proxy&lt;br /&gt;
  url = https://myexpt.triumf.ca/elog/&lt;br /&gt;
  ; the &amp;quot;main&amp;quot; tab will bring you back to mhttpd&lt;br /&gt;
  main tab = Xenon                        &lt;br /&gt;
  ; this is the URL of mhttpd https proxy&lt;br /&gt;
  main tab url = https://myexpt.triumf.ca&lt;br /&gt;
  ; email server for sending email notifications, contact your campus network administrator for correct hostname&lt;br /&gt;
  smtp host = smtp.triumf.ca             &lt;br /&gt;
  ; Define one logbook for online use. Severl logbooks can be defined here&lt;br /&gt;
  [MyOnline]&lt;br /&gt;
  ; directory where the logfiles will be written to&lt;br /&gt;
  Data dir = /myexpt/logbook            &lt;br /&gt;
  Comment = My MIDAS Experiment Electronic Logbook&lt;br /&gt;
  ; mimic old mhttpd behaviour&lt;br /&gt;
  Attributes = Run number, Author, Type, System, Subject     &lt;br /&gt;
  Options Type = Routine, Shift Summary, Minor Error, Severe Error, Fix, Question, Info, Modification, Alarm, Test, Other, &lt;br /&gt;
  Options System = General, DAQ, Detector, Electronics, Target, Beamline&lt;br /&gt;
  Extendable Options = Type, System&lt;br /&gt;
  ; This substitution will enter the current run number&lt;br /&gt;
  Preset Run number = $shell(odbedit -e myexpt -h NodeA -d Runinfo -c &#039;ls -v \&amp;quot;run number\&amp;quot;&#039;)    &lt;br /&gt;
  Preset Author = $long_name&lt;br /&gt;
  Required Attributes = Type, Subject&lt;br /&gt;
  ; Run number and Author cannot be changed&lt;br /&gt;
  Locked Attributes = Run number, Author  &lt;br /&gt;
  Page Title = ELOG - $subject&lt;br /&gt;
  Reverse sort = 1&lt;br /&gt;
  Quick filter = Date, Type, Author&lt;br /&gt;
  ; Don&#039;t send any emails&lt;br /&gt;
  Suppress email to users = 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konstantin&#039;s most recently used config file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[global]&lt;br /&gt;
port = 9085&lt;br /&gt;
URL = https://daq00.triumf.ca/elog-dl&lt;br /&gt;
SMTP host = smtp.triumf.ca&lt;br /&gt;
Logfile = elogd.log&lt;br /&gt;
Time format = %d %b %Y %H:%M&lt;br /&gt;
Default encoding = 1&lt;br /&gt;
Allowed encoding = 1&lt;br /&gt;
Password file = elogd-dl.passwd&lt;br /&gt;
Admin User = Olchansk&lt;br /&gt;
Menu commands = List, New, Edit, Delete, Reply, Duplicate, Find, Config, Help&lt;br /&gt;
Self register = 3&lt;br /&gt;
Message Width = 80&lt;br /&gt;
Preset Author = $long_name&lt;br /&gt;
Preset on reply Author = $long_name&lt;br /&gt;
Locked Attributes = Author&lt;br /&gt;
Max content length = 100000000&lt;br /&gt;
&lt;br /&gt;
[DarkLight]&lt;br /&gt;
Theme = default&lt;br /&gt;
Comment = DarkLight elog&lt;br /&gt;
Attributes = Author, Type, Category, Subject&lt;br /&gt;
Options Type = Routine, Problem Report, Problem Fixed&lt;br /&gt;
Options Category = General, Software, Hardware&lt;br /&gt;
Extendable Options = Category, Type&lt;br /&gt;
Required Attributes = Author, Type&lt;br /&gt;
Page Title = DL - $subject&lt;br /&gt;
Reverse sort = 1&lt;br /&gt;
Quick filter = Date, Type&lt;br /&gt;
Use Email Subject = [DL] $subject&lt;br /&gt;
Use email from = noreply@daq00.triumf.ca&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Start the Elog daemon ==&lt;br /&gt;
* start the elog daemon&lt;br /&gt;
* -x is for the shell substitution of the command Preset Run number = $shell(...)&lt;br /&gt;
* if elog is not running on same machine as midas (mhttpd, mlogger), change the odbedit command in the config file to have correct -e and -h arguments.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$HOME/packages/elog/elogd -c dldaq.cfg -x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Activate External Elog ==&lt;br /&gt;
&lt;br /&gt;
At this point the Elog from the MIDAS web page is accessing the [[[#Built-in Elog|Built-in]]] (internal) Elog. To activate the external Elog you need to edit two ODB variables (which mhttpd automatically created):&lt;br /&gt;
&lt;br /&gt;
* the ODB string &amp;quot;/Elog/URL&amp;quot; needs to be set to the URL of the external ELOG.&lt;br /&gt;
* the ODB bool &amp;quot;/Elog/External Elog&amp;quot; needs to be set to &#039;y&#039;&lt;br /&gt;
&lt;br /&gt;
Note that after changing the ODB variable &#039;External Elog&#039; from &#039;n&#039; to &#039;y&#039; you will need to reload the elog page in order to get the external elog.&lt;br /&gt;
&lt;br /&gt;
Confirm proper operation of the external Elog by creating an entry. You will be prompted for a username and password. Click on New registration. Full control of these features are described in the Elog documentation.&lt;br /&gt;
Stop and restart the Elogd in the background.&lt;br /&gt;
&lt;br /&gt;
   NodeB:~&amp;gt;/installation_elog_dir/elogd -c elogd.cfg -x -D&lt;br /&gt;
&lt;br /&gt;
= How to convert Built-in Elog files to External files =&lt;br /&gt;
In the event you had a previous entry under the internal elog, you can convert the internal to external using the elconv tool.&lt;br /&gt;
&lt;br /&gt;
   NodeB:~&amp;gt; cp internal/elog_logbook/*.log /myexpt/logbook/.&lt;br /&gt;
   NodeB:~&amp;gt; cd /myexpt/logbook&lt;br /&gt;
   NodeB:~&amp;gt; /installation_elog_dir/elconv&lt;br /&gt;
&lt;br /&gt;
= How to send entries to the Elog from a script =&lt;br /&gt;
Utilities exist to send elog entries automatically (e.g. from a script run at begin-of-run). Use [[Melog|melog]] to send an&lt;br /&gt;
entry to a Built-in elog, or [[Elog|elog]] to send an entry to an &#039;&#039;&#039;external&#039;&#039;&#039; elog.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
[[Category:ELOG]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3417</id>
		<title>Frontend user code</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3417"/>
		<updated>2024-01-08T22:45:10Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Using mfed to reduce the amount of boiler-plate code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation]]&lt;br /&gt;
* [[Frontend Application]] &lt;br /&gt;
* [[Equipment List Parameters]]&lt;br /&gt;
* [[Slow Control System]]&lt;br /&gt;
* [[Equipment Flags]]&lt;br /&gt;
* [[Event Notification (Hot-Link)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This section describes the features of the user-written part of a &amp;quot;C-style&amp;quot; [[Frontend Operation#Frontend|Frontend]], referred to here as &#039;&#039;&#039;frontend.c&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
You can also write frontends using [[Frontend_user_code_(object_oriented_-_TMFE)|object-oriented C++ (TMFE)]] or [[Python]].&lt;br /&gt;
&lt;br /&gt;
The frontend system has evolved over the decades, and contains some legacy features that are not often used (e.g. interrupt handlers). For backwards-compatibility, these features are still present, but this does require a bit more boiler-plate code to be written for each frontend. We will first document every feature supported by the frontend system, then explain a slightly more user-friendly wrapper (mfed.cxx) that helps reduce the amount of boiler-plate needed for a new frontend.&lt;br /&gt;
&lt;br /&gt;
= Frontend Templates =&lt;br /&gt;
To make a user-written or custom frontend, users will usually modify one of the &#039;&#039;&#039;templates&#039;&#039;&#039; provided in the MIDAS package&lt;br /&gt;
under [[Environment Variables#MIDASSYS|$MIDASSYS]]/examples/ for their particular hardware and other requirements. Make sure to pick the closest template, e.g. if writing a [[Slow Control System|Slow Control]] frontend, pick a slow-control template. For each example frontend, a Makefile is also provided.&lt;br /&gt;
&lt;br /&gt;
A partial list of the templates provided is shown below:&lt;br /&gt;
{| style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;3&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Hardware &lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Filename&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Directory (MIDAS package)&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Purpose&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Language&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevmemodules.c&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevme.cxx&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c++/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|CAMAC &lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| Access to CAMAC modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|mtfe.c&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| &#039;&#039;&#039;Multithreaded&#039;&#039;&#039; using ring buffer&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Wiener CC-USB &lt;br /&gt;
|feccusb.cxx&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| CAMAC/USB demo &lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|RS485 bus &lt;br /&gt;
|mscb_fe.c&lt;br /&gt;
| $MIDASSYS/examples/slowcont/&lt;br /&gt;
| &#039;&#039;&#039;Slow control&#039;&#039;&#039; with [https://midas.psi.ch/mscb/ MSCB]&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|EPICS&lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/epics/&lt;br /&gt;
| &#039;&#039;&#039;Slow controls&#039;&#039;&#039;&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|Camac&lt;br /&gt;
|ebfe.c&lt;br /&gt;
| $MIDASSYS/examples/eventbuilder/&lt;br /&gt;
| &#039;&#039;&#039;Event Builder&#039;&#039;&#039; (with [[mevb]])&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|frontend.cxx&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| &#039;&#039;&#039;mfed&#039;&#039;&#039; (wrapper to simplify writing a frontend)&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The features of a typical frontend program are best explained by reference to examples of the user code &lt;br /&gt;
provided in the Midas Package. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Frontend code=&lt;br /&gt;
Note that there are several kinds of frontend used for different purposes, e.g.&lt;br /&gt;
* frontend to read VME or CAMAC hardware, using interrupt or polling (e.g. to read experimental data)&lt;br /&gt;
* multithreaded frontend where polling can be in a separate thread&lt;br /&gt;
* slow control frontend (can be multithreaded) see also [[Slow Control System]]&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Templates for many types of user frontend code are provided in the MIDAS packages (see [[#Frontend Templates]]). &lt;br /&gt;
&lt;br /&gt;
The following sections refer to these templates.&lt;br /&gt;
Most of the examples are taken from the [https://bitbucket.org/tmidas/midas/src/develop/examples/basic/largefe.cxx largefe.cxx example]. Documentation on the MIDAS library subroutines to access the ODB (some of which are used in the examples below) can be found in the [[ODB_Access_and_Use|ODB access page]].&lt;br /&gt;
&lt;br /&gt;
The user frontend code is then compiled and linked with the system part and the MIDAS library &lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Task]]).  Makefiles are provided with the templates that can be modified as needed.&lt;br /&gt;
&lt;br /&gt;
== Access to command line parameters ==&lt;br /&gt;
The function &#039;&#039;&#039;mfe_get_args&#039;&#039;&#039; gives access to the [[Frontend Application|Frontend]] command line parameters. &lt;br /&gt;
&lt;br /&gt;
This example shows how to use it in the frontend user code:&lt;br /&gt;
&lt;br /&gt;
  int argc;&lt;br /&gt;
  char **argv;&lt;br /&gt;
  mfe_get_args(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
  for (int i = 0; i &amp;lt; argc; i++) {&lt;br /&gt;
     // Use argv[i] &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
See [[Frontend_Application#Arguments|Frontend Application Arguments]] for the arguments that are handled automatically by the frontend system.&lt;br /&gt;
&lt;br /&gt;
==Include files==&lt;br /&gt;
The following example (from template frontend file &#039;&#039;$MIDASSYS/examples/basic/largefe.cxx&#039;&#039;) shows the standard include files needed for a frontend. The user may add any other include files as needed (e.g. those needed for VME access). &lt;br /&gt;
&lt;br /&gt;
Some legacy frontends may include the &#039;&#039;&#039;[[ODB#experim.h include file|experim.h]]&#039;&#039;&#039; file. This was an old way of accessing the ODB, but was not very user-friendly when the ODB structure needed to change.&lt;br /&gt;
&lt;br /&gt;
The example below shows a typical list of include files for a frontend:&lt;br /&gt;
 &lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
 #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;div id=&amp;quot;Frontend Name&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
==Global Declarations==&lt;br /&gt;
The following example (from template frontend file fevmemodules.c - see [[#Frontend Templates]]) shows the global declaration. The declarations are system wide.  Some may be changed to suit the user, but none should not be removed.&lt;br /&gt;
&lt;br /&gt;
; frontend_name  : This value can be modified to reflect the purpose of the code &lt;br /&gt;
; frontend_call_loop : If set to TRUE, the function frontend_loop() gets called very frequently. If FALSE, frontend_loop() does not run. The user can add suitable code to this routine if desired (e.g. to check for a condition).&lt;br /&gt;
; display_period : The time interval (defined in milliseconds) between the refresh of a frontend status display. The value of zero disables the display. If the frontend is started in the background with the display enabled, the stdout should be redirected to the null device to prevent the process from hanging.&lt;br /&gt;
; max_event_size : specifies the maximum size (in bytes) of the expected event.&lt;br /&gt;
; event_buffer_size : specifies the maximum size (in bytes) of the buffer to be allocated by the system.&lt;br /&gt;
; equipment_common_overwrite : whether the definitions in the EQUIPMENT struct override those in the /Equipment/largefe/Common section of the ODB. If FALSE, the values in the struct will be used the first time the program runs, then the ODB values will be used afterwards (e.g. allowing you to change the period of a [[#Event_Types_and_Triggers|periodic equipment]] via the ODB).&lt;br /&gt;
&lt;br /&gt;
See below for an example of global declarations from a frontend.&lt;br /&gt;
&lt;br /&gt;
 /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
 /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
 const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 /* The frontend file name, don&#039;t change it */&lt;br /&gt;
 const char *frontend_file_name = __FILE__;&lt;br /&gt;
 &lt;br /&gt;
 /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 &lt;br /&gt;
 /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
 INT display_period = 0;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size produced by this frontend */&lt;br /&gt;
 INT max_event_size = 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
 INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
 &lt;br /&gt;
 /* buffer size to hold events */&lt;br /&gt;
 INT event_buffer_size = 10 * 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* whether the values in EQUIPMENT struct override ODB values */&lt;br /&gt;
 BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==System Function Declarations==&lt;br /&gt;
&lt;br /&gt;
These lines declare the pre-defined system functions which should be present. &lt;br /&gt;
&lt;br /&gt;
 INT frontend_init();&lt;br /&gt;
 INT frontend_exit();&lt;br /&gt;
 INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
 INT end_of_run(INT run_number, char *error);&lt;br /&gt;
 INT pause_run(INT run_number, char *error);&lt;br /&gt;
 INT resume_run(INT run_number, char *error);&lt;br /&gt;
 INT frontend_loop();&lt;br /&gt;
&lt;br /&gt;
==Readout Function Declarations==&lt;br /&gt;
Following the previous group is a second group of declarations, which define the readout functions. These depend on the defined equipments, and run when the respective equipment is triggered. In this example, one equipment will be defined, so there is one declaration. The user functions will be described in detail in later sections. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off);&lt;br /&gt;
&lt;br /&gt;
If using an interrupt, callback function prototypes are also included&lt;br /&gt;
&lt;br /&gt;
 extern void interrupt_routine(void);&lt;br /&gt;
 void register_cnaf_callback(int debug);&lt;br /&gt;
&lt;br /&gt;
==Equipment List==&lt;br /&gt;
&lt;br /&gt;
This list of structs defines the behaviour of your frontend (e.g. [[#Event_Types_and_Triggers|periodic or polled equipment]]). See [[Equipment List Parameters|Equipment List]] for full documentation of the entries.&lt;br /&gt;
&lt;br /&gt;
Note that these are the default values for each equipment (used the very first time a frontend is run). If [[#Global declarations|equipment_common_overwrite]] is FALSE, then some of the values will subsequently be read from the ODB instead. If [[#Global declarations|equipment_common_overwrite]] is TRUE, then the ODB will be updated with the coded values each time the program runs.&lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;large&amp;quot;,                   /* equipment name */&lt;br /&gt;
     {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
      &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
      EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
      0,                       /* event source */&lt;br /&gt;
      &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
      TRUE,                    /* enabled */&lt;br /&gt;
      RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
      2000,                    /* read every 2 sec */&lt;br /&gt;
      0,                       /* stop run after this event limit */&lt;br /&gt;
      0,                       /* number of sub events */&lt;br /&gt;
      0,                       /* log history */&lt;br /&gt;
      &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
      read_large_event,        /* readout routine */&lt;br /&gt;
      NULL, NULL,              /* keep null */&lt;br /&gt;
      NULL,                    /* init string */&lt;br /&gt;
   },&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;&amp;quot;}&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the case of a polled equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Trigger&amp;quot;,            // equipment name&lt;br /&gt;
        {&lt;br /&gt;
          ...&lt;br /&gt;
          &amp;lt;b&amp;gt;EQ_POLLED&amp;lt;/b&amp;gt;,          // equipment type&lt;br /&gt;
          ...&lt;br /&gt;
          500,                // poll for 500ms &lt;br /&gt;
          ...&lt;br /&gt;
          &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
          read_my_event,    // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the case of a periodic equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Scaler&amp;quot;,           // equipment name&lt;br /&gt;
         {    &lt;br /&gt;
            ...&lt;br /&gt;
            EQ_PERIODIC     // equipment type&lt;br /&gt;
            ...&lt;br /&gt;
            10000,          // period (read every 10s)&lt;br /&gt;
            ...&lt;br /&gt;
            &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
       read_my_event,   // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sequence of Operations in the frontend==&lt;br /&gt;
&lt;br /&gt;
The following table shows the sequence of operations of the  [[Frontend Operation#Frontend|Frontend]] System functions.  These functions must be implemented in the user&#039;s code (but may be as simple as just returning &amp;lt;code&amp;gt;SUCCESS&amp;lt;/code&amp;gt; if the function is not relevant for your use case). These functions are called by &#039;&#039;mfe.cxx&#039;&#039; at the appropriate time. The System Transition functions are associated with a particular [[Run States and Transitions|Run Transition]] as shown below:&lt;br /&gt;
&lt;br /&gt;
{|  style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Transition Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Associated [[Run States and Transitions|Transition]]&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Action&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_init()&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| Runs once after system initialization, before Equipment registration. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| begin_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_START&lt;br /&gt;
| Runs after system statistics reset at each begin-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| pause_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_PAUSE&lt;br /&gt;
| Runs at each pause-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| resume_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_RESUME&lt;br /&gt;
| Runs at each resume-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| end_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_STOP&lt;br /&gt;
| Runs at each end-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_exit()&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Runs once before any Slow Control Equipment exit &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Each defined Equipment has the option to force itself to run at individual transition times (see Equipment [[Equipment List Parameters#ReadOn|ReadOn Flag]]), so that its equipment function will be called on a certain transition (or combination of transitions). &lt;br /&gt;
&lt;br /&gt;
The system transition functions all run &#039;&#039;&#039;prior to&#039;&#039;&#039; the equipment functions. This gives the system the chance to take basic action on the transition request (e.g. enable/disable interrupt) before the equipment runs. All the transition routines run with a [[Run States and Transitions#Default Transition Sequency Numbers|Transition Sequence number]] of 500 (the default). This allows users to add additional functions in the frontend that will run before or after any of the transitions (such as a prestart() or a poststop() function). See [[Run States and Transitions#Run Transition Priority|Run Transition Priority]] for more information.&lt;br /&gt;
&lt;br /&gt;
==Function frontend_init ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
No parameters.&lt;br /&gt;
&lt;br /&gt;
This function runs &#039;&#039;&#039;once only&#039;&#039;&#039; at the application startup. Users may perform hardware checking, loading/setting of global variables, mapping of required ODB structures (see [[ODB#experim.h include file|experim.h include file]]), &lt;br /&gt;
[[Event Notification (Hot-Link)#How to set up a Hot-Link|setting up hot-links]] etc. in frontend_init(), e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT frontend_init()&lt;br /&gt;
 {&lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;Initializing...&amp;quot;, &amp;quot;yellow&amp;quot;);&lt;br /&gt;
   ....                  &lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;OK&amp;quot;, &amp;quot;green&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Reporting Equipment Status===&lt;br /&gt;
If running with the webserver [[mhttpd]], a frontend can send an update to the [[Status Page]], to report on its progress, using the function set_equipment_status() (see above example). This is useful when hardware can take a long time to respond.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function begin_of_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being started&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called every time a run start transition occurs, i.e. at begin-of-run. It allows the updating of user parameters, and the loading/setup/clearing of hardware. At the exit of this function, the acquisition should be armed and ready to test the interrupt (if used), e.g. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT begin_of_run (INT runnumber, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   // Read/validate some settings from the ODB (and return FE_ERR_ODB if there&#039;s a problem).&lt;br /&gt;
   // Apply them to the hardware (and return FE_ERR_HW if there&#039;s a problem).&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Functions pause/resume_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being paused/resumed.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
These two functions are called upon &amp;quot;Pause&amp;quot; and &amp;quot;Resume&amp;quot; command respectively. Any code relevant to the upcoming run state can be included,e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT pause_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   disable_trigger();&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
                            //&lt;br /&gt;
 INT resume_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   enable_trigger();&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function end_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being ended.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called at every &amp;quot;stop run&amp;quot; transition. It provides the opportunity to disable the hardware, e.g.&lt;br /&gt;
&lt;br /&gt;
 INT end_of_run(INT run_number, char *error)&lt;br /&gt;
 {&lt;br /&gt;
   // Stop the hardware.&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_exit==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
The function runs when the frontend program is shut down. Can be used to release any locked resources like memory, communications ports etc. e.g.&lt;br /&gt;
&lt;br /&gt;
  function frontend_exit()&lt;br /&gt;
  {&lt;br /&gt;
     mvme_close(gVme);&lt;br /&gt;
     return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_loop==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
If [[#Global declarations|frontend_call_loop]] is set to TRUE, this routine is called when the frontend is idle and at least once between every event. You could use it for example to check if there has been a timeout from hardware.&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 ...&lt;br /&gt;
                                 &lt;br /&gt;
 INT frontend_loop()&lt;br /&gt;
 {&lt;br /&gt;
   // Implement any code that needs to be run very frequently&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Event Types and Triggers==&lt;br /&gt;
The frontend supports several different types of [[Frontend Operation#Frontend event triggers|event trigger]]. The event trigger type is specified by the [[Equipment Flags|Equipment Flag]]  in the  [[Equipment List Parameters|Equipment Declaration]]. Common event types are &amp;quot;polled events&amp;quot; where the Equipment Flag is  [[Equipment Flags#EQ_POLLED|EQ_POLLED]], &amp;quot;interrupts events&amp;quot; where the Flag is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]], and &amp;quot;periodic events&amp;quot; where the Flag is [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]. The name of the associated readout routine is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type.&lt;br /&gt;
&lt;br /&gt;
Polled  and interrupt events (see [[#Event Types|Event Types]]) require several extra functions to handle the hardware that periodic events do not require. These are described below.&lt;br /&gt;
&lt;br /&gt;
Note that each frontend may contain:&lt;br /&gt;
* zero or one polled equipments&lt;br /&gt;
* zero or one interrupt equipments&lt;br /&gt;
* any number of periodic equipments&lt;br /&gt;
&lt;br /&gt;
==Function poll_event==&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* INT &#039;&#039;&#039;count&#039;&#039;&#039;&lt;br /&gt;
* BOOL &#039;&#039;&#039;test&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment.&lt;br /&gt;
         &lt;br /&gt;
The user must provide suitable code in the routine poll_event(), e.g. reading a register from a VME module to see if any data is available&lt;br /&gt;
&lt;br /&gt;
 INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
 {&lt;br /&gt;
    /* Polling routine for events. Returns TRUE if event  is available. If test equals TRUE, don&#039;t return. &lt;br /&gt;
       The test flag is used to time the polling &lt;br /&gt;
     */&lt;br /&gt;
     int i;&lt;br /&gt;
     int lam = 0;&lt;br /&gt;
                                 //&lt;br /&gt;
     for (i = 0; i &amp;lt; count; i++, lam++) {&lt;br /&gt;
       lam = vmeio_CsrRead(myvme, VMEIO_BASE);&lt;br /&gt;
       if (lam)&lt;br /&gt;
         if (!test)&lt;br /&gt;
           return lam;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function interrupt_configure==&lt;br /&gt;
* INT &#039;&#039;&#039;cmd&#039;&#039;&#039; &lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* PTYPE &#039;&#039;&#039;adr&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_INTERRUPT, an interrupt configuration routine called interrupt_configure() must be provided by the user.&lt;br /&gt;
The interrupt configuration routine has the following declaration: &lt;br /&gt;
&lt;br /&gt;
 /*-- Interrupt configuration --------------------------*/&lt;br /&gt;
 INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
 {&lt;br /&gt;
  int vec = 0;&lt;br /&gt;
  switch (cmd) &lt;br /&gt;
  {&lt;br /&gt;
    case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x1);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
      mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
      mvme_interrupt_attach(myvme, INT_LEVEL, INT_VECTOR, &lt;br /&gt;
                (void *)adr, &amp;amp;myinfo);&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+0x10, INT_VECTOR);&lt;br /&gt;
      vec = mvme_read_value(myvme, VLAM_BASE+0x10);&lt;br /&gt;
      printf(&amp;quot;Interrupt Attached to 0x%x for vector:0x%x\n&amp;quot;,&lt;br /&gt;
                     adr, vec&amp;amp;0xFF);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DETACH:&lt;br /&gt;
      printf(&amp;quot;Interrupt Detach\n&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Under the four commands listed above, the user must implement the hardware operation needed to perform the requested action. In the Midas drivers directory examples can be found of such an interrupt code for CAMAC. See source code such as hyt1331.c,ces8210.c &lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user in the frontend.&lt;br /&gt;
&lt;br /&gt;
==Event Readout routine==&lt;br /&gt;
&lt;br /&gt;
An event readout routine is required for all equipment, and is responsible for sending the actual data to midas. The framework calls the event readout routine whenever an equipment has been triggered (e.g. periodicially, or because poll_event() returned TRUE for a polled equipment etc). The function is of the form&lt;br /&gt;
&lt;br /&gt;
 INT function_name ( char *pevent ... )&lt;br /&gt;
 {&lt;br /&gt;
   INT event_size;&lt;br /&gt;
   ........  // read data from hardware&lt;br /&gt;
   ........  // pack into banks depending on format&lt;br /&gt;
   ........&lt;br /&gt;
   return (event_size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
where the first argument of the readout function (pevent)  provides the pointer to the newly constructed event, and points to the first valid location for storing the data.&lt;br /&gt;
;NOTE &lt;br /&gt;
* &amp;lt;b&amp;gt;The return value is the event size&amp;lt;/b&amp;gt;, and must be the number of bytes collected in this function. This is different to almost every other function in midas (where the return value is a status code).&lt;br /&gt;
* You can return 0 if you&#039;ve decided you don&#039;t actually want to write this event.&lt;br /&gt;
* The event serial number will be incremented by one for every call to the readout routine, as long as the returned size is non-zero.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===General readout function===&lt;br /&gt;
&lt;br /&gt;
A readout function is needed to send out data. This is done using one of the supported [[Event Structure|event structures]] usually [[Event Structure#MIDAS Format Event|&amp;quot;MIDAS&amp;quot; format]] data banks.  The bank format (&amp;quot;MIDAS&amp;quot; in the example above) is declared in the [[Equipment List Parameters]]   [[Equipment List Parameters#Format|Format]] field.&lt;br /&gt;
&lt;br /&gt;
An example of a scaler readout routine read_scaler_event() where the data is read out into [[MIDAS Event Construction|MIDAS data banks]] is shown below. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
   DWORD *pddata;&lt;br /&gt;
&lt;br /&gt;
   /* init bank structure */&lt;br /&gt;
   bk_init32(pevent);&lt;br /&gt;
&lt;br /&gt;
   /* create bank (bank names must be 4 chars long) */&lt;br /&gt;
   bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
&lt;br /&gt;
   /* fill data (just dummy values in this case) */&lt;br /&gt;
   memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
   pddata += 1000000;&lt;br /&gt;
   memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
&lt;br /&gt;
   /* close the bank */&lt;br /&gt;
   bk_close(pevent, pddata);&lt;br /&gt;
&lt;br /&gt;
   /* return the number of bytes we wrote */&lt;br /&gt;
   return bk_size(pevent);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Some other examples of event readout routines in this document are&lt;br /&gt;
* [[Event Structure#FIXED Event Construction|FIXED Format event construction]]&lt;br /&gt;
* [[Super Event]] construction&lt;br /&gt;
* [[Slow Control System|Slow Control]]&lt;br /&gt;
&lt;br /&gt;
Many other examples of readout routines can be found in the [[#Frontend Templates|frontend templates]] in the MIDAS package.&lt;br /&gt;
&lt;br /&gt;
===Polled or Interrupt readout routine===&lt;br /&gt;
In the case of a [[#Polled and Interrupt Events|Polled or Interrupt event]], the content of the memory location pointed to by &#039;&#039;&#039;pevent&#039;&#039;&#039; (see [[#Event Readout routine|Event Readout routine]]) prior to its use in the readout function, contains the interrupt source bitwise register. This feature can be exploited in order to identify which hardware module has triggered the readout when multiple interrupts have been assigned to the same readout function.&lt;br /&gt;
&lt;br /&gt;
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism.&lt;br /&gt;
The  [[Equipment List Parameters|Equipment declaration]] is of the form: &lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
               //&lt;br /&gt;
    {&amp;quot;Trigger&amp;quot;,  /* equipment name */&lt;br /&gt;
       ...&lt;br /&gt;
 #ifdef USE_INT&lt;br /&gt;
       EQ_INTERRUPT, /* equipment type */&lt;br /&gt;
 #else&lt;br /&gt;
       EQ_POLLED,    /* equipment type */&lt;br /&gt;
 #endif&lt;br /&gt;
   /* interrupt source: crate 0, all stations */&lt;br /&gt;
       LAM_SOURCE(0, 0x0),&lt;br /&gt;
       ....&lt;br /&gt;
       &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,&lt;br /&gt;
     },&lt;br /&gt;
     read_trigger_event, /* readout routine */&lt;br /&gt;
     NULL, NULL,&lt;br /&gt;
     trigger_bank_list,&lt;br /&gt;
 }&lt;br /&gt;
  &lt;br /&gt;
Note that the [[Macros#CAMAC Macros|LAM_SOURCE macro]] simply codes the parameters into a bitwise register.&lt;br /&gt;
&lt;br /&gt;
The readout routine would contains code such as&lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   DWORD  *pdata;&lt;br /&gt;
 #endif&lt;br /&gt;
                        //&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   /* read ADC0 data */&lt;br /&gt;
   v792_EvtCntRead(myvme, VADC0_BASE, &amp;amp;evtcnt);&lt;br /&gt;
   ........&lt;br /&gt;
   /* Read Event */&lt;br /&gt;
   v792_EventRead(myvme, VADC0_BASE, pdata, &amp;amp;nentry);&lt;br /&gt;
   ........&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
 #endif&lt;br /&gt;
                       //&lt;br /&gt;
   ........&lt;br /&gt;
   return (size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The data will be packed into banks as described for the [[General readout function|general readout function]] above.&lt;br /&gt;
The example &amp;lt;code&amp;gt;$MIDASSYS/examples/Triumf/c/fevmemodules.c&amp;lt;/code&amp;gt; contains a complete example of read_trigger_event().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Manual Trigger==&lt;br /&gt;
Another type of [[Frontend Operation#Frontend event trigger|frontend event trigger]] supported is the &amp;quot;manual trigger&amp;quot;,  where the Equipment Flag is [[Equipment Flags#EQ_MANUAL_TRIGGER|EQ_MANUAL_TRIGGER]]. This flag means that an event can be triggered through the URL &amp;lt;code&amp;gt;?cmd=Trigger/&amp;lt;equipment_name&amp;gt;&amp;lt;/code&amp;gt; (e.g. &amp;lt;code&amp;gt;http://my.host:8080/?cmd=Trigger/MyEquipmentName&amp;lt;/code&amp;gt;). If you add this URL to the [[/Alias ODB tree|/Alias]] part of the ODB, then a link will appear on midas webpages that can be clicked to trigger an event.&lt;br /&gt;
&lt;br /&gt;
In some cases, the same readout code may be used for two types of event: a manual trigger and (say) a poll event. It is possible to determine whether the readout of an event was triggered by a manual trigger or a regular trigger by adding a call to the  [[MIDAS Event Header Macros|event header Macro]] DATA_SIZE in the readout routine:&lt;br /&gt;
&lt;br /&gt;
  flag = DATA_SIZE(pevent);&lt;br /&gt;
&lt;br /&gt;
If the result is&lt;br /&gt;
  *  flag = 0 normal call&lt;br /&gt;
  *  flag = 1 manual trigger&lt;br /&gt;
&lt;br /&gt;
It is also possible for a backend MIDAS client (such as an analyzer or custom data archiver) to trigger a manual trigger event. The client controls when an event is sent by means of&lt;br /&gt;
a  function that requests an event by triggering the event sending mechanism with a RPC call.&lt;br /&gt;
&lt;br /&gt;
With a frontend Equipment declaration of a manually triggered event of the form:&lt;br /&gt;
    &lt;br /&gt;
    { &amp;quot;Histo&amp;quot;,             /* equipment name */&lt;br /&gt;
       2, 0,                 /* event ID, trigger mask */&lt;br /&gt;
       &amp;quot;SYSTEM&amp;quot;,             /* event buffer */&lt;br /&gt;
       EQ_MANUAL_TRIG,     /* equipment type */&lt;br /&gt;
       0,    &lt;br /&gt;
       .......&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
the code fragment to manually trigger this event is:&lt;br /&gt;
&lt;br /&gt;
  int main(unsigned int argc,char **argv)&lt;br /&gt;
  {&lt;br /&gt;
      .......&lt;br /&gt;
      bm_request_event(hBufEvent, 2, TRIGGER_ALL, GET_ALL, &amp;amp;request_id, process_event_TD);&lt;br /&gt;
      .......&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
When it is time to save the data during the run, the function below is called:&lt;br /&gt;
&lt;br /&gt;
 BOOL trigger_histo_event(void)&lt;br /&gt;
 {&lt;br /&gt;
   HNDLE hconn;&lt;br /&gt;
   BOOL event_triggered;&lt;br /&gt;
                            //&lt;br /&gt;
   event_triggered = FALSE;&lt;br /&gt;
   ...................&lt;br /&gt;
   if (run_state == STATE_RUNNING) {   // Check the frontend client exists&lt;br /&gt;
        if( cm_exist(ClientName,TRUE))  {    &lt;br /&gt;
            status = cm_connect_client (ClientName, &amp;amp;hconn);&lt;br /&gt;
            if(status != RPC_SUCCESS)&lt;br /&gt;
                cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Cannot connect to frontend \&amp;quot;%s\&amp;quot; (%d)&amp;quot;,&lt;br /&gt;
                       ClientName,status);&lt;br /&gt;
            else  {     // successfully connected to frontend client&lt;br /&gt;
                status = rpc_client_call(hconn, RPC_MANUAL_TRIG, 2); // trigger a histo event&lt;br /&gt;
                if (status != CM_SUCCESS)&lt;br /&gt;
                    cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error triggering event from frontend (%d)&amp;quot;,status);&lt;br /&gt;
                else {  // successfully triggered event&lt;br /&gt;
                    event_triggered=TRUE;&lt;br /&gt;
                    status =cm_disconnect_client(hconn, FALSE);&lt;br /&gt;
                    if (status != CM_SUCCESS)&lt;br /&gt;
                        cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error disconnecting client (%d)&amp;quot;,status);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Frontend client %s not running (%d)&amp;quot;,&lt;br /&gt;
                   ClientName,status);&lt;br /&gt;
    } &lt;br /&gt;
    return(event_triggered);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Deferred Transition==&lt;br /&gt;
This option permits the user to postpone any transition issued by any requester until some condition is satisfied.&lt;br /&gt;
For example:&lt;br /&gt;
* It may not be advisable to pause or stop a run until some hardware has turned off a particular valve. &lt;br /&gt;
* The start of the acquisition system should be postponed until the beam rate has been stable for a given period of time.&lt;br /&gt;
* While active, a particular acquisition system should not be interrupted until the &amp;quot;cycle&amp;quot; is completed.&lt;br /&gt;
&lt;br /&gt;
In these examples, any application having access to the state of the hardware can register to be a &amp;quot;transition Deferred&amp;quot; client. The MIDAS system will then catch any transition request and postpone the trigger of such a transition until the condition  is satisfied.&lt;br /&gt;
&lt;br /&gt;
The Deferred transition requires 3 steps for setup&lt;br /&gt;
# Register for the deferred transition &lt;br /&gt;
# Provide a callback function to serve the deferred transition &lt;br /&gt;
# Implement the condition code &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note that you should only have ONE frontend that defines a deferred transition&amp;lt;/b&amp;gt; (as this niche feature that was implemented with this assumption in mind...).&lt;br /&gt;
&lt;br /&gt;
The following example demonstrates this process:&lt;br /&gt;
&lt;br /&gt;
  BOOL transition_PS_requested=FALSE; // global&lt;br /&gt;
                                              //&lt;br /&gt;
  INT frontend_init()&lt;br /&gt;
  {&lt;br /&gt;
    // register for deferred transition&lt;br /&gt;
                                              //&lt;br /&gt;
    cm_register_deferred_transition(TR_STOP, wait_end_cycle);&lt;br /&gt;
    cm_register_deferred_transition(TR_PAUSE, wait_end_cycle);&lt;br /&gt;
    ...  &lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */ &lt;br /&gt;
  //-- Deferred transition callback&lt;br /&gt;
  BOOL wait_end_cycle(int transition, BOOL first)&lt;br /&gt;
  {&lt;br /&gt;
    if (first) {&lt;br /&gt;
      transition_PS_requested = TRUE;&lt;br /&gt;
      return FALSE;&lt;br /&gt;
    }&lt;br /&gt;
                        //&lt;br /&gt;
    if (end_of_mcs_cycle){&lt;br /&gt;
      transition_PS_requested = FALSE;&lt;br /&gt;
      end_of_mcs_cycle = FALSE;&lt;br /&gt;
      return TRUE;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
      return FALSE;&lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */&lt;br /&gt;
  INT read_mcs_event(char *pevent, INT offset)&lt;br /&gt;
  {  &lt;br /&gt;
      ...     // read out data at end of cycle&lt;br /&gt;
      ...&lt;br /&gt;
      end_of_mcs_cycle = TRUE; // end of cycle &lt;br /&gt;
                               //&lt;br /&gt;
      if (!transition_PS_requested)&lt;br /&gt;
         start_cycle(); // start a new cycle &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the example above, &lt;br /&gt;
&lt;br /&gt;
* The frontend code is registered for PAUSE and STOP using &#039;&#039;cm_register_deferred_transition()&#039;&#039;. The second argument &#039;&#039;wait_end_cycle&#039;&#039;  is the declaration of the callback function. &lt;br /&gt;
* The callback function &#039;&#039;wait_end_cycle&#039;&#039; will be called as soon as the transition is requested with the Boolean flag &#039;&#039;first&#039;&#039; set to TRUE.&lt;br /&gt;
* By setting &#039;&#039;transition_PS_requested&#039;&#039;  TRUE , the user will be provided with the acknowledgment of the transition request.&lt;br /&gt;
* By returning FALSE from the callback, the transition is prevented from occurring. &lt;br /&gt;
* As soon as the user condition is satisfied (&#039;&#039;end_of_mcs_cycle&#039;&#039; = TRUE), the return code in the  callback will be set to TRUE and the requested transition will be issued.&lt;br /&gt;
&lt;br /&gt;
While the transition is Deferred, the odb key  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; will contain the transition code, and the d [[mhttpd]] webserver main status page will indicate that a deferred transition is in progress.&lt;br /&gt;
&lt;br /&gt;
Once in deferred state, an [[odbedit]] override command can be issued to force the transition to happen,&lt;br /&gt;
&lt;br /&gt;
 &amp;gt;odbedit&lt;br /&gt;
 odb&amp;gt; stop now    (or &amp;quot;start now&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
or  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; can be set to 0. The transition will then take place on the next stop or start command.&lt;br /&gt;
&lt;br /&gt;
= Compilation using CMake =&lt;br /&gt;
&lt;br /&gt;
Here is a &amp;quot;minimal&amp;quot; CMakeLists.txt file that can be used to compile a user-written frontend (called &amp;quot;myfe&amp;quot; in this case) that uses the mfe framework.&lt;br /&gt;
&lt;br /&gt;
  cmake_minimum_required(VERSION 3.0)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  &lt;br /&gt;
  # Check for MIDASSYS environment variable&lt;br /&gt;
  if (NOT DEFINED ENV{MIDASSYS})&lt;br /&gt;
    message(SEND_ERROR &amp;quot;MIDASSYS environment variable not defined.&amp;quot;)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  set(CMAKE_CXX_STANDARD 11)&lt;br /&gt;
  set(MIDASSYS $ENV{MIDASSYS})&lt;br /&gt;
  &lt;br /&gt;
  if (${CMAKE_SYSTEM_NAME} MATCHES Linux)&lt;br /&gt;
    set(LIBS -lpthread -lutil -lrt)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  # Define the executable to be built, and the source code files&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  &lt;br /&gt;
  # Directories to search for &lt;br /&gt;
  target_include_directories(myfe.exe PRIVATE ${MIDASSYS}/include)&lt;br /&gt;
  &lt;br /&gt;
  # Libraries to link to&lt;br /&gt;
  target_link_libraries(myfe.exe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})&lt;br /&gt;
&lt;br /&gt;
One could then build the executable using the folllowing commands:&lt;br /&gt;
&lt;br /&gt;
  mkdir build&lt;br /&gt;
  cd build&lt;br /&gt;
  cmake ..&lt;br /&gt;
  make&lt;br /&gt;
&lt;br /&gt;
Some older operating systems may require the &amp;quot;cmake3&amp;quot; command to be used instead of &amp;quot;cmake&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Using mfed to reduce the amount of boiler-plate code =&lt;br /&gt;
&lt;br /&gt;
You can include &amp;lt;code&amp;gt;mfed.h&amp;lt;/code&amp;gt; and compile against &amp;lt;code&amp;gt;mfed.cxx&amp;lt;/code&amp;gt; to reduce the amount of boiler-plate code that needs to be written. &lt;br /&gt;
&lt;br /&gt;
In particular:&lt;br /&gt;
* You only need to define the frontend_name and frontend_file_name [[#Global Declarations|globals]] (not display_period etc)&lt;br /&gt;
* You only need to declare your event readout functions (not the [[#System Function Declarations|system functions]])&lt;br /&gt;
* You still need to define your EQUIPMENT structs&lt;br /&gt;
* You need to define frontend_init(), from which you can call install_poll_event(), install_begin_of_run() etc to use your transition functions. If you don&#039;t need a pause_run() function, you don&#039;t need to declare/define it!&lt;br /&gt;
* It does not support interrupt routines&lt;br /&gt;
&lt;br /&gt;
An example can be found in &amp;lt;code&amp;gt;$MIDASSYS/examples/experiment/frontend.cxx&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The full list of functions you can call in your frontend_init() are:&lt;br /&gt;
&lt;br /&gt;
; install_poll_event : Install a function which gets called to check if a new event is available for equipment of type EQ_POLLED.&lt;br /&gt;
; install_frontend_exit : Install a function which gets called when the frontend program finishes.&lt;br /&gt;
; install_begin_of_run : Install a function which gets called when a new run gets started.&lt;br /&gt;
; install_end_of_run : Install a function which gets called when a new run gets stopped.&lt;br /&gt;
; install_pause_run : Install a function which gets called when a new run gets paused.&lt;br /&gt;
; install_resume_run : Install a function which gets called when a new run gets resumed.&lt;br /&gt;
; install_frontend_loop : Install a function which gets called inside the main event loop as often as possible. This function gets all available CPU cycles, so in order not to take 100% CPU, this function can use the ss_sleep(10) function to give up some CPU cycles.&lt;br /&gt;
&lt;br /&gt;
When compiling with cmake, your incantation for the executable would change:&lt;br /&gt;
&lt;br /&gt;
 # Without mfed (regular frontend):&lt;br /&gt;
 add_executable(frontend frontend.cxx)&lt;br /&gt;
 &lt;br /&gt;
 # With mfed:&lt;br /&gt;
 add_executable(frontend frontend.cxx ${MIDASSYS}/src/mfed.cxx)&lt;br /&gt;
&lt;br /&gt;
== Minimal example without mfed ==&lt;br /&gt;
&lt;br /&gt;
   #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
   #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
   #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
   #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
   &lt;br /&gt;
   /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
   /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
   const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
   &lt;br /&gt;
   /* The frontend file name, don&#039;t change it */&lt;br /&gt;
   const char *frontend_file_name = __FILE__;&lt;br /&gt;
   &lt;br /&gt;
   /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
   BOOL frontend_call_loop = TRUE;&lt;br /&gt;
   &lt;br /&gt;
   /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
   INT display_period = 0000;&lt;br /&gt;
   &lt;br /&gt;
   /* maximum event size produced by this frontend */&lt;br /&gt;
   INT max_event_size = 10000;&lt;br /&gt;
   &lt;br /&gt;
   /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
   INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
   &lt;br /&gt;
   /* buffer size to hold events */&lt;br /&gt;
   INT event_buffer_size = 10 * 10000;&lt;br /&gt;
   &lt;br /&gt;
   /*-- Function declarations -----------------------------------------*/&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_init();&lt;br /&gt;
   INT frontend_exit();&lt;br /&gt;
   INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
   INT end_of_run(INT run_number, char *error);&lt;br /&gt;
   INT pause_run(INT run_number, char *error);&lt;br /&gt;
   INT resume_run(INT run_number, char *error);&lt;br /&gt;
   INT frontend_loop();&lt;br /&gt;
   INT read_large_event(char *pevent, INT off);&lt;br /&gt;
   &lt;br /&gt;
   /*-- Equipment list ------------------------------------------------*/&lt;br /&gt;
   &lt;br /&gt;
   #undef USE_INT&lt;br /&gt;
   BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
   &lt;br /&gt;
   EQUIPMENT equipment[] = {&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;large&amp;quot;,                 /* equipment name */&lt;br /&gt;
         {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
         &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
         EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
         0,                       /* event source */&lt;br /&gt;
         &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
         TRUE,                    /* enabled */&lt;br /&gt;
         RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
         2000,                    /* read every 2 sec */&lt;br /&gt;
         0,                       /* stop run after this event limit */&lt;br /&gt;
         0,                       /* number of sub events */&lt;br /&gt;
         0,                       /* log history */&lt;br /&gt;
         &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
         read_large_event,        /* readout routine */&lt;br /&gt;
         NULL, NULL,              /* keep null */&lt;br /&gt;
         NULL,                    /* init string */&lt;br /&gt;
         },&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;&amp;quot;}&lt;br /&gt;
   };&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_init()&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_exit()&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT begin_of_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT end_of_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT pause_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT resume_run(INT run_number, char *error)&lt;br /&gt;
   {&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_loop()&lt;br /&gt;
   {&lt;br /&gt;
      // Do something very frequently&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
      return 0;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
   {&lt;br /&gt;
      switch (cmd) {&lt;br /&gt;
      case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
         break;&lt;br /&gt;
      case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
         break;&lt;br /&gt;
      case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
         break;&lt;br /&gt;
      case CMD_INTERRUPT_DETACH:&lt;br /&gt;
         break;&lt;br /&gt;
      }&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   &lt;br /&gt;
   INT read_large_event(char *pevent, INT off)&lt;br /&gt;
   {&lt;br /&gt;
      DWORD *pddata;&lt;br /&gt;
   &lt;br /&gt;
      /* init bank structure */&lt;br /&gt;
      bk_init32(pevent);&lt;br /&gt;
   &lt;br /&gt;
      bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
      memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
      pddata += 1000000;&lt;br /&gt;
      memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
      bk_close(pevent, pddata);&lt;br /&gt;
   &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
== Minimal example with mfed ==&lt;br /&gt;
&lt;br /&gt;
Note that you only need boiler-plate for the features you&#039;re actually going to use.&lt;br /&gt;
&lt;br /&gt;
   #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
   #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
   #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
   #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
   &lt;br /&gt;
   /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
   /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
   const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
   &lt;br /&gt;
   /* The frontend file name, don&#039;t change it */&lt;br /&gt;
   const char *frontend_file_name = __FILE__;&lt;br /&gt;
   &lt;br /&gt;
   &lt;br /&gt;
   /*-- Function declarations -----------------------------------------*/&lt;br /&gt;
   INT frontend_init();&lt;br /&gt;
   INT my_frontend_loop();&lt;br /&gt;
   INT read_large_event(char *pevent, INT off);&lt;br /&gt;
   &lt;br /&gt;
   /*-- Equipment list ------------------------------------------------*/&lt;br /&gt;
   &lt;br /&gt;
   #undef USE_INT&lt;br /&gt;
   BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
   &lt;br /&gt;
   EQUIPMENT equipment[] = {&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;large&amp;quot;,                 /* equipment name */&lt;br /&gt;
         {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
         &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
         EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
         0,                       /* event source */&lt;br /&gt;
         &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
         TRUE,                    /* enabled */&lt;br /&gt;
         RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
         2000,                    /* read every 2 sec */&lt;br /&gt;
         0,                       /* stop run after this event limit */&lt;br /&gt;
         0,                       /* number of sub events */&lt;br /&gt;
         0,                       /* log history */&lt;br /&gt;
         &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
         read_large_event,        /* readout routine */&lt;br /&gt;
         NULL, NULL,              /* keep null */&lt;br /&gt;
         NULL,                    /* init string */&lt;br /&gt;
         },&lt;br /&gt;
   &lt;br /&gt;
      {&amp;quot;&amp;quot;}&lt;br /&gt;
   };&lt;br /&gt;
   &lt;br /&gt;
   INT frontend_init()&lt;br /&gt;
   {&lt;br /&gt;
      install_frontend_loop(my_frontend_loop);&lt;br /&gt;
   &lt;br /&gt;
      // Can also do install_begin_of_run() etc...&lt;br /&gt;
   &lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT my_frontend_loop()&lt;br /&gt;
   {&lt;br /&gt;
      // Do something frequently&lt;br /&gt;
      return SUCCESS;&lt;br /&gt;
   }&lt;br /&gt;
   &lt;br /&gt;
   INT read_large_event(char *pevent, INT off)&lt;br /&gt;
   {&lt;br /&gt;
      DWORD *pddata;&lt;br /&gt;
   &lt;br /&gt;
      /* init bank structure */&lt;br /&gt;
      bk_init32(pevent);&lt;br /&gt;
   &lt;br /&gt;
      bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
      memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
      pddata += 1000000;&lt;br /&gt;
      memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
      bk_close(pevent, pddata);&lt;br /&gt;
   &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3416</id>
		<title>Frontend user code</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3416"/>
		<updated>2024-01-05T20:02:54Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation]]&lt;br /&gt;
* [[Frontend Application]] &lt;br /&gt;
* [[Equipment List Parameters]]&lt;br /&gt;
* [[Slow Control System]]&lt;br /&gt;
* [[Equipment Flags]]&lt;br /&gt;
* [[Event Notification (Hot-Link)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This section describes the features of the user-written part of a &amp;quot;C-style&amp;quot; [[Frontend Operation#Frontend|Frontend]], referred to here as &#039;&#039;&#039;frontend.c&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
You can also write frontends using [[Frontend_user_code_(object_oriented_-_TMFE)|object-oriented C++ (TMFE)]] or [[Python]].&lt;br /&gt;
&lt;br /&gt;
The frontend system has evolved over the decades, and contains some legacy features that are not often used (e.g. interrupt handlers). For backwards-compatibility, these features are still present, but this does require a bit more boiler-plate code to be written for each frontend. We will first document every feature supported by the frontend system, then explain a slightly more user-friendly wrapper (mfed.cxx) that helps reduce the amount of boiler-plate needed for a new frontend.&lt;br /&gt;
&lt;br /&gt;
= Frontend Templates =&lt;br /&gt;
To make a user-written or custom frontend, users will usually modify one of the &#039;&#039;&#039;templates&#039;&#039;&#039; provided in the MIDAS package&lt;br /&gt;
under [[Environment Variables#MIDASSYS|$MIDASSYS]]/examples/ for their particular hardware and other requirements. Make sure to pick the closest template, e.g. if writing a [[Slow Control System|Slow Control]] frontend, pick a slow-control template. For each example frontend, a Makefile is also provided.&lt;br /&gt;
&lt;br /&gt;
A partial list of the templates provided is shown below:&lt;br /&gt;
{| style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;3&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Hardware &lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Filename&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Directory (MIDAS package)&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Purpose&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Language&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevmemodules.c&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevme.cxx&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c++/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|CAMAC &lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| Access to CAMAC modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|mtfe.c&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| &#039;&#039;&#039;Multithreaded&#039;&#039;&#039; using ring buffer&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Wiener CC-USB &lt;br /&gt;
|feccusb.cxx&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| CAMAC/USB demo &lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|RS485 bus &lt;br /&gt;
|mscb_fe.c&lt;br /&gt;
| $MIDASSYS/examples/slowcont/&lt;br /&gt;
| &#039;&#039;&#039;Slow control&#039;&#039;&#039; with [https://midas.psi.ch/mscb/ MSCB]&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|EPICS&lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/epics/&lt;br /&gt;
| &#039;&#039;&#039;Slow controls&#039;&#039;&#039;&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|Camac&lt;br /&gt;
|ebfe.c&lt;br /&gt;
| $MIDASSYS/examples/eventbuilder/&lt;br /&gt;
| &#039;&#039;&#039;Event Builder&#039;&#039;&#039; (with [[mevb]])&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|frontend.cxx&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| &#039;&#039;&#039;mfed&#039;&#039;&#039; (wrapper to simplify writing a frontend)&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The features of a typical frontend program are best explained by reference to examples of the user code &lt;br /&gt;
provided in the Midas Package. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Frontend code=&lt;br /&gt;
Note that there are several kinds of frontend used for different purposes, e.g.&lt;br /&gt;
* frontend to read VME or CAMAC hardware, using interrupt or polling (e.g. to read experimental data)&lt;br /&gt;
* multithreaded frontend where polling can be in a separate thread&lt;br /&gt;
* slow control frontend (can be multithreaded) see also [[Slow Control System]]&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Templates for many types of user frontend code are provided in the MIDAS packages (see [[#Frontend Templates]]). &lt;br /&gt;
&lt;br /&gt;
The following sections refer to these templates.&lt;br /&gt;
Most of the examples are taken from the [https://bitbucket.org/tmidas/midas/src/develop/examples/basic/largefe.cxx largefe.cxx example]. Documentation on the MIDAS library subroutines to access the ODB (some of which are used in the examples below) can be found in the [[ODB_Access_and_Use|ODB access page]].&lt;br /&gt;
&lt;br /&gt;
The user frontend code is then compiled and linked with the system part and the MIDAS library &lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Task]]).  Makefiles are provided with the templates that can be modified as needed.&lt;br /&gt;
&lt;br /&gt;
== Access to command line parameters ==&lt;br /&gt;
The function &#039;&#039;&#039;mfe_get_args&#039;&#039;&#039; gives access to the [[Frontend Application|Frontend]] command line parameters. &lt;br /&gt;
&lt;br /&gt;
This example shows how to use it in the frontend user code:&lt;br /&gt;
&lt;br /&gt;
  int argc;&lt;br /&gt;
  char **argv;&lt;br /&gt;
  mfe_get_args(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
  for (int i = 0; i &amp;lt; argc; i++) {&lt;br /&gt;
     // Use argv[i] &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
See [[Frontend_Application#Arguments|Frontend Application Arguments]] for the arguments that are handled automatically by the frontend system.&lt;br /&gt;
&lt;br /&gt;
==Include files==&lt;br /&gt;
The following example (from template frontend file &#039;&#039;$MIDASSYS/examples/basic/largefe.cxx&#039;&#039;) shows the standard include files needed for a frontend. The user may add any other include files as needed (e.g. those needed for VME access). &lt;br /&gt;
&lt;br /&gt;
Some legacy frontends may include the &#039;&#039;&#039;[[ODB#experim.h include file|experim.h]]&#039;&#039;&#039; file. This was an old way of accessing the ODB, but was not very user-friendly when the ODB structure needed to change.&lt;br /&gt;
&lt;br /&gt;
The example below shows a typical list of include files for a frontend:&lt;br /&gt;
 &lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
 #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;div id=&amp;quot;Frontend Name&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
==Global Declarations==&lt;br /&gt;
The following example (from template frontend file fevmemodules.c - see [[#Frontend Templates]]) shows the global declaration. The declarations are system wide.  Some may be changed to suit the user, but none should not be removed.&lt;br /&gt;
&lt;br /&gt;
; frontend_name  : This value can be modified to reflect the purpose of the code &lt;br /&gt;
; frontend_call_loop : If set to TRUE, the function frontend_loop() gets called very frequently. If FALSE, frontend_loop() does not run. The user can add suitable code to this routine if desired (e.g. to check for a condition).&lt;br /&gt;
; display_period : The time interval (defined in milliseconds) between the refresh of a frontend status display. The value of zero disables the display. If the frontend is started in the background with the display enabled, the stdout should be redirected to the null device to prevent the process from hanging.&lt;br /&gt;
; max_event_size : specifies the maximum size (in bytes) of the expected event.&lt;br /&gt;
; event_buffer_size : specifies the maximum size (in bytes) of the buffer to be allocated by the system.&lt;br /&gt;
; equipment_common_overwrite : whether the definitions in the EQUIPMENT struct override those in the /Equipment/largefe/Common section of the ODB. If FALSE, the values in the struct will be used the first time the program runs, then the ODB values will be used afterwards (e.g. allowing you to change the period of a [[#Event_Types_and_Triggers|periodic equipment]] via the ODB).&lt;br /&gt;
&lt;br /&gt;
See below for an example of global declarations from a frontend.&lt;br /&gt;
&lt;br /&gt;
 /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
 /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
 const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 /* The frontend file name, don&#039;t change it */&lt;br /&gt;
 const char *frontend_file_name = __FILE__;&lt;br /&gt;
 &lt;br /&gt;
 /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 &lt;br /&gt;
 /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
 INT display_period = 0;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size produced by this frontend */&lt;br /&gt;
 INT max_event_size = 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
 INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
 &lt;br /&gt;
 /* buffer size to hold events */&lt;br /&gt;
 INT event_buffer_size = 10 * 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* whether the values in EQUIPMENT struct override ODB values */&lt;br /&gt;
 BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==System Function Declarations==&lt;br /&gt;
&lt;br /&gt;
These lines declare the pre-defined system functions which should be present. &lt;br /&gt;
&lt;br /&gt;
 INT frontend_init();&lt;br /&gt;
 INT frontend_exit();&lt;br /&gt;
 INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
 INT end_of_run(INT run_number, char *error);&lt;br /&gt;
 INT pause_run(INT run_number, char *error);&lt;br /&gt;
 INT resume_run(INT run_number, char *error);&lt;br /&gt;
 INT frontend_loop();&lt;br /&gt;
&lt;br /&gt;
==Readout Function Declarations==&lt;br /&gt;
Following the previous group is a second group of declarations, which define the readout functions. These depend on the defined equipments, and run when the respective equipment is triggered. In this example, one equipment will be defined, so there is one declaration. The user functions will be described in detail in later sections. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off);&lt;br /&gt;
&lt;br /&gt;
If using an interrupt, callback function prototypes are also included&lt;br /&gt;
&lt;br /&gt;
 extern void interrupt_routine(void);&lt;br /&gt;
 void register_cnaf_callback(int debug);&lt;br /&gt;
&lt;br /&gt;
==Equipment List==&lt;br /&gt;
&lt;br /&gt;
This list of structs defines the behaviour of your frontend (e.g. [[#Event_Types_and_Triggers|periodic or polled equipment]]). See [[Equipment List Parameters|Equipment List]] for full documentation of the entries.&lt;br /&gt;
&lt;br /&gt;
Note that these are the default values for each equipment (used the very first time a frontend is run). If [[#Global declarations|equipment_common_overwrite]] is FALSE, then some of the values will subsequently be read from the ODB instead. If [[#Global declarations|equipment_common_overwrite]] is TRUE, then the ODB will be updated with the coded values each time the program runs.&lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;large&amp;quot;,                   /* equipment name */&lt;br /&gt;
     {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
      &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
      EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
      0,                       /* event source */&lt;br /&gt;
      &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
      TRUE,                    /* enabled */&lt;br /&gt;
      RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
      2000,                    /* read every 2 sec */&lt;br /&gt;
      0,                       /* stop run after this event limit */&lt;br /&gt;
      0,                       /* number of sub events */&lt;br /&gt;
      0,                       /* log history */&lt;br /&gt;
      &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
      read_large_event,        /* readout routine */&lt;br /&gt;
      NULL, NULL,              /* keep null */&lt;br /&gt;
      NULL,                    /* init string */&lt;br /&gt;
   },&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;&amp;quot;}&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the case of a polled equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Trigger&amp;quot;,            // equipment name&lt;br /&gt;
        {&lt;br /&gt;
          ...&lt;br /&gt;
          &amp;lt;b&amp;gt;EQ_POLLED&amp;lt;/b&amp;gt;,          // equipment type&lt;br /&gt;
          ...&lt;br /&gt;
          500,                // poll for 500ms &lt;br /&gt;
          ...&lt;br /&gt;
          &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
          read_my_event,    // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the case of a periodic equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Scaler&amp;quot;,           // equipment name&lt;br /&gt;
         {    &lt;br /&gt;
            ...&lt;br /&gt;
            EQ_PERIODIC     // equipment type&lt;br /&gt;
            ...&lt;br /&gt;
            10000,          // period (read every 10s)&lt;br /&gt;
            ...&lt;br /&gt;
            &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
       read_my_event,   // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sequence of Operations in the frontend==&lt;br /&gt;
&lt;br /&gt;
The following table shows the sequence of operations of the  [[Frontend Operation#Frontend|Frontend]] System functions.  These functions must be implemented in the user&#039;s code (but may be as simple as just returning &amp;lt;code&amp;gt;SUCCESS&amp;lt;/code&amp;gt; if the function is not relevant for your use case). These functions are called by &#039;&#039;mfe.cxx&#039;&#039; at the appropriate time. The System Transition functions are associated with a particular [[Run States and Transitions|Run Transition]] as shown below:&lt;br /&gt;
&lt;br /&gt;
{|  style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Transition Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Associated [[Run States and Transitions|Transition]]&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Action&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_init()&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| Runs once after system initialization, before Equipment registration. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| begin_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_START&lt;br /&gt;
| Runs after system statistics reset at each begin-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| pause_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_PAUSE&lt;br /&gt;
| Runs at each pause-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| resume_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_RESUME&lt;br /&gt;
| Runs at each resume-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| end_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_STOP&lt;br /&gt;
| Runs at each end-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_exit()&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Runs once before any Slow Control Equipment exit &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Each defined Equipment has the option to force itself to run at individual transition times (see Equipment [[Equipment List Parameters#ReadOn|ReadOn Flag]]), so that its equipment function will be called on a certain transition (or combination of transitions). &lt;br /&gt;
&lt;br /&gt;
The system transition functions all run &#039;&#039;&#039;prior to&#039;&#039;&#039; the equipment functions. This gives the system the chance to take basic action on the transition request (e.g. enable/disable interrupt) before the equipment runs. All the transition routines run with a [[Run States and Transitions#Default Transition Sequency Numbers|Transition Sequence number]] of 500 (the default). This allows users to add additional functions in the frontend that will run before or after any of the transitions (such as a prestart() or a poststop() function). See [[Run States and Transitions#Run Transition Priority|Run Transition Priority]] for more information.&lt;br /&gt;
&lt;br /&gt;
==Function frontend_init ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
No parameters.&lt;br /&gt;
&lt;br /&gt;
This function runs &#039;&#039;&#039;once only&#039;&#039;&#039; at the application startup. Users may perform hardware checking, loading/setting of global variables, mapping of required ODB structures (see [[ODB#experim.h include file|experim.h include file]]), &lt;br /&gt;
[[Event Notification (Hot-Link)#How to set up a Hot-Link|setting up hot-links]] etc. in frontend_init(), e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT frontend_init()&lt;br /&gt;
 {&lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;Initializing...&amp;quot;, &amp;quot;yellow&amp;quot;);&lt;br /&gt;
   ....                  &lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;OK&amp;quot;, &amp;quot;green&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Reporting Equipment Status===&lt;br /&gt;
If running with the webserver [[mhttpd]], a frontend can send an update to the [[Status Page]], to report on its progress, using the function set_equipment_status() (see above example). This is useful when hardware can take a long time to respond.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function begin_of_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being started&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called every time a run start transition occurs, i.e. at begin-of-run. It allows the updating of user parameters, and the loading/setup/clearing of hardware. At the exit of this function, the acquisition should be armed and ready to test the interrupt (if used), e.g. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT begin_of_run (INT runnumber, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   // Read/validate some settings from the ODB (and return FE_ERR_ODB if there&#039;s a problem).&lt;br /&gt;
   // Apply them to the hardware (and return FE_ERR_HW if there&#039;s a problem).&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Functions pause/resume_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being paused/resumed.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
These two functions are called upon &amp;quot;Pause&amp;quot; and &amp;quot;Resume&amp;quot; command respectively. Any code relevant to the upcoming run state can be included,e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT pause_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   disable_trigger();&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
                            //&lt;br /&gt;
 INT resume_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   enable_trigger();&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function end_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being ended.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called at every &amp;quot;stop run&amp;quot; transition. It provides the opportunity to disable the hardware, e.g.&lt;br /&gt;
&lt;br /&gt;
 INT end_of_run(INT run_number, char *error)&lt;br /&gt;
 {&lt;br /&gt;
   // Stop the hardware.&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_exit==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
The function runs when the frontend program is shut down. Can be used to release any locked resources like memory, communications ports etc. e.g.&lt;br /&gt;
&lt;br /&gt;
  function frontend_exit()&lt;br /&gt;
  {&lt;br /&gt;
     mvme_close(gVme);&lt;br /&gt;
     return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_loop==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
If [[#Global declarations|frontend_call_loop]] is set to TRUE, this routine is called when the frontend is idle and at least once between every event. You could use it for example to check if there has been a timeout from hardware.&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 ...&lt;br /&gt;
                                 &lt;br /&gt;
 INT frontend_loop()&lt;br /&gt;
 {&lt;br /&gt;
   // Implement any code that needs to be run very frequently&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Event Types and Triggers==&lt;br /&gt;
The frontend supports several different types of [[Frontend Operation#Frontend event triggers|event trigger]]. The event trigger type is specified by the [[Equipment Flags|Equipment Flag]]  in the  [[Equipment List Parameters|Equipment Declaration]]. Common event types are &amp;quot;polled events&amp;quot; where the Equipment Flag is  [[Equipment Flags#EQ_POLLED|EQ_POLLED]], &amp;quot;interrupts events&amp;quot; where the Flag is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]], and &amp;quot;periodic events&amp;quot; where the Flag is [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]. The name of the associated readout routine is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type.&lt;br /&gt;
&lt;br /&gt;
Polled  and interrupt events (see [[#Event Types|Event Types]]) require several extra functions to handle the hardware that periodic events do not require. These are described below.&lt;br /&gt;
&lt;br /&gt;
Note that each frontend may contain:&lt;br /&gt;
* zero or one polled equipments&lt;br /&gt;
* zero or one interrupt equipments&lt;br /&gt;
* any number of periodic equipments&lt;br /&gt;
&lt;br /&gt;
==Function poll_event==&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* INT &#039;&#039;&#039;count&#039;&#039;&#039;&lt;br /&gt;
* BOOL &#039;&#039;&#039;test&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment.&lt;br /&gt;
         &lt;br /&gt;
The user must provide suitable code in the routine poll_event(), e.g. reading a register from a VME module to see if any data is available&lt;br /&gt;
&lt;br /&gt;
 INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
 {&lt;br /&gt;
    /* Polling routine for events. Returns TRUE if event  is available. If test equals TRUE, don&#039;t return. &lt;br /&gt;
       The test flag is used to time the polling &lt;br /&gt;
     */&lt;br /&gt;
     int i;&lt;br /&gt;
     int lam = 0;&lt;br /&gt;
                                 //&lt;br /&gt;
     for (i = 0; i &amp;lt; count; i++, lam++) {&lt;br /&gt;
       lam = vmeio_CsrRead(myvme, VMEIO_BASE);&lt;br /&gt;
       if (lam)&lt;br /&gt;
         if (!test)&lt;br /&gt;
           return lam;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function interrupt_configure==&lt;br /&gt;
* INT &#039;&#039;&#039;cmd&#039;&#039;&#039; &lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* PTYPE &#039;&#039;&#039;adr&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_INTERRUPT, an interrupt configuration routine called interrupt_configure() must be provided by the user.&lt;br /&gt;
The interrupt configuration routine has the following declaration: &lt;br /&gt;
&lt;br /&gt;
 /*-- Interrupt configuration --------------------------*/&lt;br /&gt;
 INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
 {&lt;br /&gt;
  int vec = 0;&lt;br /&gt;
  switch (cmd) &lt;br /&gt;
  {&lt;br /&gt;
    case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x1);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
      mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
      mvme_interrupt_attach(myvme, INT_LEVEL, INT_VECTOR, &lt;br /&gt;
                (void *)adr, &amp;amp;myinfo);&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+0x10, INT_VECTOR);&lt;br /&gt;
      vec = mvme_read_value(myvme, VLAM_BASE+0x10);&lt;br /&gt;
      printf(&amp;quot;Interrupt Attached to 0x%x for vector:0x%x\n&amp;quot;,&lt;br /&gt;
                     adr, vec&amp;amp;0xFF);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DETACH:&lt;br /&gt;
      printf(&amp;quot;Interrupt Detach\n&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Under the four commands listed above, the user must implement the hardware operation needed to perform the requested action. In the Midas drivers directory examples can be found of such an interrupt code for CAMAC. See source code such as hyt1331.c,ces8210.c &lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user in the frontend.&lt;br /&gt;
&lt;br /&gt;
==Event Readout routine==&lt;br /&gt;
&lt;br /&gt;
An event readout routine is required for all equipment, and is responsible for sending the actual data to midas. The framework calls the event readout routine whenever an equipment has been triggered (e.g. periodicially, or because poll_event() returned TRUE for a polled equipment etc). The function is of the form&lt;br /&gt;
&lt;br /&gt;
 INT function_name ( char *pevent ... )&lt;br /&gt;
 {&lt;br /&gt;
   INT event_size;&lt;br /&gt;
   ........  // read data from hardware&lt;br /&gt;
   ........  // pack into banks depending on format&lt;br /&gt;
   ........&lt;br /&gt;
   return (event_size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
where the first argument of the readout function (pevent)  provides the pointer to the newly constructed event, and points to the first valid location for storing the data.&lt;br /&gt;
;NOTE &lt;br /&gt;
* &amp;lt;b&amp;gt;The return value is the event size&amp;lt;/b&amp;gt;, and must be the number of bytes collected in this function. This is different to almost every other function in midas (where the return value is a status code).&lt;br /&gt;
* You can return 0 if you&#039;ve decided you don&#039;t actually want to write this event.&lt;br /&gt;
* The event serial number will be incremented by one for every call to the readout routine, as long as the returned size is non-zero.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===General readout function===&lt;br /&gt;
&lt;br /&gt;
A readout function is needed to send out data. This is done using one of the supported [[Event Structure|event structures]] usually [[Event Structure#MIDAS Format Event|&amp;quot;MIDAS&amp;quot; format]] data banks.  The bank format (&amp;quot;MIDAS&amp;quot; in the example above) is declared in the [[Equipment List Parameters]]   [[Equipment List Parameters#Format|Format]] field.&lt;br /&gt;
&lt;br /&gt;
An example of a scaler readout routine read_scaler_event() where the data is read out into [[MIDAS Event Construction|MIDAS data banks]] is shown below. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
   DWORD *pddata;&lt;br /&gt;
&lt;br /&gt;
   /* init bank structure */&lt;br /&gt;
   bk_init32(pevent);&lt;br /&gt;
&lt;br /&gt;
   /* create bank (bank names must be 4 chars long) */&lt;br /&gt;
   bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
&lt;br /&gt;
   /* fill data (just dummy values in this case) */&lt;br /&gt;
   memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
   pddata += 1000000;&lt;br /&gt;
   memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
&lt;br /&gt;
   /* close the bank */&lt;br /&gt;
   bk_close(pevent, pddata);&lt;br /&gt;
&lt;br /&gt;
   /* return the number of bytes we wrote */&lt;br /&gt;
   return bk_size(pevent);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Some other examples of event readout routines in this document are&lt;br /&gt;
* [[Event Structure#FIXED Event Construction|FIXED Format event construction]]&lt;br /&gt;
* [[Super Event]] construction&lt;br /&gt;
* [[Slow Control System|Slow Control]]&lt;br /&gt;
&lt;br /&gt;
Many other examples of readout routines can be found in the [[#Frontend Templates|frontend templates]] in the MIDAS package.&lt;br /&gt;
&lt;br /&gt;
===Polled or Interrupt readout routine===&lt;br /&gt;
In the case of a [[#Polled and Interrupt Events|Polled or Interrupt event]], the content of the memory location pointed to by &#039;&#039;&#039;pevent&#039;&#039;&#039; (see [[#Event Readout routine|Event Readout routine]]) prior to its use in the readout function, contains the interrupt source bitwise register. This feature can be exploited in order to identify which hardware module has triggered the readout when multiple interrupts have been assigned to the same readout function.&lt;br /&gt;
&lt;br /&gt;
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism.&lt;br /&gt;
The  [[Equipment List Parameters|Equipment declaration]] is of the form: &lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
               //&lt;br /&gt;
    {&amp;quot;Trigger&amp;quot;,  /* equipment name */&lt;br /&gt;
       ...&lt;br /&gt;
 #ifdef USE_INT&lt;br /&gt;
       EQ_INTERRUPT, /* equipment type */&lt;br /&gt;
 #else&lt;br /&gt;
       EQ_POLLED,    /* equipment type */&lt;br /&gt;
 #endif&lt;br /&gt;
   /* interrupt source: crate 0, all stations */&lt;br /&gt;
       LAM_SOURCE(0, 0x0),&lt;br /&gt;
       ....&lt;br /&gt;
       &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,&lt;br /&gt;
     },&lt;br /&gt;
     read_trigger_event, /* readout routine */&lt;br /&gt;
     NULL, NULL,&lt;br /&gt;
     trigger_bank_list,&lt;br /&gt;
 }&lt;br /&gt;
  &lt;br /&gt;
Note that the [[Macros#CAMAC Macros|LAM_SOURCE macro]] simply codes the parameters into a bitwise register.&lt;br /&gt;
&lt;br /&gt;
The readout routine would contains code such as&lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   DWORD  *pdata;&lt;br /&gt;
 #endif&lt;br /&gt;
                        //&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   /* read ADC0 data */&lt;br /&gt;
   v792_EvtCntRead(myvme, VADC0_BASE, &amp;amp;evtcnt);&lt;br /&gt;
   ........&lt;br /&gt;
   /* Read Event */&lt;br /&gt;
   v792_EventRead(myvme, VADC0_BASE, pdata, &amp;amp;nentry);&lt;br /&gt;
   ........&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
 #endif&lt;br /&gt;
                       //&lt;br /&gt;
   ........&lt;br /&gt;
   return (size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The data will be packed into banks as described for the [[General readout function|general readout function]] above.&lt;br /&gt;
The example &amp;lt;code&amp;gt;$MIDASSYS/examples/Triumf/c/fevmemodules.c&amp;lt;/code&amp;gt; contains a complete example of read_trigger_event().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Manual Trigger==&lt;br /&gt;
Another type of [[Frontend Operation#Frontend event trigger|frontend event trigger]] supported is the &amp;quot;manual trigger&amp;quot;,  where the Equipment Flag is [[Equipment Flags#EQ_MANUAL_TRIGGER|EQ_MANUAL_TRIGGER]]. This flag means that an event can be triggered through the URL &amp;lt;code&amp;gt;?cmd=Trigger/&amp;lt;equipment_name&amp;gt;&amp;lt;/code&amp;gt; (e.g. &amp;lt;code&amp;gt;http://my.host:8080/?cmd=Trigger/MyEquipmentName&amp;lt;/code&amp;gt;). If you add this URL to the [[/Alias ODB tree|/Alias]] part of the ODB, then a link will appear on midas webpages that can be clicked to trigger an event.&lt;br /&gt;
&lt;br /&gt;
In some cases, the same readout code may be used for two types of event: a manual trigger and (say) a poll event. It is possible to determine whether the readout of an event was triggered by a manual trigger or a regular trigger by adding a call to the  [[MIDAS Event Header Macros|event header Macro]] DATA_SIZE in the readout routine:&lt;br /&gt;
&lt;br /&gt;
  flag = DATA_SIZE(pevent);&lt;br /&gt;
&lt;br /&gt;
If the result is&lt;br /&gt;
  *  flag = 0 normal call&lt;br /&gt;
  *  flag = 1 manual trigger&lt;br /&gt;
&lt;br /&gt;
It is also possible for a backend MIDAS client (such as an analyzer or custom data archiver) to trigger a manual trigger event. The client controls when an event is sent by means of&lt;br /&gt;
a  function that requests an event by triggering the event sending mechanism with a RPC call.&lt;br /&gt;
&lt;br /&gt;
With a frontend Equipment declaration of a manually triggered event of the form:&lt;br /&gt;
    &lt;br /&gt;
    { &amp;quot;Histo&amp;quot;,             /* equipment name */&lt;br /&gt;
       2, 0,                 /* event ID, trigger mask */&lt;br /&gt;
       &amp;quot;SYSTEM&amp;quot;,             /* event buffer */&lt;br /&gt;
       EQ_MANUAL_TRIG,     /* equipment type */&lt;br /&gt;
       0,    &lt;br /&gt;
       .......&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
the code fragment to manually trigger this event is:&lt;br /&gt;
&lt;br /&gt;
  int main(unsigned int argc,char **argv)&lt;br /&gt;
  {&lt;br /&gt;
      .......&lt;br /&gt;
      bm_request_event(hBufEvent, 2, TRIGGER_ALL, GET_ALL, &amp;amp;request_id, process_event_TD);&lt;br /&gt;
      .......&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
When it is time to save the data during the run, the function below is called:&lt;br /&gt;
&lt;br /&gt;
 BOOL trigger_histo_event(void)&lt;br /&gt;
 {&lt;br /&gt;
   HNDLE hconn;&lt;br /&gt;
   BOOL event_triggered;&lt;br /&gt;
                            //&lt;br /&gt;
   event_triggered = FALSE;&lt;br /&gt;
   ...................&lt;br /&gt;
   if (run_state == STATE_RUNNING) {   // Check the frontend client exists&lt;br /&gt;
        if( cm_exist(ClientName,TRUE))  {    &lt;br /&gt;
            status = cm_connect_client (ClientName, &amp;amp;hconn);&lt;br /&gt;
            if(status != RPC_SUCCESS)&lt;br /&gt;
                cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Cannot connect to frontend \&amp;quot;%s\&amp;quot; (%d)&amp;quot;,&lt;br /&gt;
                       ClientName,status);&lt;br /&gt;
            else  {     // successfully connected to frontend client&lt;br /&gt;
                status = rpc_client_call(hconn, RPC_MANUAL_TRIG, 2); // trigger a histo event&lt;br /&gt;
                if (status != CM_SUCCESS)&lt;br /&gt;
                    cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error triggering event from frontend (%d)&amp;quot;,status);&lt;br /&gt;
                else {  // successfully triggered event&lt;br /&gt;
                    event_triggered=TRUE;&lt;br /&gt;
                    status =cm_disconnect_client(hconn, FALSE);&lt;br /&gt;
                    if (status != CM_SUCCESS)&lt;br /&gt;
                        cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error disconnecting client (%d)&amp;quot;,status);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Frontend client %s not running (%d)&amp;quot;,&lt;br /&gt;
                   ClientName,status);&lt;br /&gt;
    } &lt;br /&gt;
    return(event_triggered);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Deferred Transition==&lt;br /&gt;
This option permits the user to postpone any transition issued by any requester until some condition is satisfied.&lt;br /&gt;
For example:&lt;br /&gt;
* It may not be advisable to pause or stop a run until some hardware has turned off a particular valve. &lt;br /&gt;
* The start of the acquisition system should be postponed until the beam rate has been stable for a given period of time.&lt;br /&gt;
* While active, a particular acquisition system should not be interrupted until the &amp;quot;cycle&amp;quot; is completed.&lt;br /&gt;
&lt;br /&gt;
In these examples, any application having access to the state of the hardware can register to be a &amp;quot;transition Deferred&amp;quot; client. The MIDAS system will then catch any transition request and postpone the trigger of such a transition until the condition  is satisfied.&lt;br /&gt;
&lt;br /&gt;
The Deferred transition requires 3 steps for setup&lt;br /&gt;
# Register for the deferred transition &lt;br /&gt;
# Provide a callback function to serve the deferred transition &lt;br /&gt;
# Implement the condition code &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note that you should only have ONE frontend that defines a deferred transition&amp;lt;/b&amp;gt; (as this niche feature that was implemented with this assumption in mind...).&lt;br /&gt;
&lt;br /&gt;
The following example demonstrates this process:&lt;br /&gt;
&lt;br /&gt;
  BOOL transition_PS_requested=FALSE; // global&lt;br /&gt;
                                              //&lt;br /&gt;
  INT frontend_init()&lt;br /&gt;
  {&lt;br /&gt;
    // register for deferred transition&lt;br /&gt;
                                              //&lt;br /&gt;
    cm_register_deferred_transition(TR_STOP, wait_end_cycle);&lt;br /&gt;
    cm_register_deferred_transition(TR_PAUSE, wait_end_cycle);&lt;br /&gt;
    ...  &lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */ &lt;br /&gt;
  //-- Deferred transition callback&lt;br /&gt;
  BOOL wait_end_cycle(int transition, BOOL first)&lt;br /&gt;
  {&lt;br /&gt;
    if (first) {&lt;br /&gt;
      transition_PS_requested = TRUE;&lt;br /&gt;
      return FALSE;&lt;br /&gt;
    }&lt;br /&gt;
                        //&lt;br /&gt;
    if (end_of_mcs_cycle){&lt;br /&gt;
      transition_PS_requested = FALSE;&lt;br /&gt;
      end_of_mcs_cycle = FALSE;&lt;br /&gt;
      return TRUE;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
      return FALSE;&lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */&lt;br /&gt;
  INT read_mcs_event(char *pevent, INT offset)&lt;br /&gt;
  {  &lt;br /&gt;
      ...     // read out data at end of cycle&lt;br /&gt;
      ...&lt;br /&gt;
      end_of_mcs_cycle = TRUE; // end of cycle &lt;br /&gt;
                               //&lt;br /&gt;
      if (!transition_PS_requested)&lt;br /&gt;
         start_cycle(); // start a new cycle &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the example above, &lt;br /&gt;
&lt;br /&gt;
* The frontend code is registered for PAUSE and STOP using &#039;&#039;cm_register_deferred_transition()&#039;&#039;. The second argument &#039;&#039;wait_end_cycle&#039;&#039;  is the declaration of the callback function. &lt;br /&gt;
* The callback function &#039;&#039;wait_end_cycle&#039;&#039; will be called as soon as the transition is requested with the Boolean flag &#039;&#039;first&#039;&#039; set to TRUE.&lt;br /&gt;
* By setting &#039;&#039;transition_PS_requested&#039;&#039;  TRUE , the user will be provided with the acknowledgment of the transition request.&lt;br /&gt;
* By returning FALSE from the callback, the transition is prevented from occurring. &lt;br /&gt;
* As soon as the user condition is satisfied (&#039;&#039;end_of_mcs_cycle&#039;&#039; = TRUE), the return code in the  callback will be set to TRUE and the requested transition will be issued.&lt;br /&gt;
&lt;br /&gt;
While the transition is Deferred, the odb key  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; will contain the transition code, and the d [[mhttpd]] webserver main status page will indicate that a deferred transition is in progress.&lt;br /&gt;
&lt;br /&gt;
Once in deferred state, an [[odbedit]] override command can be issued to force the transition to happen,&lt;br /&gt;
&lt;br /&gt;
 &amp;gt;odbedit&lt;br /&gt;
 odb&amp;gt; stop now    (or &amp;quot;start now&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
or  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; can be set to 0. The transition will then take place on the next stop or start command.&lt;br /&gt;
&lt;br /&gt;
= Compilation using CMake =&lt;br /&gt;
&lt;br /&gt;
Here is a &amp;quot;minimal&amp;quot; CMakeLists.txt file that can be used to compile a user-written frontend (called &amp;quot;myfe&amp;quot; in this case) that uses the mfe framework.&lt;br /&gt;
&lt;br /&gt;
  cmake_minimum_required(VERSION 3.0)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  &lt;br /&gt;
  # Check for MIDASSYS environment variable&lt;br /&gt;
  if (NOT DEFINED ENV{MIDASSYS})&lt;br /&gt;
    message(SEND_ERROR &amp;quot;MIDASSYS environment variable not defined.&amp;quot;)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  set(CMAKE_CXX_STANDARD 11)&lt;br /&gt;
  set(MIDASSYS $ENV{MIDASSYS})&lt;br /&gt;
  &lt;br /&gt;
  if (${CMAKE_SYSTEM_NAME} MATCHES Linux)&lt;br /&gt;
    set(LIBS -lpthread -lutil -lrt)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  # Define the executable to be built, and the source code files&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  &lt;br /&gt;
  # Directories to search for &lt;br /&gt;
  target_include_directories(myfe.exe PRIVATE ${MIDASSYS}/include)&lt;br /&gt;
  &lt;br /&gt;
  # Libraries to link to&lt;br /&gt;
  target_link_libraries(myfe.exe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})&lt;br /&gt;
&lt;br /&gt;
One could then build the executable using the folllowing commands:&lt;br /&gt;
&lt;br /&gt;
  mkdir build&lt;br /&gt;
  cd build&lt;br /&gt;
  cmake ..&lt;br /&gt;
  make&lt;br /&gt;
&lt;br /&gt;
Some older operating systems may require the &amp;quot;cmake3&amp;quot; command to be used instead of &amp;quot;cmake&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Using mfed to reduce the amount of boiler-plate code =&lt;br /&gt;
&lt;br /&gt;
You can include &amp;lt;code&amp;gt;mfed.h&amp;lt;/code&amp;gt; and compile against &amp;lt;code&amp;gt;mfed.cxx&amp;lt;/code&amp;gt; to reduce the amount of boiler-plate code that needs to be written. &lt;br /&gt;
&lt;br /&gt;
In particular:&lt;br /&gt;
* You only need to define the frontend_name and frontend_file_name [[#Global Declarations|globals]] (not display_period etc)&lt;br /&gt;
* You only need to declare your event readout functions (not the [[#System Function Declarations|system functions]])&lt;br /&gt;
* You still need to define your EQUIPMENT structs&lt;br /&gt;
* You need to define frontend_init(), from which you can call install_poll_event(), install_begin_of_run() etc to use your transition functions. If you don&#039;t need a pause_run() function, you don&#039;t need to declare/define it!&lt;br /&gt;
* It does not support interrupt routines&lt;br /&gt;
&lt;br /&gt;
An example can be found in &amp;lt;code&amp;gt;$MIDASSYS/examples/experiment/frontend.cxx&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The full list of functions you can call in your frontend_init() are:&lt;br /&gt;
&lt;br /&gt;
; install_poll_event : Install a function which gets called to check if a new event is available for equipment of type EQ_POLLED.&lt;br /&gt;
; install_frontend_exit : Install a function which gets called when the frontend program finishes.&lt;br /&gt;
; install_begin_of_run : Install a function which gets called when a new run gets started.&lt;br /&gt;
; install_end_of_run : Install a function which gets called when a new run gets stopped.&lt;br /&gt;
; install_pause_run : Install a function which gets called when a new run gets paused.&lt;br /&gt;
; install_resume_run : Install a function which gets called when a new run gets resumed.&lt;br /&gt;
; install_frontend_loop : Install a function which gets called inside the main event loop as often as possible. This function gets all available CPU cycles, so in order not to take 100% CPU, this function can use the ss_sleep(10) function to give up some CPU cycles.&lt;br /&gt;
&lt;br /&gt;
When compiling with cmake, your incantation for the executable would change:&lt;br /&gt;
&lt;br /&gt;
 # Without mfed (regular frontend):&lt;br /&gt;
 add_executable(frontend frontend.cxx)&lt;br /&gt;
 &lt;br /&gt;
 # With mfed:&lt;br /&gt;
 add_executable(frontend frontend.cxx ${MIDASSYS}/src/mfed.cxx)&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3415</id>
		<title>Frontend user code</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3415"/>
		<updated>2024-01-05T19:23:24Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation]]&lt;br /&gt;
* [[Frontend Application]] &lt;br /&gt;
* [[Equipment List Parameters]]&lt;br /&gt;
* [[Slow Control System]]&lt;br /&gt;
* [[Equipment Flags]]&lt;br /&gt;
* [[Event Notification (Hot-Link)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This section describes the features of the user-written part of a &amp;quot;C-style&amp;quot; [[Frontend Operation#Frontend|Frontend]], referred to here as &#039;&#039;&#039;frontend.c&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
You can also write frontends using [[Frontend_user_code_(object_oriented_-_TMFE)|object-oriented C++ (TMFE)]] or [[Python]].&lt;br /&gt;
&lt;br /&gt;
The frontend system has evolved over the decades, and contains some legacy features that are not often used (e.g. interrupt handlers). For backwards-compatibility, these features are still present, but this does require a bit more boiler-plate code to be written for each frontend. We will first document every feature supported by the frontend system, then explain a slightly more user-friendly wrapper (mfed.cxx) that helps reduce the amount of boiler-plate needed for a new frontend.&lt;br /&gt;
&lt;br /&gt;
= Frontend Templates =&lt;br /&gt;
To make a user-written or custom frontend, users will usually modify one of the &#039;&#039;&#039;templates&#039;&#039;&#039; provided in the MIDAS package&lt;br /&gt;
under [[Environment Variables#MIDASSYS|$MIDASSYS]]/examples/ for their particular hardware and other requirements. Make sure to pick the closest template, e.g. if writing a [[Slow Control System|Slow Control]] frontend, pick a slow-control template. For each example frontend, a Makefile is also provided.&lt;br /&gt;
&lt;br /&gt;
A partial list of the templates provided is shown below:&lt;br /&gt;
{| style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;3&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Hardware &lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Filename&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Directory (MIDAS package)&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Purpose&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Language&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevmemodules.c&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevme.cxx&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c++/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|CAMAC &lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| Access to CAMAC modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|mtfe.c&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| &#039;&#039;&#039;Multithreaded&#039;&#039;&#039; using ring buffer&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Wiener CC-USB &lt;br /&gt;
|feccusb.cxx&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| CAMAC/USB demo &lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|RS485 bus &lt;br /&gt;
|mscb_fe.c&lt;br /&gt;
| $MIDASSYS/examples/slowcont/&lt;br /&gt;
| &#039;&#039;&#039;Slow control&#039;&#039;&#039; with [https://midas.psi.ch/mscb/ MSCB]&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|EPICS&lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/epics/&lt;br /&gt;
| &#039;&#039;&#039;Slow controls&#039;&#039;&#039;&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|Camac&lt;br /&gt;
|ebfe.c&lt;br /&gt;
| $MIDASSYS/examples/eventbuilder/&lt;br /&gt;
| &#039;&#039;&#039;Event Builder&#039;&#039;&#039; (with [[mevb]])&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|frontend.cxx&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| &#039;&#039;&#039;mfed&#039;&#039;&#039; (wrapper to simplify writing a frontend)&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The features of a typical frontend program are best explained by reference to examples of the user code &lt;br /&gt;
provided in the Midas Package. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Frontend code=&lt;br /&gt;
Note that there are several kinds of frontend used for different purposes, e.g.&lt;br /&gt;
* frontend to read VME or CAMAC hardware, using interrupt or polling (e.g. to read experimental data)&lt;br /&gt;
* multithreaded frontend where polling can be in a separate thread&lt;br /&gt;
* slow control frontend (can be multithreaded) see also [[Slow Control System]]&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Templates for many types of user frontend code are provided in the MIDAS packages (see [[#Frontend Templates]]). &lt;br /&gt;
&lt;br /&gt;
The following sections refer to these templates.&lt;br /&gt;
Most of the examples are taken from the [https://bitbucket.org/tmidas/midas/src/develop/examples/basic/largefe.cxx largefe.cxx example]. Documentation on the MIDAS library subroutines to access the ODB (some of which are used in the examples below) can be found in the [[ODB_Access_and_Use|ODB access page]].&lt;br /&gt;
&lt;br /&gt;
The user frontend code is then compiled and linked with the system part and the MIDAS library &lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Task]]).  Makefiles are provided with the templates that can be modified as needed.&lt;br /&gt;
&lt;br /&gt;
== Access to command line parameters ==&lt;br /&gt;
The function &#039;&#039;&#039;mfe_get_args&#039;&#039;&#039; gives access to the [[Frontend Application|Frontend]] command line parameters. &lt;br /&gt;
&lt;br /&gt;
This example shows how to use it in the frontend user code:&lt;br /&gt;
&lt;br /&gt;
  int argc;&lt;br /&gt;
  char **argv;&lt;br /&gt;
  mfe_get_args(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
  for (int i = 0; i &amp;lt; argc; i++) {&lt;br /&gt;
     // Use argv[i] &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
See [[Frontend_Application#Arguments|Frontend Application Arguments]] for the arguments that are handled automatically by the frontend system.&lt;br /&gt;
&lt;br /&gt;
==Include files==&lt;br /&gt;
The following example (from template frontend file &#039;&#039;$MIDASSYS/examples/basic/largefe.cxx&#039;&#039;) shows the standard include files needed for a frontend. The user may add any other include files as needed (e.g. those needed for VME access). &lt;br /&gt;
&lt;br /&gt;
Some legacy frontends may include the &#039;&#039;&#039;[[ODB#experim.h include file|experim.h]]&#039;&#039;&#039; file. This was an old way of accessing the ODB, but was not very user-friendly when the ODB structure needed to change.&lt;br /&gt;
&lt;br /&gt;
The example below shows a typical list of include files for a frontend:&lt;br /&gt;
 &lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
 #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;div id=&amp;quot;Frontend Name&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
==Global Declarations==&lt;br /&gt;
The following example (from template frontend file fevmemodules.c - see [[#Frontend Templates]]) shows the global declaration. The declarations are system wide.  Some may be changed to suit the user, but none should not be removed.&lt;br /&gt;
&lt;br /&gt;
; frontend_name  : This value can be modified to reflect the purpose of the code &lt;br /&gt;
; frontend_call_loop : If set to TRUE, the function frontend_loop() gets called very frequently. If FALSE, frontend_loop() does not run. The user can add suitable code to this routine if desired (e.g. to check for a condition).&lt;br /&gt;
; display_period : The time interval (defined in milliseconds) between the refresh of a frontend status display. The value of zero disables the display. If the frontend is started in the background with the display enabled, the stdout should be redirected to the null device to prevent the process from hanging.&lt;br /&gt;
; max_event_size : specifies the maximum size (in bytes) of the expected event.&lt;br /&gt;
; event_buffer_size : specifies the maximum size (in bytes) of the buffer to be allocated by the system.&lt;br /&gt;
; equipment_common_overwrite : whether the definitions in the EQUIPMENT struct override those in the /Equipment/largefe/Common section of the ODB. If FALSE, the values in the struct will be used the first time the program runs, then the ODB values will be used afterwards (e.g. allowing you to change the period of a [[#Event_Types_and_Triggers|periodic equipment]] via the ODB).&lt;br /&gt;
&lt;br /&gt;
See below for an example of global declarations from a frontend.&lt;br /&gt;
&lt;br /&gt;
 /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
 /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
 const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 /* The frontend file name, don&#039;t change it */&lt;br /&gt;
 const char *frontend_file_name = __FILE__;&lt;br /&gt;
 &lt;br /&gt;
 /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 &lt;br /&gt;
 /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
 INT display_period = 0;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size produced by this frontend */&lt;br /&gt;
 INT max_event_size = 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
 INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
 &lt;br /&gt;
 /* buffer size to hold events */&lt;br /&gt;
 INT event_buffer_size = 10 * 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* whether the values in EQUIPMENT struct override ODB values */&lt;br /&gt;
 BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==System Function Declarations==&lt;br /&gt;
&lt;br /&gt;
These lines declare the pre-defined system functions which should be present. &lt;br /&gt;
&lt;br /&gt;
 INT frontend_init();&lt;br /&gt;
 INT frontend_exit();&lt;br /&gt;
 INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
 INT end_of_run(INT run_number, char *error);&lt;br /&gt;
 INT pause_run(INT run_number, char *error);&lt;br /&gt;
 INT resume_run(INT run_number, char *error);&lt;br /&gt;
 INT frontend_loop();&lt;br /&gt;
&lt;br /&gt;
==Readout Function Declarations==&lt;br /&gt;
Following the previous group is a second group of declarations, which define the readout functions. These depend on the defined equipments, and run when the respective equipment is triggered. In this example, one equipment will be defined, so there is one declaration. The user functions will be described in detail in later sections. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off);&lt;br /&gt;
&lt;br /&gt;
If using an interrupt, callback function prototypes are also included&lt;br /&gt;
&lt;br /&gt;
 extern void interrupt_routine(void);&lt;br /&gt;
 void register_cnaf_callback(int debug);&lt;br /&gt;
&lt;br /&gt;
==Equipment List==&lt;br /&gt;
&lt;br /&gt;
This list of structs defines the behaviour of your frontend (e.g. [[#Event_Types_and_Triggers|periodic or polled equipment]]). See [[Equipment List Parameters|Equipment List]] for full documentation of the entries.&lt;br /&gt;
&lt;br /&gt;
Note that these are the default values for each equipment (used the very first time a frontend is run). If [[#Global declarations|equipment_common_overwrite]] is FALSE, then some of the values will subsequently be read from the ODB instead. If [[#Global declarations|equipment_common_overwrite]] is TRUE, then the ODB will be updated with the coded values each time the program runs.&lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;large&amp;quot;,                   /* equipment name */&lt;br /&gt;
     {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
      &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
      EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
      0,                       /* event source */&lt;br /&gt;
      &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
      TRUE,                    /* enabled */&lt;br /&gt;
      RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
      2000,                    /* read every 2 sec */&lt;br /&gt;
      0,                       /* stop run after this event limit */&lt;br /&gt;
      0,                       /* number of sub events */&lt;br /&gt;
      0,                       /* log history */&lt;br /&gt;
      &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
      read_large_event,        /* readout routine */&lt;br /&gt;
      NULL, NULL,              /* keep null */&lt;br /&gt;
      NULL,                    /* init string */&lt;br /&gt;
   },&lt;br /&gt;
 &lt;br /&gt;
   {&amp;quot;&amp;quot;}&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the case of a polled equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Trigger&amp;quot;,            // equipment name&lt;br /&gt;
        {&lt;br /&gt;
          ...&lt;br /&gt;
          &amp;lt;b&amp;gt;EQ_POLLED&amp;lt;/b&amp;gt;,          // equipment type&lt;br /&gt;
          ...&lt;br /&gt;
          500,                // poll for 500ms &lt;br /&gt;
          ...&lt;br /&gt;
          &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
          read_my_event,    // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the case of a periodic equipment, the struct would be of the form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Scaler&amp;quot;,           // equipment name&lt;br /&gt;
         {    &lt;br /&gt;
            ...&lt;br /&gt;
            EQ_PERIODIC     // equipment type&lt;br /&gt;
            ...&lt;br /&gt;
            10000,          // period (read every 10s)&lt;br /&gt;
            ...&lt;br /&gt;
            &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
       read_my_event,   // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sequence of Operations in the frontend==&lt;br /&gt;
&lt;br /&gt;
The following table shows the sequence of operations of the  [[Frontend Operation#Frontend|Frontend]] System functions.  These functions must be implemented in the user&#039;s code (but may be as simple as just returning &amp;lt;code&amp;gt;SUCCESS&amp;lt;/code&amp;gt; if the function is not relevant for your use case). These functions are called by &#039;&#039;mfe.cxx&#039;&#039; at the appropriate time. The System Transition functions are associated with a particular [[Run States and Transitions|Run Transition]] as shown below:&lt;br /&gt;
&lt;br /&gt;
{|  style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Transition Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Associated [[Run States and Transitions|Transition]]&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Action&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_init()&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| Runs once after system initialization, before Equipment registration. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| begin_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_START&lt;br /&gt;
| Runs after system statistics reset at each begin-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| pause_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_PAUSE&lt;br /&gt;
| Runs at each pause-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| resume_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_RESUME&lt;br /&gt;
| Runs at each resume-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| end_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_STOP&lt;br /&gt;
| Runs at each end-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_exit()&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Runs once before any Slow Control Equipment exit &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Each defined Equipment has the option to force itself to run at individual transition times (see Equipment [[Equipment List Parameters#ReadOn|ReadOn Flag]]), so that its equipment function will be called on a certain transition (or combination of transitions). &lt;br /&gt;
&lt;br /&gt;
The system transition functions all run &#039;&#039;&#039;prior to&#039;&#039;&#039; the equipment functions. This gives the system the chance to take basic action on the transition request (e.g. enable/disable interrupt) before the equipment runs. All the transition routines run with a [[Run States and Transitions#Default Transition Sequency Numbers|Transition Sequence number]] of 500 (the default). This allows users to add additional functions in the frontend that will run before or after any of the transitions (such as a prestart() or a poststop() function). See [[Run States and Transitions#Run Transition Priority|Run Transition Priority]] for more information.&lt;br /&gt;
&lt;br /&gt;
==Function frontend_init ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
No parameters.&lt;br /&gt;
&lt;br /&gt;
This function runs &#039;&#039;&#039;once only&#039;&#039;&#039; at the application startup. Users may perform hardware checking, loading/setting of global variables, mapping of required ODB structures (see [[ODB#experim.h include file|experim.h include file]]), &lt;br /&gt;
[[Event Notification (Hot-Link)#How to set up a Hot-Link|setting up hot-links]] etc. in frontend_init(), e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT frontend_init()&lt;br /&gt;
 {&lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;Initializing...&amp;quot;, &amp;quot;yellow&amp;quot;);&lt;br /&gt;
   ....                  &lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;OK&amp;quot;, &amp;quot;green&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Reporting Equipment Status===&lt;br /&gt;
If running with the webserver [[mhttpd]], a frontend can send an update to the [[Status Page]], to report on its progress, using the function set_equipment_status() (see above example). This is useful when hardware can take a long time to respond.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function begin_of_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being started&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called every time a run start transition occurs, i.e. at begin-of-run. It allows the updating of user parameters, and the loading/setup/clearing of hardware. At the exit of this function, the acquisition should be armed and ready to test the interrupt (if used), e.g. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT begin_of_run (INT runnumber, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   // Read/validate some settings from the ODB (and return FE_ERR_ODB if there&#039;s a problem).&lt;br /&gt;
   // Apply them to the hardware (and return FE_ERR_HW if there&#039;s a problem).&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Functions pause/resume_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being paused/resumed.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
These two functions are called upon &amp;quot;Pause&amp;quot; and &amp;quot;Resume&amp;quot; command respectively. Any code relevant to the upcoming run state can be included,e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT pause_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   disable_trigger();&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
                            //&lt;br /&gt;
 INT resume_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   enable_trigger();&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function end_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being ended.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called at every &amp;quot;stop run&amp;quot; transition. It provides the opportunity to disable the hardware, e.g.&lt;br /&gt;
&lt;br /&gt;
 INT end_of_run(INT run_number, char *error)&lt;br /&gt;
 {&lt;br /&gt;
   // Stop the hardware.&lt;br /&gt;
   // etc...&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_exit==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
The function runs when the frontend program is shut down. Can be used to release any locked resources like memory, communications ports etc. e.g.&lt;br /&gt;
&lt;br /&gt;
  function frontend_exit()&lt;br /&gt;
  {&lt;br /&gt;
     mvme_close(gVme);&lt;br /&gt;
     return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_loop==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
If [[#Global declarations|frontend_call_loop]] is set to TRUE, this routine is called when the frontend is idle and at least once between every event. You could use it for example to check if there has been a timeout from hardware.&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 ...&lt;br /&gt;
                                 &lt;br /&gt;
 INT frontend_loop()&lt;br /&gt;
 {&lt;br /&gt;
   // Implement any code that needs to be run very frequently&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Event Types and Triggers==&lt;br /&gt;
The frontend supports several different types of [[Frontend Operation#Frontend event triggers|event trigger]]. The event trigger type is specified by the [[Equipment Flags|Equipment Flag]]  in the  [[Equipment List Parameters|Equipment Declaration]]. Common event types are &amp;quot;polled events&amp;quot; where the Equipment Flag is  [[Equipment Flags#EQ_POLLED|EQ_POLLED]], &amp;quot;interrupts events&amp;quot; where the Flag is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]], and &amp;quot;periodic events&amp;quot; where the Flag is [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]. The name of the associated readout routine is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type.&lt;br /&gt;
&lt;br /&gt;
Polled  and interrupt events (see [[#Event Types|Event Types]]) require several extra functions to handle the hardware that periodic events do not require. These are described below.&lt;br /&gt;
&lt;br /&gt;
Note that each frontend may contain:&lt;br /&gt;
* zero or one polled equipments&lt;br /&gt;
* zero or one interrupt equipments&lt;br /&gt;
* any number of periodic equipments&lt;br /&gt;
&lt;br /&gt;
==Function poll_event==&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* INT &#039;&#039;&#039;count&#039;&#039;&#039;&lt;br /&gt;
* BOOL &#039;&#039;&#039;test&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment.&lt;br /&gt;
         &lt;br /&gt;
The user must provide suitable code in the routine poll_event(), e.g. reading a register from a VME module to see if any data is available&lt;br /&gt;
&lt;br /&gt;
 INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
 {&lt;br /&gt;
    /* Polling routine for events. Returns TRUE if event  is available. If test equals TRUE, don&#039;t return. &lt;br /&gt;
       The test flag is used to time the polling &lt;br /&gt;
     */&lt;br /&gt;
     int i;&lt;br /&gt;
     int lam = 0;&lt;br /&gt;
                                 //&lt;br /&gt;
     for (i = 0; i &amp;lt; count; i++, lam++) {&lt;br /&gt;
       lam = vmeio_CsrRead(myvme, VMEIO_BASE);&lt;br /&gt;
       if (lam)&lt;br /&gt;
         if (!test)&lt;br /&gt;
           return lam;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function interrupt_configure==&lt;br /&gt;
* INT &#039;&#039;&#039;cmd&#039;&#039;&#039; &lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* PTYPE &#039;&#039;&#039;adr&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_INTERRUPT, an interrupt configuration routine called interrupt_configure() must be provided by the user.&lt;br /&gt;
The interrupt configuration routine has the following declaration: &lt;br /&gt;
&lt;br /&gt;
 /*-- Interrupt configuration --------------------------*/&lt;br /&gt;
 INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
 {&lt;br /&gt;
  int vec = 0;&lt;br /&gt;
  switch (cmd) &lt;br /&gt;
  {&lt;br /&gt;
    case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x1);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
      mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
      mvme_interrupt_attach(myvme, INT_LEVEL, INT_VECTOR, &lt;br /&gt;
                (void *)adr, &amp;amp;myinfo);&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+0x10, INT_VECTOR);&lt;br /&gt;
      vec = mvme_read_value(myvme, VLAM_BASE+0x10);&lt;br /&gt;
      printf(&amp;quot;Interrupt Attached to 0x%x for vector:0x%x\n&amp;quot;,&lt;br /&gt;
                     adr, vec&amp;amp;0xFF);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DETACH:&lt;br /&gt;
      printf(&amp;quot;Interrupt Detach\n&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Under the four commands listed above, the user must implement the hardware operation needed to perform the requested action. In the Midas drivers directory examples can be found of such an interrupt code for CAMAC. See source code such as hyt1331.c,ces8210.c &lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user in the frontend.&lt;br /&gt;
&lt;br /&gt;
==Event Readout routine==&lt;br /&gt;
&lt;br /&gt;
An event readout routine is required for all equipment, and is responsible for sending the actual data to midas. The framework calls the event readout routine whenever an equipment has been triggered (e.g. periodicially, or because poll_event() returned TRUE for a polled equipment etc). The function is of the form&lt;br /&gt;
&lt;br /&gt;
 INT function_name ( char *pevent ... )&lt;br /&gt;
 {&lt;br /&gt;
   INT event_size;&lt;br /&gt;
   ........  // read data from hardware&lt;br /&gt;
   ........  // pack into banks depending on format&lt;br /&gt;
   ........&lt;br /&gt;
   return (event_size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
where the first argument of the readout function (pevent)  provides the pointer to the newly constructed event, and points to the first valid location for storing the data.&lt;br /&gt;
;NOTE &lt;br /&gt;
* &amp;lt;b&amp;gt;The return value is the event size&amp;lt;/b&amp;gt;, and must be the number of bytes collected in this function. This is different to almost every other function in midas (where the return value is a status code).&lt;br /&gt;
* You can return 0 if you&#039;ve decided you don&#039;t actually want to write this event.&lt;br /&gt;
* The event serial number will be incremented by one for every call to the readout routine, as long as the returned size is non-zero.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===General readout function===&lt;br /&gt;
&lt;br /&gt;
A readout function is needed to send out data. This is done using one of the supported [[Event Structure|event structures]] usually [[Event Structure#MIDAS Format Event|&amp;quot;MIDAS&amp;quot; format]] data banks.  The bank format (&amp;quot;MIDAS&amp;quot; in the example above) is declared in the [[Equipment List Parameters]]   [[Equipment List Parameters#Format|Format]] field.&lt;br /&gt;
&lt;br /&gt;
An example of a scaler readout routine read_scaler_event() where the data is read out into [[MIDAS Event Construction|MIDAS data banks]] is shown below. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
   DWORD *pddata;&lt;br /&gt;
&lt;br /&gt;
   /* init bank structure */&lt;br /&gt;
   bk_init32(pevent);&lt;br /&gt;
&lt;br /&gt;
   /* create bank (bank names must be 4 chars long) */&lt;br /&gt;
   bk_create(pevent, &amp;quot;BIGG&amp;quot;, TID_DWORD, (void **) &amp;amp;pddata);&lt;br /&gt;
&lt;br /&gt;
   /* fill data (just dummy values in this case) */&lt;br /&gt;
   memset((char *) pddata, 0x0000, 100);&lt;br /&gt;
   pddata += 1000000;&lt;br /&gt;
   memset((char *) pddata - 100, 0xFFFF, 100);&lt;br /&gt;
&lt;br /&gt;
   /* close the bank */&lt;br /&gt;
   bk_close(pevent, pddata);&lt;br /&gt;
&lt;br /&gt;
   /* return the number of bytes we wrote */&lt;br /&gt;
   return bk_size(pevent);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Some other examples of event readout routines in this document are&lt;br /&gt;
* [[Event Structure#FIXED Event Construction|FIXED Format event construction]]&lt;br /&gt;
* [[Super Event]] construction&lt;br /&gt;
* [[Slow Control System|Slow Control]]&lt;br /&gt;
&lt;br /&gt;
Many other examples of readout routines can be found in the [[#Frontend Templates|frontend templates]] in the MIDAS package.&lt;br /&gt;
&lt;br /&gt;
===Polled or Interrupt readout routine===&lt;br /&gt;
In the case of a [[#Polled and Interrupt Events|Polled or Interrupt event]], the content of the memory location pointed to by &#039;&#039;&#039;pevent&#039;&#039;&#039; (see [[#Event Readout routine|Event Readout routine]]) prior to its use in the readout function, contains the interrupt source bitwise register. This feature can be exploited in order to identify which hardware module has triggered the readout when multiple interrupts have been assigned to the same readout function.&lt;br /&gt;
&lt;br /&gt;
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism.&lt;br /&gt;
The  [[Equipment List Parameters|Equipment declaration]] is of the form: &lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
               //&lt;br /&gt;
    {&amp;quot;Trigger&amp;quot;,  /* equipment name */&lt;br /&gt;
       ...&lt;br /&gt;
 #ifdef USE_INT&lt;br /&gt;
       EQ_INTERRUPT, /* equipment type */&lt;br /&gt;
 #else&lt;br /&gt;
       EQ_POLLED,    /* equipment type */&lt;br /&gt;
 #endif&lt;br /&gt;
   /* interrupt source: crate 0, all stations */&lt;br /&gt;
       LAM_SOURCE(0, 0x0),&lt;br /&gt;
       ....&lt;br /&gt;
       &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,&lt;br /&gt;
     },&lt;br /&gt;
     read_trigger_event, /* readout routine */&lt;br /&gt;
     NULL, NULL,&lt;br /&gt;
     trigger_bank_list,&lt;br /&gt;
 }&lt;br /&gt;
  &lt;br /&gt;
Note that the [[Macros#CAMAC Macros|LAM_SOURCE macro]] simply codes the parameters into a bitwise register.&lt;br /&gt;
&lt;br /&gt;
The readout routine would contains code such as&lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   DWORD  *pdata;&lt;br /&gt;
 #endif&lt;br /&gt;
                        //&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   /* read ADC0 data */&lt;br /&gt;
   v792_EvtCntRead(myvme, VADC0_BASE, &amp;amp;evtcnt);&lt;br /&gt;
   ........&lt;br /&gt;
   /* Read Event */&lt;br /&gt;
   v792_EventRead(myvme, VADC0_BASE, pdata, &amp;amp;nentry);&lt;br /&gt;
   ........&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
 #endif&lt;br /&gt;
                       //&lt;br /&gt;
   ........&lt;br /&gt;
   return (size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The data will be packed into banks as described for the [[General readout function|general readout function]] above.&lt;br /&gt;
The example &amp;lt;code&amp;gt;$MIDASSYS/examples/Triumf/c/fevmemodules.c&amp;lt;/code&amp;gt; contains a complete example of read_trigger_event().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Manual Trigger==&lt;br /&gt;
Another type of [[Frontend Operation#Frontend event trigger|frontend event trigger]] supported is the &amp;quot;manual trigger&amp;quot;,  where the Equipment Flag is [[Equipment Flags#EQ_MANUAL_TRIGGER|EQ_MANUAL_TRIGGER]]. This flag means that an event can be triggered through the URL &amp;lt;code&amp;gt;?cmd=Trigger/&amp;lt;equipment_name&amp;gt;&amp;lt;/code&amp;gt; (e.g. &amp;lt;code&amp;gt;http://my.host:8080/?cmd=Trigger/MyEquipmentName&amp;lt;/code&amp;gt;). If you add this URL to the [[/Alias ODB tree|/Alias]] part of the ODB, then a link will appear on midas webpages that can be clicked to trigger an event.&lt;br /&gt;
&lt;br /&gt;
In some cases, the same readout code may be used for two types of event: a manual trigger and (say) a poll event. It is possible to determine whether the readout of an event was triggered by a manual trigger or a regular trigger by adding a call to the  [[MIDAS Event Header Macros|event header Macro]] DATA_SIZE in the readout routine:&lt;br /&gt;
&lt;br /&gt;
  flag = DATA_SIZE(pevent);&lt;br /&gt;
&lt;br /&gt;
If the result is&lt;br /&gt;
  *  flag = 0 normal call&lt;br /&gt;
  *  flag = 1 manual trigger&lt;br /&gt;
&lt;br /&gt;
It is also possible for a backend MIDAS client (such as an analyzer or custom data archiver) to trigger a manual trigger event. The client controls when an event is sent by means of&lt;br /&gt;
a  function that requests an event by triggering the event sending mechanism with a RPC call.&lt;br /&gt;
&lt;br /&gt;
With a frontend Equipment declaration of a manually triggered event of the form:&lt;br /&gt;
    &lt;br /&gt;
    { &amp;quot;Histo&amp;quot;,             /* equipment name */&lt;br /&gt;
       2, 0,                 /* event ID, trigger mask */&lt;br /&gt;
       &amp;quot;SYSTEM&amp;quot;,             /* event buffer */&lt;br /&gt;
       EQ_MANUAL_TRIG,     /* equipment type */&lt;br /&gt;
       0,    &lt;br /&gt;
       .......&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
the code fragment to manually trigger this event is:&lt;br /&gt;
&lt;br /&gt;
  int main(unsigned int argc,char **argv)&lt;br /&gt;
  {&lt;br /&gt;
      .......&lt;br /&gt;
      bm_request_event(hBufEvent, 2, TRIGGER_ALL, GET_ALL, &amp;amp;request_id, process_event_TD);&lt;br /&gt;
      .......&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
When it is time to save the data during the run, the function below is called:&lt;br /&gt;
&lt;br /&gt;
 BOOL trigger_histo_event(void)&lt;br /&gt;
 {&lt;br /&gt;
   HNDLE hconn;&lt;br /&gt;
   BOOL event_triggered;&lt;br /&gt;
                            //&lt;br /&gt;
   event_triggered = FALSE;&lt;br /&gt;
   ...................&lt;br /&gt;
   if (run_state == STATE_RUNNING) {   // Check the frontend client exists&lt;br /&gt;
        if( cm_exist(ClientName,TRUE))  {    &lt;br /&gt;
            status = cm_connect_client (ClientName, &amp;amp;hconn);&lt;br /&gt;
            if(status != RPC_SUCCESS)&lt;br /&gt;
                cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Cannot connect to frontend \&amp;quot;%s\&amp;quot; (%d)&amp;quot;,&lt;br /&gt;
                       ClientName,status);&lt;br /&gt;
            else  {     // successfully connected to frontend client&lt;br /&gt;
                status = rpc_client_call(hconn, RPC_MANUAL_TRIG, 2); // trigger a histo event&lt;br /&gt;
                if (status != CM_SUCCESS)&lt;br /&gt;
                    cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error triggering event from frontend (%d)&amp;quot;,status);&lt;br /&gt;
                else {  // successfully triggered event&lt;br /&gt;
                    event_triggered=TRUE;&lt;br /&gt;
                    status =cm_disconnect_client(hconn, FALSE);&lt;br /&gt;
                    if (status != CM_SUCCESS)&lt;br /&gt;
                        cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error disconnecting client (%d)&amp;quot;,status);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Frontend client %s not running (%d)&amp;quot;,&lt;br /&gt;
                   ClientName,status);&lt;br /&gt;
    } &lt;br /&gt;
    return(event_triggered);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Deferred Transition==&lt;br /&gt;
This option permits the user to postpone any transition issued by any requester until some condition is satisfied.&lt;br /&gt;
For example:&lt;br /&gt;
* It may not be advisable to pause or stop a run until some hardware has turned off a particular valve. &lt;br /&gt;
* The start of the acquisition system should be postponed until the beam rate has been stable for a given period of time.&lt;br /&gt;
* While active, a particular acquisition system should not be interrupted until the &amp;quot;cycle&amp;quot; is completed.&lt;br /&gt;
&lt;br /&gt;
In these examples, any application having access to the state of the hardware can register to be a &amp;quot;transition Deferred&amp;quot; client. The MIDAS system will then catch any transition request and postpone the trigger of such a transition until the condition  is satisfied.&lt;br /&gt;
&lt;br /&gt;
The Deferred transition requires 3 steps for setup&lt;br /&gt;
# Register for the deferred transition &lt;br /&gt;
# Provide a callback function to serve the deferred transition &lt;br /&gt;
# Implement the condition code &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note that you should only have ONE frontend that defines a deferred transition&amp;lt;/b&amp;gt; (as this niche feature that was implemented with this assumption in mind...).&lt;br /&gt;
&lt;br /&gt;
The following example demonstrates this process:&lt;br /&gt;
&lt;br /&gt;
  BOOL transition_PS_requested=FALSE; // global&lt;br /&gt;
                                              //&lt;br /&gt;
  INT frontend_init()&lt;br /&gt;
  {&lt;br /&gt;
    // register for deferred transition&lt;br /&gt;
                                              //&lt;br /&gt;
    cm_register_deferred_transition(TR_STOP, wait_end_cycle);&lt;br /&gt;
    cm_register_deferred_transition(TR_PAUSE, wait_end_cycle);&lt;br /&gt;
    ...  &lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */ &lt;br /&gt;
  //-- Deferred transition callback&lt;br /&gt;
  BOOL wait_end_cycle(int transition, BOOL first)&lt;br /&gt;
  {&lt;br /&gt;
    if (first) {&lt;br /&gt;
      transition_PS_requested = TRUE;&lt;br /&gt;
      return FALSE;&lt;br /&gt;
    }&lt;br /&gt;
                        //&lt;br /&gt;
    if (end_of_mcs_cycle){&lt;br /&gt;
      transition_PS_requested = FALSE;&lt;br /&gt;
      end_of_mcs_cycle = FALSE;&lt;br /&gt;
      return TRUE;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
      return FALSE;&lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */&lt;br /&gt;
  INT read_mcs_event(char *pevent, INT offset)&lt;br /&gt;
  {  &lt;br /&gt;
      ...     // read out data at end of cycle&lt;br /&gt;
      ...&lt;br /&gt;
      end_of_mcs_cycle = TRUE; // end of cycle &lt;br /&gt;
                               //&lt;br /&gt;
      if (!transition_PS_requested)&lt;br /&gt;
         start_cycle(); // start a new cycle &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the example above, &lt;br /&gt;
&lt;br /&gt;
* The frontend code is registered for PAUSE and STOP using &#039;&#039;cm_register_deferred_transition()&#039;&#039;. The second argument &#039;&#039;wait_end_cycle&#039;&#039;  is the declaration of the callback function. &lt;br /&gt;
* The callback function &#039;&#039;wait_end_cycle&#039;&#039; will be called as soon as the transition is requested with the Boolean flag &#039;&#039;first&#039;&#039; set to TRUE.&lt;br /&gt;
* By setting &#039;&#039;transition_PS_requested&#039;&#039;  TRUE , the user will be provided with the acknowledgment of the transition request.&lt;br /&gt;
* By returning FALSE from the callback, the transition is prevented from occurring. &lt;br /&gt;
* As soon as the user condition is satisfied (&#039;&#039;end_of_mcs_cycle&#039;&#039; = TRUE), the return code in the  callback will be set to TRUE and the requested transition will be issued.&lt;br /&gt;
&lt;br /&gt;
While the transition is Deferred, the odb key  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; will contain the transition code, and the d [[mhttpd]] webserver main status page will indicate that a deferred transition is in progress.&lt;br /&gt;
&lt;br /&gt;
Once in deferred state, an [[odbedit]] override command can be issued to force the transition to happen,&lt;br /&gt;
&lt;br /&gt;
 &amp;gt;odbedit&lt;br /&gt;
 odb&amp;gt; stop now    (or &amp;quot;start now&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
or  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; can be set to 0. The transition will then take place on the next stop or start command.&lt;br /&gt;
&lt;br /&gt;
= Compilation using CMake =&lt;br /&gt;
&lt;br /&gt;
Here is a &amp;quot;minimal&amp;quot; CMakeLists.txt file that can be used to compile a user-written frontend (called &amp;quot;myfe&amp;quot; in this case) that uses the mfe framework.&lt;br /&gt;
&lt;br /&gt;
  cmake_minimum_required(VERSION 3.0)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  &lt;br /&gt;
  # Check for MIDASSYS environment variable&lt;br /&gt;
  if (NOT DEFINED ENV{MIDASSYS})&lt;br /&gt;
    message(SEND_ERROR &amp;quot;MIDASSYS environment variable not defined.&amp;quot;)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  set(CMAKE_CXX_STANDARD 11)&lt;br /&gt;
  set(MIDASSYS $ENV{MIDASSYS})&lt;br /&gt;
  &lt;br /&gt;
  if (${CMAKE_SYSTEM_NAME} MATCHES Linux)&lt;br /&gt;
    set(LIBS -lpthread -lutil -lrt)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  # Define the executable to be built, and the source code files&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  &lt;br /&gt;
  # Directories to search for &lt;br /&gt;
  target_include_directories(myfe.exe PRIVATE ${MIDASSYS}/include)&lt;br /&gt;
  &lt;br /&gt;
  # Libraries to link to&lt;br /&gt;
  target_link_libraries(myfe.exe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})&lt;br /&gt;
&lt;br /&gt;
One could then build the executable using the folllowing commands:&lt;br /&gt;
&lt;br /&gt;
  mkdir build&lt;br /&gt;
  cd build&lt;br /&gt;
  cmake ..&lt;br /&gt;
  make&lt;br /&gt;
&lt;br /&gt;
Some older operating systems may require the &amp;quot;cmake3&amp;quot; command to be used instead of &amp;quot;cmake&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3414</id>
		<title>Frontend user code</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3414"/>
		<updated>2024-01-05T18:55:31Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation]]&lt;br /&gt;
* [[Frontend Application]] &lt;br /&gt;
* [[Equipment List Parameters]]&lt;br /&gt;
* [[Slow Control System]]&lt;br /&gt;
* [[Equipment Flags]]&lt;br /&gt;
* [[Event Notification (Hot-Link)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This section describes the features of the user-written part of a &amp;quot;C-style&amp;quot; [[Frontend Operation#Frontend|Frontend]], referred to here as &#039;&#039;&#039;frontend.c&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
You can also write frontends using [[Frontend_user_code_(object_oriented_-_TMFE)|object-oriented C++ (TMFE)]] or [[Python]].&lt;br /&gt;
&lt;br /&gt;
The frontend system has evolved over the decades, and contains some legacy features that are not often used (e.g. interrupt handlers). For backwards-compatibility, these features are still present, but this does require a bit more boiler-plate code to be written for each frontend. We will first document every feature supported by the frontend system, then explain a slightly more user-friendly wrapper (mfed.cxx) that helps reduce the amount of boiler-plate needed for a new frontend.&lt;br /&gt;
&lt;br /&gt;
= Frontend Templates =&lt;br /&gt;
To make a user-written or custom frontend, users will usually modify one of the &#039;&#039;&#039;templates&#039;&#039;&#039; provided in the MIDAS package&lt;br /&gt;
under [[Environment Variables#MIDASSYS|$MIDASSYS]]/examples/ for their particular hardware and other requirements. Make sure to pick the closest template, e.g. if writing a [[Slow Control System|Slow Control]] frontend, pick a slow-control template. For each example frontend, a Makefile is also provided.&lt;br /&gt;
&lt;br /&gt;
A partial list of the templates provided is shown below:&lt;br /&gt;
{| style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;3&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Hardware &lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Filename&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Directory (MIDAS package)&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Purpose&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Language&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevmemodules.c&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevme.cxx&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c++/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|CAMAC &lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| Access to CAMAC modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|mtfe.c&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| &#039;&#039;&#039;Multithreaded&#039;&#039;&#039; using ring buffer&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Wiener CC-USB &lt;br /&gt;
|feccusb.cxx&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| CAMAC/USB demo &lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|RS485 bus &lt;br /&gt;
|mscb_fe.c&lt;br /&gt;
| $MIDASSYS/examples/slowcont/&lt;br /&gt;
| &#039;&#039;&#039;Slow control&#039;&#039;&#039; with [https://midas.psi.ch/mscb/ MSCB]&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|EPICS&lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/epics/&lt;br /&gt;
| &#039;&#039;&#039;Slow controls&#039;&#039;&#039;&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|Camac&lt;br /&gt;
|ebfe.c&lt;br /&gt;
| $MIDASSYS/examples/eventbuilder/&lt;br /&gt;
| &#039;&#039;&#039;Event Builder&#039;&#039;&#039; (with [[mevb]])&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|frontend.cxx&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| &#039;&#039;&#039;mfed&#039;&#039;&#039; (wrapper to simplify writing a frontend)&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The features of a typical frontend program are best explained by reference to examples of the user code &lt;br /&gt;
provided in the Midas Package. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Frontend code=&lt;br /&gt;
Note that there are several kinds of frontend used for different purposes, e.g.&lt;br /&gt;
* frontend to read VME or CAMAC hardware, using interrupt or polling (e.g. to read experimental data)&lt;br /&gt;
* multithreaded frontend where polling can be in a separate thread&lt;br /&gt;
* slow control frontend (can be multithreaded) see also [[Slow Control System]]&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Templates for many types of user frontend code are provided in the MIDAS packages (see [[#Frontend Templates]]). &lt;br /&gt;
&lt;br /&gt;
The following sections refer to these templates.&lt;br /&gt;
Most of the examples are taken from the [https://bitbucket.org/tmidas/midas/src/develop/examples/basic/largefe.cxx largefe.cxx example]. Documentation on the MIDAS library subroutines to access the ODB (some of which are used in the examples below) can be found in the [[ODB_Access_and_Use|ODB access page]].&lt;br /&gt;
&lt;br /&gt;
The user frontend code is then compiled and linked with the system part and the MIDAS library &lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Task]]).  Makefiles are provided with the templates that can be modified as needed.&lt;br /&gt;
&lt;br /&gt;
== Access to command line parameters ==&lt;br /&gt;
The function &#039;&#039;&#039;mfe_get_args&#039;&#039;&#039; gives access to the [[Frontend Application|Frontend]] command line parameters. &lt;br /&gt;
&lt;br /&gt;
This example shows how to use it in the frontend user code:&lt;br /&gt;
&lt;br /&gt;
  int argc;&lt;br /&gt;
  char **argv;&lt;br /&gt;
  mfe_get_args(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
  for (int i = 0; i &amp;lt; argc; i++) {&lt;br /&gt;
     // Use argv[i] &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
See [[Frontend_Application#Arguments|Frontend Application Arguments]] for the arguments that are handled automatically by the frontend system.&lt;br /&gt;
&lt;br /&gt;
==Include files==&lt;br /&gt;
The following example (from template frontend file &#039;&#039;$MIDASSYS/examples/basic/largefe.cxx&#039;&#039;) shows the standard include files needed for a frontend. The user may add any other include files as needed (e.g. those needed for VME access). &lt;br /&gt;
&lt;br /&gt;
Some legacy frontends may include the &#039;&#039;&#039;[[ODB#experim.h include file|experim.h]]&#039;&#039;&#039; file. This was an old way of accessing the ODB, but was not very user-friendly when the ODB structure needed to change.&lt;br /&gt;
&lt;br /&gt;
The example below shows a typical list of include files for a frontend:&lt;br /&gt;
 &lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
 #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;div id=&amp;quot;Frontend Name&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
==Global Declarations==&lt;br /&gt;
The following example (from template frontend file fevmemodules.c - see [[#Frontend Templates]]) shows the global declaration. The declarations are system wide.  Some may be changed to suit the user, but none should not be removed.&lt;br /&gt;
&lt;br /&gt;
; frontend_name  : This value can be modified to reflect the purpose of the code &lt;br /&gt;
; frontend_call_loop : If set to TRUE, the function frontend_loop() gets called very frequently. If FALSE, frontend_loop() does not run. The user can add suitable code to this routine if desired (e.g. to check for a condition).&lt;br /&gt;
; display_period : The time interval (defined in milliseconds) between the refresh of a frontend status display. The value of zero disables the display. If the frontend is started in the background with the display enabled, the stdout should be redirected to the null device to prevent the process from hanging.&lt;br /&gt;
; max_event_size : specifies the maximum size (in bytes) of the expected event.&lt;br /&gt;
; event_buffer_size : specifies the maximum size (in bytes) of the buffer to be allocated by the system.&lt;br /&gt;
; equipment_common_overwrite : whether the definitions in the EQUIPMENT struct override those in the /Equipment/largefe/Common section of the ODB. If FALSE, the values in the struct will be used the first time the program runs, then the ODB values will be used afterwards (e.g. allowing you to change the period of a periodic equipment via the ODB).&lt;br /&gt;
&lt;br /&gt;
See below for an example of global declarations from a frontend.&lt;br /&gt;
&lt;br /&gt;
 /*-- Globals -------------------------------------------------------*/&lt;br /&gt;
 /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
 const char *frontend_name = &amp;quot;largefe&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 /* The frontend file name, don&#039;t change it */&lt;br /&gt;
 const char *frontend_file_name = __FILE__;&lt;br /&gt;
 &lt;br /&gt;
 /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 &lt;br /&gt;
 /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
 INT display_period = 0;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size produced by this frontend */&lt;br /&gt;
 INT max_event_size = 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
 INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
 &lt;br /&gt;
 /* buffer size to hold events */&lt;br /&gt;
 INT event_buffer_size = 10 * 10000;&lt;br /&gt;
 &lt;br /&gt;
 /* whether the values in EQUIPMENT struct override ODB values */&lt;br /&gt;
 BOOL equipment_common_overwrite = FALSE;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==System Function Declarations==&lt;br /&gt;
&lt;br /&gt;
These lines declare the pre-defined system functions which should be present. &lt;br /&gt;
&lt;br /&gt;
 INT frontend_init();&lt;br /&gt;
 INT frontend_exit();&lt;br /&gt;
 INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
 INT end_of_run(INT run_number, char *error);&lt;br /&gt;
 INT pause_run(INT run_number, char *error);&lt;br /&gt;
 INT resume_run(INT run_number, char *error);&lt;br /&gt;
 INT frontend_loop();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Readout Function Declarations==&lt;br /&gt;
Following the previous group is a second group of declarations, which define the readout functions. These depend on the defined equipments, and run when the respective equipment is triggered. In this example, one equipment will be defined, so there is one declaration. The user functions will be described in detail in later sections. &lt;br /&gt;
&lt;br /&gt;
 INT read_large_event(char *pevent, INT off);&lt;br /&gt;
&lt;br /&gt;
If using an interrupt, callback function prototypes are also included&lt;br /&gt;
&lt;br /&gt;
 extern void interrupt_routine(void);&lt;br /&gt;
 void register_cnaf_callback(int debug);&lt;br /&gt;
&lt;br /&gt;
==Equipment List==&lt;br /&gt;
&lt;br /&gt;
This list of structs defines the behaviour of your frontend (e.g. periodic or polled equipment). See [[Equipment List Parameters|Equipment List]] for full documentation of the entries.&lt;br /&gt;
&lt;br /&gt;
Note that these are the default values for each equipment (used the very first time a frontend is run). If [[#Global declarations|equipment_common_overwrite]] is FALSE, then some of the values will subsequently be read from the ODB instead. If [[#Global declarations|equipment_common_overwrite]] is TRUE, then the ODB will be updated with the coded values each time the program runs.&lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
&lt;br /&gt;
   {&amp;quot;large&amp;quot;,                   /* equipment name */&lt;br /&gt;
     {3, 0,                    /* event ID, trigger mask */&lt;br /&gt;
      &amp;quot;SYSTEM&amp;quot;,                /* event buffer */&lt;br /&gt;
      EQ_PERIODIC | EQ_FRAGMENTED,     /* equipment type */&lt;br /&gt;
      0,                       /* event source */&lt;br /&gt;
      &amp;quot;MIDAS&amp;quot;,                 /* format */&lt;br /&gt;
      TRUE,                    /* enabled */&lt;br /&gt;
      RO_ALWAYS,               /* read when running and on transitions */&lt;br /&gt;
      2000,                    /* read every 2 sec */&lt;br /&gt;
      0,                       /* stop run after this event limit */&lt;br /&gt;
      0,                       /* number of sub events */&lt;br /&gt;
      0,                       /* log history */&lt;br /&gt;
      &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;},&lt;br /&gt;
      read_large_event,        /* readout routine */&lt;br /&gt;
      NULL, NULL,              /* keep null */&lt;br /&gt;
      NULL,                    /* init string */&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   {&amp;quot;&amp;quot;}&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
==Sequence of Operations in the frontend==&lt;br /&gt;
&lt;br /&gt;
The following table shows the sequence of operations of the  [[Frontend Operation#Frontend|Frontend]] System functions.  These functions must be implemented in the user&#039;s code (but may be as simple as just returning &amp;lt;code&amp;gt;SUCCESS&amp;lt;/code&amp;gt; if the function is not relevant for your use case). These functions are called by &#039;&#039;mfe.cxx&#039;&#039; at the appropriate time. The System Transition functions are associated with a particular [[Run States and Transitions|Run Transition]] as shown below:&lt;br /&gt;
&lt;br /&gt;
{|  style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Transition Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Associated [[Run States and Transitions|Transition]]&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Action&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_init()&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| Runs once after system initialization, before Equipment registration. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| begin_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_START&lt;br /&gt;
| Runs after system statistics reset at each begin-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| pause_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_PAUSE&lt;br /&gt;
| Runs at each pause-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| resume_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_RESUME&lt;br /&gt;
| Runs at each resume-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| end_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_STOP&lt;br /&gt;
| Runs at each end-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_exit()&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Runs once before any Slow Control Equipment exit &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Each defined Equipment has the option to force itself to run at individual transition times (see Equipment [[Equipment List Parameters#ReadOn|ReadOn Flag]]), so that its equipment function will be called on a certain transition (or combination of transitions). &lt;br /&gt;
&lt;br /&gt;
The system transition functions all run &#039;&#039;&#039;prior to&#039;&#039;&#039; the equipment functions. This gives the system the chance to take basic action on the transition request (e.g. enable/disable interrupt) before the equipment runs. All the transition routines run with a [[Run States and Transitions#Default Transition Sequency Numbers|Transition Sequence number]] of 500 (the default). This allows users to add additional functions in the frontend that will run before or after any of the transitions (such as a prestart() or a poststop() function). See [[Run States and Transitions#Run Transition Priority|Run Transition Priority]] for more information.&lt;br /&gt;
&lt;br /&gt;
==Function frontend_init ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
No parameters.&lt;br /&gt;
&lt;br /&gt;
This function runs &#039;&#039;&#039;once only&#039;&#039;&#039; at the application startup. Users may perform hardware checking, loading/setting of global variables, mapping of required ODB structures (see [[ODB#experim.h include file|experim.h include file]]), &lt;br /&gt;
[[Event Notification (Hot-Link)#How to set up a Hot-Link|setting up hot-links]] etc. in frontend_init(), e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT frontend_init()&lt;br /&gt;
 {&lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;Initializing...&amp;quot;, &amp;quot;yellow&amp;quot;);&lt;br /&gt;
   ....                  &lt;br /&gt;
   set_equipment_status(equipment[0].name, &amp;quot;OK&amp;quot;, &amp;quot;green&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Reporting Equipment Status===&lt;br /&gt;
If running with the webserver [[mhttpd]], a frontend can send an update to the [[Status Page]], to report on its progress, using the function set_equipment_status() (see above example). This is useful when hardware can take a long time to respond.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function begin_of_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being started&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called every time a run start transition occurs, i.e. at begin-of-run. It allows the updating of user parameters, and the loading/setup/clearing of hardware. At the exit of this function, the acquisition should be armed and ready to test the interrupt (if used), e.g. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT begin_of_run (INT runnumber, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   INT status;&lt;br /&gt;
   status = update_user_parameters(); // update parameters from ODB&lt;br /&gt;
         &amp;lt;br&amp;gt;&lt;br /&gt;
   // Set am to A24 non-privileged Data&lt;br /&gt;
   mvme_set_am(myvme, MVME_AM_A24_ND);&lt;br /&gt;
   // Set dmode to D32&lt;br /&gt;
   mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
                   &amp;lt;br&amp;gt;                            &lt;br /&gt;
   //-------- ADCs -------------------&lt;br /&gt;
   v792_Setup(myvme, VADC0_BASE, 2);&lt;br /&gt;
   v792_ThresholdWrite(myvme, VADC0_BASE,&lt;br /&gt;
       (WORD *)&amp;amp;(ts.v792.threshold1));&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
   csr = v792_CSR1Read(myvme, VADC0_BASE);&lt;br /&gt;
   printf(&amp;quot;Data Ready ADC0: 0x%x\n&amp;quot;, csr);&lt;br /&gt;
   ........&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
   // Reset Latch&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE, 0x1);&lt;br /&gt;
   // Clear pending interrupts&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+8, 0x0);&lt;br /&gt;
   // Enable interrupt&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Accessing variables from the ODB ===&lt;br /&gt;
The  [http://ladd00.triumf.ca/~daqweb/doc/midas/doc/html/group__odbfunctionc.html MIDAS library] contains a number of subroutines to access the ODB.  The ODB keys can easily be accessed from a frontend or other client individually, or by using a saved C structure, which is often used when a large number of keys are to be accessed. &lt;br /&gt;
&lt;br /&gt;
==== Using C structure ====&lt;br /&gt;
&amp;lt;div id=&amp;quot;get_user_parameters&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
The function get_user_parameters() relies on the ODB structure &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/Equipment/Trigger/settings&amp;lt;/span&amp;gt; being mapped in &lt;br /&gt;
[[#Function frontend_init|frontend_init()]] with db_create_record() to ensure that the required ODB structure is present. Then the contents of the ODB keys can be obtained from the C structure (defined in &lt;br /&gt;
[[#Include files|experim.h]]) with a db_get_record() call, as demonstrated in the following example:&lt;br /&gt;
&lt;br /&gt;
 INT get_user_parameters(void)&lt;br /&gt;
 {&lt;br /&gt;
  INT status,size;&lt;br /&gt;
  TRIGGER_SETTINGS ps;  // defined in experim.h&lt;br /&gt;
               &amp;lt;br&amp;gt;&lt;br /&gt;
  /* Get current  settings */&lt;br /&gt;
  size = sizeof(ps);&lt;br /&gt;
  status = db_get_record(hDB, hSet, &amp;amp;ps, &amp;amp;size, 0); // HNDLE hSet obtained in frontend_init()&lt;br /&gt;
  if (status != DB_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      cm_msg(MERROR, &amp;quot;get_exp_settings&amp;quot;, &amp;quot;cannot retrieve trigger settings record (size of ps=%d)&amp;quot;, size);&lt;br /&gt;
      return status;&lt;br /&gt;
    }&lt;br /&gt;
  // Access the required ODB keys in the /equipment/trigger/settings/ subtree from the C structure&lt;br /&gt;
  printf(&amp;quot;Trigger mode = %s \n&amp;quot;, ps.trig_mode); &lt;br /&gt;
  printf(&amp;quot;Trigger delay (ms)= %f \n&amp;quot;,ps.trigger_delay_time__ms_);&lt;br /&gt;
  ...&lt;br /&gt;
  set_trig_delay(ps.trigger_delay_time__ms_);&lt;br /&gt;
  return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
If a number of values in this structure are changed by the frontend, they can be written back into the ODB with one library call, i.e. db_set_record(..).&lt;br /&gt;
 &lt;br /&gt;
==== Accessing individual keys ====&lt;br /&gt;
If experim.h is not included, parameters can be accessed from the ODB using db_get_value() or db_get_data().&lt;br /&gt;
&lt;br /&gt;
   char hostname[20];&lt;br /&gt;
   char str[128];&lt;br /&gt;
   int size,status;&lt;br /&gt;
  /* Get the camp hostname from the mdarc area of odb  */&lt;br /&gt;
  size = sizeof(hostname); &lt;br /&gt;
  sprintf(str,&amp;quot;/Equipment/%s/mdarc/camp/camp hostname&amp;quot;,equipment[FIFO].name); &lt;br /&gt;
  status = db_get_value(hDB, 0, str, hostname, &amp;amp;size, TID_STRING, FALSE);&lt;br /&gt;
  if(status != DB_SUCCESS)&lt;br /&gt;
  {&lt;br /&gt;
    cm_msg(MERROR,&amp;quot;camp_update_params&amp;quot;,&amp;quot;cannot get Camp hostname at %s (%d)&amp;quot;,str,status);&lt;br /&gt;
    return FE_ERR_ODB;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Values can be written into the ODB using db_set_value() or db_set_data().&lt;br /&gt;
&lt;br /&gt;
Note that all the ODB access functions mentioned here can be found in the  [http://ladd00.triumf.ca/~daqweb/doc/midas/doc/html/group__odbfunctionc.html MIDAS library], and more examples can be found in the midas package e.g. under ../midas/examples/ .&lt;br /&gt;
&lt;br /&gt;
==Functions pause/resume_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being paused/resumed.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
These two functions are called upon &amp;quot;Pause&amp;quot; and &amp;quot;Resume&amp;quot; command respectively. Any code relevant to the upcoming run state can be included,e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT pause_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   disable_trigger();&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
                            //&lt;br /&gt;
 INT resume_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   enable_trigger();&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function end_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being ended.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called at every &amp;quot;stop run&amp;quot; transition. It provides the opportunity to disable the hardware, e.g.&lt;br /&gt;
&lt;br /&gt;
 INT end_of_run(INT run_number, char *error)&lt;br /&gt;
 {&lt;br /&gt;
   // Stop DAQ for seting up the parameters&lt;br /&gt;
   vf48_AcqStop(myvme, VF48_BASE);&lt;br /&gt;
                       //&lt;br /&gt;
   done = 0;&lt;br /&gt;
   stop_req = 0;&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   trig_level = 0;&lt;br /&gt;
   // Close run gate&lt;br /&gt;
   vmeio_AsyncWrite(myvme, VMEIO_BASE, 0x0);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function frontend_exit==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
The function runs When the frontend program is shut down. Can be used to release any locked resources like memory, communications ports etc. e.g.&lt;br /&gt;
&lt;br /&gt;
  function frontend_exit()&lt;br /&gt;
  {&lt;br /&gt;
     mvme_close(gVme);&lt;br /&gt;
     return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_loop==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
If the flag frontend_call_loop is set TRUE, this routine is called when the frontend is idle or once between every event. In the following example, it is being used to check for a timeout:&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 ...&lt;br /&gt;
                                 //&lt;br /&gt;
 INT frontend_loop()&lt;br /&gt;
 {&lt;br /&gt;
   char str[128];&lt;br /&gt;
                   //&lt;br /&gt;
   if (stop_req &amp;amp;&amp;amp; done==0) {&lt;br /&gt;
      db_set_value(hDB,0,&amp;quot;/logger/channels/0/Settings/Event limit&amp;quot;, &amp;amp;evlimit, sizeof(evlimit), 1, TID_DWORD);&lt;br /&gt;
      if (cm_transition(TR_STOP, 0, str, sizeof(str), ASYNC, FALSE) != CM_SUCCESS) {&lt;br /&gt;
         cm_msg(MERROR, &amp;quot;VF48 Timeout&amp;quot;, &amp;quot;cannot stop run: %s&amp;quot;, str);&lt;br /&gt;
      }&lt;br /&gt;
      inRun = 0;&lt;br /&gt;
      // Disable interrupt&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
      done = 1;&lt;br /&gt;
      cm_msg(MERROR, &amp;quot;VF48 Timeout&amp;quot;,&amp;quot;VF48 Stop requested&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Event Types and Triggers==&lt;br /&gt;
The frontend supports several different types of [[Frontend Operation#Frontend event triggers|event trigger]]. The event trigger type is specified by the &lt;br /&gt;
[[Equipment Flags|Equipment Flag]]  in the  [[Equipment List Parameters|Equipment Declaration]]. Common event types are &amp;quot;polled events&amp;quot; where the Equipment Flag is  [[Equipment Flags#EQ_POLLED|EQ_POLLED]], &amp;quot;interrupts events&amp;quot; where the Flag is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]], and &amp;quot;periodic events&amp;quot; where the Flag is [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]. The name of the associated readout routine is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type.&lt;br /&gt;
&lt;br /&gt;
Polled  and interrupt events (see [[#Event Types|Event Types]]) require several extra functions to handle the hardware that periodic events do not require. These are described below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function poll_event==&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* INT &#039;&#039;&#039;count&#039;&#039;&#039;&lt;br /&gt;
* BOOL &#039;&#039;&#039;test&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment.&lt;br /&gt;
&lt;br /&gt;
In this case, the [[Equipment List Parameters|Equipment declaration]] would have this form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Trigger&amp;quot;,            // equipment name&lt;br /&gt;
        {&lt;br /&gt;
          ...&lt;br /&gt;
          &amp;lt;b&amp;gt;EQ_POLLED&amp;lt;/b&amp;gt;,          // equipment type&lt;br /&gt;
          ...&lt;br /&gt;
          500,                // poll for 500ms &lt;br /&gt;
          ...&lt;br /&gt;
          &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
          read_trigger_event,    // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
         &lt;br /&gt;
The user must provide suitable code in the routine poll_event(), e.g.&lt;br /&gt;
&lt;br /&gt;
 INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
 {&lt;br /&gt;
    /* Polling routine for events. Returns TRUE if event  is available. If test equals TRUE, don&#039;t return. &lt;br /&gt;
       The test flag is used to time the polling &lt;br /&gt;
     */&lt;br /&gt;
     int i;&lt;br /&gt;
     int lam = 0;&lt;br /&gt;
                                 //&lt;br /&gt;
     for (i = 0; i &amp;lt; count; i++, lam++) {&lt;br /&gt;
       lam = vmeio_CsrRead(myvme, VMEIO_BASE);&lt;br /&gt;
       if (lam)&lt;br /&gt;
         if (!test)&lt;br /&gt;
           return lam;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function interrupt_configure==&lt;br /&gt;
* INT &#039;&#039;&#039;cmd&#039;&#039;&#039; &lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* PTYPE &#039;&#039;&#039;adr&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_INTERRUPT, an interrupt configuration routine called interrupt_configure() must be provided by the user.&lt;br /&gt;
The interrupt configuration routine has the following declaration: &lt;br /&gt;
&lt;br /&gt;
 /*-- Interrupt configuration --------------------------*/&lt;br /&gt;
 INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
 {&lt;br /&gt;
  int vec = 0;&lt;br /&gt;
  switch (cmd) &lt;br /&gt;
  {&lt;br /&gt;
    case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x1);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
      mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
      mvme_interrupt_attach(myvme, INT_LEVEL, INT_VECTOR, &lt;br /&gt;
                (void *)adr, &amp;amp;myinfo);&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+0x10, INT_VECTOR);&lt;br /&gt;
      vec = mvme_read_value(myvme, VLAM_BASE+0x10);&lt;br /&gt;
      printf(&amp;quot;Interrupt Attached to 0x%x for vector:0x%x\n&amp;quot;,&lt;br /&gt;
                     adr, vec&amp;amp;0xFF);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DETACH:&lt;br /&gt;
      printf(&amp;quot;Interrupt Detach\n&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Under the four commands listed above, the user must implement the hardware operation needed to perform the requested action. In the Midas drivers directory examples can be found of such an interrupt code for CAMAC. See source code such as hyt1331.c,ces8210.c &lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user in the frontend.&lt;br /&gt;
&lt;br /&gt;
==Event Readout routine==&lt;br /&gt;
In the case of [[#Polled and Interrupt Events|POLLED or INTERRUPT events]], the event readout routine is called an [[#Polled or Interrupt readout routine|Interrupt readout routine]].&lt;br /&gt;
&lt;br /&gt;
An event readout routine (called when an event occurs) is usually of the form&lt;br /&gt;
&lt;br /&gt;
 INT function_name ( char *pevent ... )&lt;br /&gt;
 {&lt;br /&gt;
   INT event_size;&lt;br /&gt;
   ........  // read data from hardware&lt;br /&gt;
   ........  // pack into banks depending on format&lt;br /&gt;
   ........&lt;br /&gt;
   return (event_size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
where the first argument of the readout function (pevent)  provides the pointer to the newly constructed event, and points to the first valid location for storing the data.&lt;br /&gt;
;NOTE &lt;br /&gt;
* The return value is the event size, and must be the number of bytes collected in this function.&lt;br /&gt;
* The event serial number will be incremented by one for every call to the readout routine, as long as the returned size is non-zero.&lt;br /&gt;
* If the returned value is set to zero, the event will be dismissed and the serial number to that event will be decremented by one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===General readout function===&lt;br /&gt;
If the Equipment type is EQ_INTERRUPT or EQ_POLLED, see also [[#Polled or Interrupt readout routine|Polled or Interrupt readout routine]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the case of a &#039;&#039;&#039;periodic&#039;&#039;&#039; event, the Equipment declaration may have this form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Scaler&amp;quot;,           // equipment name&lt;br /&gt;
         {    &lt;br /&gt;
            ...&lt;br /&gt;
            EQ_PERIODIC     // equipment type&lt;br /&gt;
            0,              // interrupt source (ignored) &lt;br /&gt;
            &amp;quot;MIDAS&amp;quot;,        // event format&lt;br /&gt;
            ...&lt;br /&gt;
            10000,          // period (read every 10s)&lt;br /&gt;
            ...&lt;br /&gt;
            &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
       read_scaler_event,   // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
A readout event will often need to send out data. This is done using one of the supported [[Event Structure|event structures]] usually [[Event Structure#MIDAS Format Event|&amp;quot;MIDAS&amp;quot; format]] data banks.  The bank format (&amp;quot;MIDAS&amp;quot; in the example above) is declared in the [[Equipment List Parameters]]   [[Equipment List Parameters#Format|Format]] field.&lt;br /&gt;
&lt;br /&gt;
An example of a scaler readout routine read_scaler_event() where the data is read out into [[MIDAS Event Construction|MIDAS data banks]] is shown below. &lt;br /&gt;
&lt;br /&gt;
 INT read_scaler_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
    DWORD *pdata, a;&lt;br /&gt;
                               //&lt;br /&gt;
    /* init bank structure; */&lt;br /&gt;
    bk_init(pevent);&lt;br /&gt;
                               //&lt;br /&gt;
    /* create SCLR bank */&lt;br /&gt;
    bk_create(pevent, &amp;quot;SCLR&amp;quot;, TID_DWORD, &amp;amp;pdata);&lt;br /&gt;
                               //&lt;br /&gt;
    /* read scaler bank (CAMAC) */&lt;br /&gt;
    for (a = 0; a &amp;lt; N_SCLR; a++)&lt;br /&gt;
       cam24i(CRATE, SLOT_SCLR, a, 0, pdata++);&lt;br /&gt;
                               //    &lt;br /&gt;
    /* close SCLR bank */&lt;br /&gt;
    bk_close(pevent, pdata);&lt;br /&gt;
                               //&lt;br /&gt;
    /* return event size in bytes */&lt;br /&gt;
    return bk_size(pevent);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Some other examples of event readout routines in this document are&lt;br /&gt;
* [[Event Structure#FIXED Event Construction|FIXED Format event construction]]&lt;br /&gt;
* [[#Polled or Interrupt readout routine|Polled or Interrupt readout routines]]&lt;br /&gt;
* [[Super Event]] construction&lt;br /&gt;
* [[Slow Control System|Slow Control]]&lt;br /&gt;
&lt;br /&gt;
Many other examples of readout routines can be found in the [[#Frontend Templates|frontend templates]] in the MIDAS package.&lt;br /&gt;
&lt;br /&gt;
===Polled or Interrupt readout routine===&lt;br /&gt;
In the case of a [[#Polled and Interrupt Events|Polled or Interrupt event]], the content of the memory location pointed to by &#039;&#039;&#039;pevent&#039;&#039;&#039; (see [[#Event Readout routine|Event Readout routine]]) prior to its use in the readout function, contains the interrupt source bitwise register. This feature can be exploited in order to identify which hardware module has triggered the readout when multiple interrupts have been assigned to the same readout function.&lt;br /&gt;
&lt;br /&gt;
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism.&lt;br /&gt;
The  [[Equipment List Parameters|Equipment declaration]] is of the form: &lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
               //&lt;br /&gt;
    {&amp;quot;Trigger&amp;quot;,  /* equipment name */&lt;br /&gt;
       ...&lt;br /&gt;
 #ifdef USE_INT&lt;br /&gt;
       EQ_INTERRUPT, /* equipment type */&lt;br /&gt;
 #else&lt;br /&gt;
       EQ_POLLED,    /* equipment type */&lt;br /&gt;
 #endif&lt;br /&gt;
   /* interrupt source: crate 0, all stations */&lt;br /&gt;
       LAM_SOURCE(0, 0x0),&lt;br /&gt;
       ....&lt;br /&gt;
       &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,&lt;br /&gt;
     },&lt;br /&gt;
     read_trigger_event, /* readout routine */&lt;br /&gt;
     NULL, NULL,&lt;br /&gt;
     trigger_bank_list,&lt;br /&gt;
 }&lt;br /&gt;
  &lt;br /&gt;
Note that the [[Macros#CAMAC Macros|LAM_SOURCE macro]] simply codes the parameters into a bitwise register.&lt;br /&gt;
&lt;br /&gt;
The readout routine would contains code such as&lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   DWORD  *pdata;&lt;br /&gt;
 #endif&lt;br /&gt;
                        //&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   /* read ADC0 data */&lt;br /&gt;
   v792_EvtCntRead(myvme, VADC0_BASE, &amp;amp;evtcnt);&lt;br /&gt;
   ........&lt;br /&gt;
   /* Read Event */&lt;br /&gt;
   v792_EventRead(myvme, VADC0_BASE, pdata, &amp;amp;nentry);&lt;br /&gt;
   ........&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
 #endif&lt;br /&gt;
                       //&lt;br /&gt;
   ........&lt;br /&gt;
   return (size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The data will be packed into banks as described for the [[General readout function|general readout function]] above.&lt;br /&gt;
The examples fevmemodules.c (VME) and frontend.c (CAMAC) (see [[#Frontend Templates]] contain a complete example of read_trigger_event().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Manual Trigger==&lt;br /&gt;
Another type of [[Frontend Operation#Frontend event trigger|frontend event trigger]] supported is the &amp;quot;manual trigger&amp;quot;,  where the Equipment Flag is [[Equipment Flags#EQ_MANUAL_TRIGGER|EQ_MANUAL_TRIGGER]]. This flag means that an event can be triggered through the URL &amp;lt;code&amp;gt;?cmd=Trigger/&amp;lt;equipment_name&amp;gt;&amp;lt;/code&amp;gt; (e.g. &amp;lt;code&amp;gt;http://my.host:8080/?cmd=Trigger/MyEquipmentName&amp;lt;/code&amp;gt;). If you add this URL to the [[/Alias ODB tree|/Alias]] part of the ODB, then a link will appear on midas webpages that can be clicked to trigger an event.&lt;br /&gt;
&lt;br /&gt;
In some cases, the same readout code may be used for two types of event: a manual trigger and (say) a poll event. It is possible to determine whether the readout of an event was triggered by a manual trigger or a regular trigger by adding a call to the  [[MIDAS Event Header Macros|event header Macro]] DATA_SIZE in the readout routine:&lt;br /&gt;
&lt;br /&gt;
  flag = DATA_SIZE(pevent);&lt;br /&gt;
&lt;br /&gt;
If the result is&lt;br /&gt;
  *  flag = 0 normal call&lt;br /&gt;
  *  flag = 1 manual trigger&lt;br /&gt;
&lt;br /&gt;
It is also possible for a backend MIDAS client (such as an analyzer or custom data archiver) to trigger a manual trigger event. The client controls when an event is sent by means of&lt;br /&gt;
a  function that requests an event by triggering the event sending mechanism with a RPC call.&lt;br /&gt;
&lt;br /&gt;
With a frontend Equipment declaration of a manually triggered event of the form:&lt;br /&gt;
    &lt;br /&gt;
    { &amp;quot;Histo&amp;quot;,             /* equipment name */&lt;br /&gt;
       2, 0,                 /* event ID, trigger mask */&lt;br /&gt;
       &amp;quot;SYSTEM&amp;quot;,             /* event buffer */&lt;br /&gt;
       EQ_MANUAL_TRIG,     /* equipment type */&lt;br /&gt;
       0,    &lt;br /&gt;
       .......&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
the code fragment to manually trigger this event is:&lt;br /&gt;
&lt;br /&gt;
  int main(unsigned int argc,char **argv)&lt;br /&gt;
  {&lt;br /&gt;
      .......&lt;br /&gt;
      bm_request_event(hBufEvent, 2, TRIGGER_ALL, GET_ALL, &amp;amp;request_id, process_event_TD);&lt;br /&gt;
      .......&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
When it is time to save the data during the run, the function below is called:&lt;br /&gt;
&lt;br /&gt;
 BOOL trigger_histo_event(void)&lt;br /&gt;
 {&lt;br /&gt;
   HNDLE hconn;&lt;br /&gt;
   BOOL event_triggered;&lt;br /&gt;
                            //&lt;br /&gt;
   event_triggered = FALSE;&lt;br /&gt;
   ...................&lt;br /&gt;
   if (run_state == STATE_RUNNING) {   // Check the frontend client exists&lt;br /&gt;
        if( cm_exist(ClientName,TRUE))  {    &lt;br /&gt;
            status = cm_connect_client (ClientName, &amp;amp;hconn);&lt;br /&gt;
            if(status != RPC_SUCCESS)&lt;br /&gt;
                cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Cannot connect to frontend \&amp;quot;%s\&amp;quot; (%d)&amp;quot;,&lt;br /&gt;
                       ClientName,status);&lt;br /&gt;
            else  {     // successfully connected to frontend client&lt;br /&gt;
                status = rpc_client_call(hconn, RPC_MANUAL_TRIG, 2); // trigger a histo event&lt;br /&gt;
                if (status != CM_SUCCESS)&lt;br /&gt;
                    cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error triggering event from frontend (%d)&amp;quot;,status);&lt;br /&gt;
                else {  // successfully triggered event&lt;br /&gt;
                    event_triggered=TRUE;&lt;br /&gt;
                    status =cm_disconnect_client(hconn, FALSE);&lt;br /&gt;
                    if (status != CM_SUCCESS)&lt;br /&gt;
                        cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error disconnecting client (%d)&amp;quot;,status);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Frontend client %s not running (%d)&amp;quot;,&lt;br /&gt;
                   ClientName,status);&lt;br /&gt;
    } &lt;br /&gt;
    return(event_triggered);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Deferred Transition==&lt;br /&gt;
This option permits the user to postpone any transition issued by any requester&lt;br /&gt;
until some condition is satisfied.&lt;br /&gt;
For example:&lt;br /&gt;
* It may not be advisable to pause or stop a run until some hardware has turned off a particular valve. &lt;br /&gt;
* The start of the acquisition system should be postponed until the beam rate has been stable for a given period of time.&lt;br /&gt;
* While active, a particular acquisition system should not be interrupted until the &amp;quot;cycle&amp;quot; is completed.&lt;br /&gt;
&lt;br /&gt;
In these examples, any application having access to the state of the hardware can register to be a &amp;quot;transition Deferred&amp;quot; client. The MIDAS system will then catch any transition request and postpone the trigger of such a transition until the condition  is satisfied.&lt;br /&gt;
&lt;br /&gt;
The Deferred transition requires 3 steps for setup&lt;br /&gt;
# Register for the deferred transition &lt;br /&gt;
# Provide a callback function to serve the deferred transition &lt;br /&gt;
# Implement the condition code &lt;br /&gt;
&lt;br /&gt;
The following example demonstrates this process:&lt;br /&gt;
&lt;br /&gt;
  BOOL transition_PS_requested=FALSE; // global&lt;br /&gt;
                                              //&lt;br /&gt;
  INT frontend_init()&lt;br /&gt;
  {&lt;br /&gt;
    // register for deferred transition&lt;br /&gt;
                                              //&lt;br /&gt;
    cm_register_deferred_transition(TR_STOP, wait_end_cycle);&lt;br /&gt;
    cm_register_deferred_transition(TR_PAUSE, wait_end_cycle);&lt;br /&gt;
    ...  &lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */ &lt;br /&gt;
  //-- Deferred transition callback&lt;br /&gt;
  BOOL wait_end_cycle(int transition, BOOL first)&lt;br /&gt;
  {&lt;br /&gt;
    if (first) {&lt;br /&gt;
      transition_PS_requested = TRUE;&lt;br /&gt;
      return FALSE;&lt;br /&gt;
    }&lt;br /&gt;
                        //&lt;br /&gt;
    if (end_of_mcs_cycle){&lt;br /&gt;
      transition_PS_requested = FALSE;&lt;br /&gt;
      end_of_mcs_cycle = FALSE;&lt;br /&gt;
      return TRUE;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
      return FALSE;&lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */&lt;br /&gt;
  INT read_mcs_event(char *pevent, INT offset)&lt;br /&gt;
  {  &lt;br /&gt;
      ...     // read out data at end of cycle&lt;br /&gt;
      ...&lt;br /&gt;
      end_of_mcs_cycle = TRUE; // end of cycle &lt;br /&gt;
                               //&lt;br /&gt;
      if (!transition_PS_requested)&lt;br /&gt;
         start_cycle(); // start a new cycle &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the example above, &lt;br /&gt;
&lt;br /&gt;
* The frontend code is registered for PAUSE and STOP using &#039;&#039;cm_register_deferred_transition()&#039;&#039;. The second argument &#039;&#039;wait_end_cycle&#039;&#039;  is the declaration of the callback function. &lt;br /&gt;
* The callback function &#039;&#039;wait_end_cycle&#039;&#039; will be called as soon as the transition is requested with the Boolean flag &#039;&#039;first&#039;&#039; set to TRUE.&lt;br /&gt;
* By setting &#039;&#039;transition_PS_requested&#039;&#039;  TRUE , the user will be provided with the acknowledgment of the transition request.&lt;br /&gt;
* By returning FALSE from the callback, the transition is prevented from occurring. &lt;br /&gt;
* As soon as the user condition is satisfied (&#039;&#039;end_of_mcs_cycle&#039;&#039; = TRUE), the return code in the  callback will be set to TRUE and the requested transition will be issued.&lt;br /&gt;
&lt;br /&gt;
While the transition is Deferred, the odb key  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; will contain the transition code, and the d [[mhttpd]] webserver main status page will indicate that a deferred transition is in progress.&lt;br /&gt;
&lt;br /&gt;
Once in deferred state, an [[odbedit]] override command can be issued to force the transition to happen,&lt;br /&gt;
&lt;br /&gt;
 &amp;gt;odbedit&lt;br /&gt;
 odb&amp;gt; stop now    (or &amp;quot;start now&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
or  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; can be set to 0. The transition will then take place on the next stop or start command.&lt;br /&gt;
&lt;br /&gt;
= Compilation using CMake =&lt;br /&gt;
&lt;br /&gt;
Here is a &amp;quot;minimal&amp;quot; CMakeLists.txt file that can be used to compile a user-written frontend (called &amp;quot;myfe&amp;quot; in this case) that uses the mfe framework.&lt;br /&gt;
&lt;br /&gt;
  cmake_minimum_required(VERSION 3.0)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  &lt;br /&gt;
  # Check for MIDASSYS environment variable&lt;br /&gt;
  if (NOT DEFINED ENV{MIDASSYS})&lt;br /&gt;
    message(SEND_ERROR &amp;quot;MIDASSYS environment variable not defined.&amp;quot;)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  set(CMAKE_CXX_STANDARD 11)&lt;br /&gt;
  set(MIDASSYS $ENV{MIDASSYS})&lt;br /&gt;
  &lt;br /&gt;
  if (${CMAKE_SYSTEM_NAME} MATCHES Linux)&lt;br /&gt;
    set(LIBS -lpthread -lutil -lrt)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  # Define the executable to be built, and the source code files&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  &lt;br /&gt;
  # Directories to search for &lt;br /&gt;
  target_include_directories(myfe.exe PRIVATE ${MIDASSYS}/include)&lt;br /&gt;
  &lt;br /&gt;
  # Libraries to link to&lt;br /&gt;
  target_link_libraries(myfe.exe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})&lt;br /&gt;
&lt;br /&gt;
One could then build the executable using the folllowing commands:&lt;br /&gt;
&lt;br /&gt;
  mkdir build&lt;br /&gt;
  cd build&lt;br /&gt;
  cmake ..&lt;br /&gt;
  make&lt;br /&gt;
&lt;br /&gt;
Some older operating systems may require the &amp;quot;cmake3&amp;quot; command to be used instead of &amp;quot;cmake&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3413</id>
		<title>Frontend user code</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3413"/>
		<updated>2024-01-05T17:56:20Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation]]&lt;br /&gt;
* [[Frontend Application]] &lt;br /&gt;
* [[Equipment List Parameters]]&lt;br /&gt;
* [[Slow Control System]]&lt;br /&gt;
* [[Equipment Flags]]&lt;br /&gt;
* [[Event Notification (Hot-Link)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This section describes the features of the user-written part of a &amp;quot;C-style&amp;quot; [[Frontend Operation#Frontend|Frontend]], referred to here as &#039;&#039;&#039;frontend.c&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
You can also write frontends using [[Frontend_user_code_(object_oriented_-_TMFE)|object-oriented C++ (TMFE)]] or [[Python]].&lt;br /&gt;
&lt;br /&gt;
The frontend system has evolved over the decades, and contains some legacy features that are not often used (e.g. interrupt handlers). For backwards-compatibility, these features are still present, but this does require a bit more boiler-plate code to be written for each frontend. We will first document every feature supported by the frontend system, then explain a slightly more user-friendly wrapper (mfed.cxx) that helps reduce the amount of boiler-plate needed for a new frontend.&lt;br /&gt;
&lt;br /&gt;
= Frontend Templates =&lt;br /&gt;
To make a user-written or custom frontend, users will usually modify one of the &#039;&#039;&#039;templates&#039;&#039;&#039; provided in the MIDAS package&lt;br /&gt;
under [[Environment Variables#MIDASSYS|$MIDASSYS]]/examples/ for their particular hardware and other requirements. Make sure to pick the closest template, e.g. if writing a [[Slow Control System|Slow Control]] frontend, pick a slow-control template. For each example frontend, a Makefile is also provided.&lt;br /&gt;
&lt;br /&gt;
A partial list of the templates provided is shown below:&lt;br /&gt;
{| style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;3&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Hardware &lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Filename&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Directory (MIDAS package)&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Purpose&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Language&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevmemodules.c&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevme.cxx&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c++/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|CAMAC &lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| Access to CAMAC modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|mtfe.c&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| &#039;&#039;&#039;Multithreaded&#039;&#039;&#039; using ring buffer&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Wiener CC-USB &lt;br /&gt;
|feccusb.cxx&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| CAMAC/USB demo &lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|RS485 bus &lt;br /&gt;
|mscb_fe.c&lt;br /&gt;
| $MIDASSYS/examples/slowcont/&lt;br /&gt;
| &#039;&#039;&#039;Slow control&#039;&#039;&#039; with [https://midas.psi.ch/mscb/ MSCB]&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|EPICS&lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/epics/&lt;br /&gt;
| &#039;&#039;&#039;Slow controls&#039;&#039;&#039;&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|Camac&lt;br /&gt;
|ebfe.c&lt;br /&gt;
| $MIDASSYS/examples/eventbuilder/&lt;br /&gt;
| &#039;&#039;&#039;Event Builder&#039;&#039;&#039; (with [[mevb]])&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|frontend.cxx&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| &#039;&#039;&#039;mfed&#039;&#039;&#039; (wrapper to simplify writing a frontend)&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The features of a typical frontend program are best explained by reference to examples of the user code &lt;br /&gt;
provided in the Midas Package. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Frontend code=&lt;br /&gt;
Note that there are several kinds of frontend used for different purposes, e.g.&lt;br /&gt;
* frontend to read VME or CAMAC hardware, using interrupt or polling (e.g. to read experimental data)&lt;br /&gt;
* multithreaded frontend where polling can be in a separate thread&lt;br /&gt;
* slow control frontend (can be multithreaded) see also [[Slow Control System]]&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Templates for many types of user frontend code are provided in the MIDAS packages (see [[#Frontend Templates]]). &lt;br /&gt;
&lt;br /&gt;
The following sections refer to these templates.&lt;br /&gt;
Most of the examples are taken from the [https://bitbucket.org/tmidas/midas/src/develop/examples/basic/largefe.cxx largefe.cxx example]. Documentation on the MIDAS library subroutines to access the ODB (some of which are used in the examples below) can be found in the [[ODB_Access_and_Use|ODB access page]].&lt;br /&gt;
&lt;br /&gt;
The user frontend code is then compiled and linked with the system part and the MIDAS library &lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Task]]).  Makefiles are provided with the templates that can be modified as needed.&lt;br /&gt;
&lt;br /&gt;
== Access to command line parameters ==&lt;br /&gt;
The function &#039;&#039;&#039;mfe_get_args&#039;&#039;&#039; gives access to the [[Frontend Application|Frontend]] command line parameters. &lt;br /&gt;
&lt;br /&gt;
This example shows how to use it in the frontend user code:&lt;br /&gt;
&lt;br /&gt;
  int argc;&lt;br /&gt;
  char **argv;&lt;br /&gt;
  mfe_get_args(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
  for (int i = 0; i &amp;lt; argc; i++) {&lt;br /&gt;
     // Use argv[i] &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
See [[Frontend_Application#Arguments|Frontend Application Arguments]] for the arguments that are handled automatically by the frontend system.&lt;br /&gt;
&lt;br /&gt;
==Include files==&lt;br /&gt;
The following example (from template frontend file &#039;&#039;$MIDASSYS/examples/basic/largefe.cxx&#039;&#039;) shows the standard include files needed for a frontend. The user may add any other include files as needed (e.g. those needed for VME access). &lt;br /&gt;
&lt;br /&gt;
Some legacy frontends may include the &#039;&#039;&#039;[[ODB#experim.h include file|experim.h]]&#039;&#039;&#039; file. This was an old way of accessing the ODB, but was not very user-friendly when the ODB structure needed to change.&lt;br /&gt;
&lt;br /&gt;
The example below shows a typical list of include files for a frontend:&lt;br /&gt;
 &lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
 #include &amp;quot;midas.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;mfe.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;div id=&amp;quot;Frontend Name&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
==Global declarations==&lt;br /&gt;
The following example (from template frontend file fevmemodules.c - see [[#Frontend Templates]]) shows the global declaration. The declarations are system wide.  Some may be changed to suit the user, but none should not be removed.&lt;br /&gt;
&lt;br /&gt;
; frontend_name  : This value can be modified to reflect the purpose of the code &lt;br /&gt;
; frontend_call_loop : If set to TRUE, the function frontend_loop() runs after every equipment loop. If FALSE, frontend_loop() does not run. The user can add suitable code to this routine if desired (e.g. to check for a condition).&lt;br /&gt;
; display_period : The time interval (defined in milliseconds) between the refresh of a frontend status display. The value of zero disables the display. If the frontend is started in the background with the display enabled, the stdout should be redirected to the null device to prevent the process from hanging.&lt;br /&gt;
; max_event_size : specifies the maximum size (in bytes) of the expected event.&lt;br /&gt;
; event_buffer_size : specifies the maximum size (in bytes) of the buffer to be allocated by the system.&lt;br /&gt;
&lt;br /&gt;
See below for an example of global declarations from a frontend.&lt;br /&gt;
&lt;br /&gt;
 /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
 char *frontend_name = &amp;quot;fevmemodules&amp;quot;;&lt;br /&gt;
 /* The frontend file name, don&#039;t change it */&lt;br /&gt;
 char *frontend_file_name = __FILE__;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
 BOOL frontend_call_loop = FALSE;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
 INT display_period = 000;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* maximum event size produced by this frontend */&lt;br /&gt;
 INT max_event_size = 200000;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
 INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* buffer size to hold events */&lt;br /&gt;
 INT event_buffer_size = 10 * 100000;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Global User Declarations==&lt;br /&gt;
&lt;br /&gt;
After the global declarations, the user may add his or her own declarations.&lt;br /&gt;
The following example  (from template frontend file fevmemodules.c - see [[#Frontend Templates]])  defines various VME hardware parameters. &lt;br /&gt;
An example of hardware declarations in a frontend are shown below:&lt;br /&gt;
&lt;br /&gt;
 /* Hardware */&lt;br /&gt;
 MVME_INTERFACE *myvme;&lt;br /&gt;
                      //&lt;br /&gt;
 /* VME base addresses */&lt;br /&gt;
 DWORD VMEIO_BASE = 0x780000;&lt;br /&gt;
 DWORD VTDC0_BASE = 0xF10000;&lt;br /&gt;
 .......&lt;br /&gt;
 /* Globals */&lt;br /&gt;
 #define  N_ADC 100&lt;br /&gt;
 #define  N_TDC 100&lt;br /&gt;
 #define  N_PTS 5000&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==System Function Prototypes==&lt;br /&gt;
&lt;br /&gt;
These prototypes declare the pre-defined system functions which should be present. &lt;br /&gt;
 INT frontend_init();&lt;br /&gt;
 INT frontend_exit();&lt;br /&gt;
 INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
 INT end_of_run(INT run_number, char *error);&lt;br /&gt;
 INT pause_run(INT run_number, char *error);&lt;br /&gt;
 INT resume_run(INT run_number, char *error);&lt;br /&gt;
 INT frontend_loop();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Readout Function Prototypes==&lt;br /&gt;
Following the previous group is a second group of prototypes, which define the readout functions. These depend on the defined equipments, and run when the respective equipment is triggered. In this example, two equipments will be defined, so there are two prototypes. The user functions will be described in detail in the following sections. &lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off);&lt;br /&gt;
 INT read_scaler_event(char *pevent, INT off);&lt;br /&gt;
&lt;br /&gt;
If using an interrupt, callback function prototypes are also included&lt;br /&gt;
&lt;br /&gt;
 extern void interrupt_routine(void);&lt;br /&gt;
 void register_cnaf_callback(int debug);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Bank Definition and Equipment List==&lt;br /&gt;
&lt;br /&gt;
See [[Equipment List Parameters#Bank Definition|Bank Definitions]] and [[Equipment List Parameters|Equipment List]] for detailed information.&lt;br /&gt;
&lt;br /&gt;
However, this may be easier to follow if first the sequence of operations, and examples of the the different types of events (polled/interrupt or periodic etc.) and the functions they require in the frontend are introduced.&lt;br /&gt;
&lt;br /&gt;
==Sequence of Operations in the frontend==&lt;br /&gt;
&lt;br /&gt;
The following table shows the sequence of operations of the  [[Frontend Operation#Frontend|Frontend]] System functions.  These subroutines must be present in &#039;&#039;frontend.c&#039;&#039;,&lt;br /&gt;
but the contents are coded by the user. These functions are called by &#039;&#039;mfe.c&#039;&#039; at the appropriate time. The System Transition functions are associated with a particular [[Run States and Transitions|Run Transition]] as shown below:&lt;br /&gt;
&lt;br /&gt;
{|  style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Transition Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Associated [[Run States and Transitions|Transition]]&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Action&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_init()&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| Runs once after system initialization, before Equipment registration. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| begin_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_START&lt;br /&gt;
| Runs after system statistics reset at each begin-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| pause_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_PAUSE&lt;br /&gt;
| Runs at each pause-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| resume_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_RESUME&lt;br /&gt;
| Runs at each resume-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| end_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_STOP&lt;br /&gt;
| Runs at each end-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_exit()&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Runs once before any Slow Control Equipment exit &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Each defined Equipment has the option to force itself to run at individual transition times (see Equipment [[Equipment List Parameters#ReadOn|ReadOn Flag]]), so that its equipment function will be called on a certain transition (or combination of transitions). &lt;br /&gt;
&lt;br /&gt;
The system transition functions all run &#039;&#039;&#039;prior to&#039;&#039;&#039; the equipment functions. This gives the system the chance to take basic action on the transition request (e.g. enable/disable interrupt) before the equipment runs. All the transition routines run with a [[Run States and Transitions#Default Transition Sequency Numbers|Transition Sequence number]] of 500 (the default). This allows users to add additional functions in the frontend that will run before or after any of the transitions (such as a prestart() or a poststop() function). See [[Run States and Transitions#Run Transition Priority|Run Transition Priority]] for more information.&lt;br /&gt;
&lt;br /&gt;
==Function frontend_init ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
No parameters.&lt;br /&gt;
&lt;br /&gt;
This function runs &#039;&#039;&#039;once only&#039;&#039;&#039; at the application startup. Users may perform hardware checking, loading/setting of global variables, mapping of required ODB structures (see [[ODB#experim.h include file|experim.h include file]]), &lt;br /&gt;
[[Event Notification (Hot-Link)#How to set up a Hot-Link|setting up hot-links]] etc. in frontend_init(), e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT frontend_init()&lt;br /&gt;
 {&lt;br /&gt;
   set_equipment_status(equipment[idx].name, &amp;quot;Initializing...&amp;quot;, &amp;quot;yellow&amp;quot;);&lt;br /&gt;
   ....&lt;br /&gt;
         &amp;lt;br&amp;gt;&lt;br /&gt;
 /* Book Setting space */&lt;br /&gt;
   TRIGGER_SETTINGS_STR(trigger_settings_str);&lt;br /&gt;
         &amp;lt;br&amp;gt; &lt;br /&gt;
   /* Map /equipment/Trigger/settings (structure defined in experim.h) */&lt;br /&gt;
   sprintf(set_str, &amp;quot;/Equipment/Trigger/Settings&amp;quot;);&lt;br /&gt;
   status = db_create_record(hDB, 0, set_str, strcomb(trigger_settings_str));&lt;br /&gt;
   status = db_find_key (hDB, 0, set_str, &amp;amp;hSet);&lt;br /&gt;
   if (status != DB_SUCCESS)&lt;br /&gt;
     cm_msg(MINFO,&amp;quot;FE&amp;quot;,&amp;quot;Key %s not found&amp;quot;, set_str);&lt;br /&gt;
   return status;&lt;br /&gt;
         &amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;lt;br&amp;gt;&lt;br /&gt;
   // Open VME interface&lt;br /&gt;
   status = mvme_open(&amp;amp;myvme, 0);&lt;br /&gt;
           &amp;lt;br&amp;gt;                                 &lt;br /&gt;
   // Set am to A24 non-privileged Data&lt;br /&gt;
   mvme_set_am(myvme, MVME_AM_A24_ND);&lt;br /&gt;
               &amp;lt;br&amp;gt;                             &lt;br /&gt;
   // Set dmode to D32&lt;br /&gt;
   mvme_set_dmode(myvme, MVME_DMODE_D32)&lt;br /&gt;
   ....&lt;br /&gt;
                          &amp;lt;br&amp;gt;                  &lt;br /&gt;
   set_equipment_status(equipment[idx].name, &amp;quot;OK&amp;quot;, &amp;quot;green&amp;quot;);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Reporting Equipment Status===&lt;br /&gt;
If running with the webserver [[mhttpd]], a frontend can send an update to the [[Status Page]], to report on its progress, using the function set_equipment_status() (see above example). This is useful when hardware can take a long time to respond.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function begin_of_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being started&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called every time a run start transition occurs, i.e. at begin-of-run. It allows the updating of user parameters, and the loading/setup/clearing of hardware. At the exit of this function, the acquisition should be armed and ready to test the interrupt (if used), e.g. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT begin_of_run (INT runnumber, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   INT status;&lt;br /&gt;
   status = update_user_parameters(); // update parameters from ODB&lt;br /&gt;
         &amp;lt;br&amp;gt;&lt;br /&gt;
   // Set am to A24 non-privileged Data&lt;br /&gt;
   mvme_set_am(myvme, MVME_AM_A24_ND);&lt;br /&gt;
   // Set dmode to D32&lt;br /&gt;
   mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
                   &amp;lt;br&amp;gt;                            &lt;br /&gt;
   //-------- ADCs -------------------&lt;br /&gt;
   v792_Setup(myvme, VADC0_BASE, 2);&lt;br /&gt;
   v792_ThresholdWrite(myvme, VADC0_BASE,&lt;br /&gt;
       (WORD *)&amp;amp;(ts.v792.threshold1));&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
   csr = v792_CSR1Read(myvme, VADC0_BASE);&lt;br /&gt;
   printf(&amp;quot;Data Ready ADC0: 0x%x\n&amp;quot;, csr);&lt;br /&gt;
   ........&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
   // Reset Latch&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE, 0x1);&lt;br /&gt;
   // Clear pending interrupts&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+8, 0x0);&lt;br /&gt;
   // Enable interrupt&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Accessing variables from the ODB ===&lt;br /&gt;
The  [http://ladd00.triumf.ca/~daqweb/doc/midas/doc/html/group__odbfunctionc.html MIDAS library] contains a number of subroutines to access the ODB.  The ODB keys can easily be accessed from a frontend or other client individually, or by using a saved C structure, which is often used when a large number of keys are to be accessed. &lt;br /&gt;
&lt;br /&gt;
==== Using C structure ====&lt;br /&gt;
&amp;lt;div id=&amp;quot;get_user_parameters&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
The function get_user_parameters() relies on the ODB structure &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/Equipment/Trigger/settings&amp;lt;/span&amp;gt; being mapped in &lt;br /&gt;
[[#Function frontend_init|frontend_init()]] with db_create_record() to ensure that the required ODB structure is present. Then the contents of the ODB keys can be obtained from the C structure (defined in &lt;br /&gt;
[[#Include files|experim.h]]) with a db_get_record() call, as demonstrated in the following example:&lt;br /&gt;
&lt;br /&gt;
 INT get_user_parameters(void)&lt;br /&gt;
 {&lt;br /&gt;
  INT status,size;&lt;br /&gt;
  TRIGGER_SETTINGS ps;  // defined in experim.h&lt;br /&gt;
               &amp;lt;br&amp;gt;&lt;br /&gt;
  /* Get current  settings */&lt;br /&gt;
  size = sizeof(ps);&lt;br /&gt;
  status = db_get_record(hDB, hSet, &amp;amp;ps, &amp;amp;size, 0); // HNDLE hSet obtained in frontend_init()&lt;br /&gt;
  if (status != DB_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      cm_msg(MERROR, &amp;quot;get_exp_settings&amp;quot;, &amp;quot;cannot retrieve trigger settings record (size of ps=%d)&amp;quot;, size);&lt;br /&gt;
      return status;&lt;br /&gt;
    }&lt;br /&gt;
  // Access the required ODB keys in the /equipment/trigger/settings/ subtree from the C structure&lt;br /&gt;
  printf(&amp;quot;Trigger mode = %s \n&amp;quot;, ps.trig_mode); &lt;br /&gt;
  printf(&amp;quot;Trigger delay (ms)= %f \n&amp;quot;,ps.trigger_delay_time__ms_);&lt;br /&gt;
  ...&lt;br /&gt;
  set_trig_delay(ps.trigger_delay_time__ms_);&lt;br /&gt;
  return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
If a number of values in this structure are changed by the frontend, they can be written back into the ODB with one library call, i.e. db_set_record(..).&lt;br /&gt;
 &lt;br /&gt;
==== Accessing individual keys ====&lt;br /&gt;
If experim.h is not included, parameters can be accessed from the ODB using db_get_value() or db_get_data().&lt;br /&gt;
&lt;br /&gt;
   char hostname[20];&lt;br /&gt;
   char str[128];&lt;br /&gt;
   int size,status;&lt;br /&gt;
  /* Get the camp hostname from the mdarc area of odb  */&lt;br /&gt;
  size = sizeof(hostname); &lt;br /&gt;
  sprintf(str,&amp;quot;/Equipment/%s/mdarc/camp/camp hostname&amp;quot;,equipment[FIFO].name); &lt;br /&gt;
  status = db_get_value(hDB, 0, str, hostname, &amp;amp;size, TID_STRING, FALSE);&lt;br /&gt;
  if(status != DB_SUCCESS)&lt;br /&gt;
  {&lt;br /&gt;
    cm_msg(MERROR,&amp;quot;camp_update_params&amp;quot;,&amp;quot;cannot get Camp hostname at %s (%d)&amp;quot;,str,status);&lt;br /&gt;
    return FE_ERR_ODB;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Values can be written into the ODB using db_set_value() or db_set_data().&lt;br /&gt;
&lt;br /&gt;
Note that all the ODB access functions mentioned here can be found in the  [http://ladd00.triumf.ca/~daqweb/doc/midas/doc/html/group__odbfunctionc.html MIDAS library], and more examples can be found in the midas package e.g. under ../midas/examples/ .&lt;br /&gt;
&lt;br /&gt;
==Functions pause/resume_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being paused/resumed.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
These two functions are called upon &amp;quot;Pause&amp;quot; and &amp;quot;Resume&amp;quot; command respectively. Any code relevant to the upcoming run state can be included,e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT pause_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   disable_trigger();&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
                            //&lt;br /&gt;
 INT resume_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   enable_trigger();&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function end_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being ended.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called at every &amp;quot;stop run&amp;quot; transition. It provides the opportunity to disable the hardware, e.g.&lt;br /&gt;
&lt;br /&gt;
 INT end_of_run(INT run_number, char *error)&lt;br /&gt;
 {&lt;br /&gt;
   // Stop DAQ for seting up the parameters&lt;br /&gt;
   vf48_AcqStop(myvme, VF48_BASE);&lt;br /&gt;
                       //&lt;br /&gt;
   done = 0;&lt;br /&gt;
   stop_req = 0;&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   trig_level = 0;&lt;br /&gt;
   // Close run gate&lt;br /&gt;
   vmeio_AsyncWrite(myvme, VMEIO_BASE, 0x0);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function frontend_exit==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
The function runs When the frontend program is shut down. Can be used to release any locked resources like memory, communications ports etc. e.g.&lt;br /&gt;
&lt;br /&gt;
  function frontend_exit()&lt;br /&gt;
  {&lt;br /&gt;
     mvme_close(gVme);&lt;br /&gt;
     return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_loop==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
If the flag frontend_call_loop is set TRUE, this routine is called when the frontend is idle or once between every event. In the following example, it is being used to check for a timeout:&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 ...&lt;br /&gt;
                                 //&lt;br /&gt;
 INT frontend_loop()&lt;br /&gt;
 {&lt;br /&gt;
   char str[128];&lt;br /&gt;
                   //&lt;br /&gt;
   if (stop_req &amp;amp;&amp;amp; done==0) {&lt;br /&gt;
      db_set_value(hDB,0,&amp;quot;/logger/channels/0/Settings/Event limit&amp;quot;, &amp;amp;evlimit, sizeof(evlimit), 1, TID_DWORD);&lt;br /&gt;
      if (cm_transition(TR_STOP, 0, str, sizeof(str), ASYNC, FALSE) != CM_SUCCESS) {&lt;br /&gt;
         cm_msg(MERROR, &amp;quot;VF48 Timeout&amp;quot;, &amp;quot;cannot stop run: %s&amp;quot;, str);&lt;br /&gt;
      }&lt;br /&gt;
      inRun = 0;&lt;br /&gt;
      // Disable interrupt&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
      done = 1;&lt;br /&gt;
      cm_msg(MERROR, &amp;quot;VF48 Timeout&amp;quot;,&amp;quot;VF48 Stop requested&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Event Types and Triggers==&lt;br /&gt;
The frontend supports several different types of [[Frontend Operation#Frontend event triggers|event trigger]]. The event trigger type is specified by the &lt;br /&gt;
[[Equipment Flags|Equipment Flag]]  in the  [[Equipment List Parameters|Equipment Declaration]]. Common event types are &amp;quot;polled events&amp;quot; where the Equipment Flag is  [[Equipment Flags#EQ_POLLED|EQ_POLLED]], &amp;quot;interrupts events&amp;quot; where the Flag is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]], and &amp;quot;periodic events&amp;quot; where the Flag is [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]. The name of the associated readout routine is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type.&lt;br /&gt;
&lt;br /&gt;
Polled  and interrupt events (see [[#Event Types|Event Types]]) require several extra functions to handle the hardware that periodic events do not require. These are described below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function poll_event==&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* INT &#039;&#039;&#039;count&#039;&#039;&#039;&lt;br /&gt;
* BOOL &#039;&#039;&#039;test&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment.&lt;br /&gt;
&lt;br /&gt;
In this case, the [[Equipment List Parameters|Equipment declaration]] would have this form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Trigger&amp;quot;,            // equipment name&lt;br /&gt;
        {&lt;br /&gt;
          ...&lt;br /&gt;
          &amp;lt;b&amp;gt;EQ_POLLED&amp;lt;/b&amp;gt;,          // equipment type&lt;br /&gt;
          ...&lt;br /&gt;
          500,                // poll for 500ms &lt;br /&gt;
          ...&lt;br /&gt;
          &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
          read_trigger_event,    // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
         &lt;br /&gt;
The user must provide suitable code in the routine poll_event(), e.g.&lt;br /&gt;
&lt;br /&gt;
 INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
 {&lt;br /&gt;
    /* Polling routine for events. Returns TRUE if event  is available. If test equals TRUE, don&#039;t return. &lt;br /&gt;
       The test flag is used to time the polling &lt;br /&gt;
     */&lt;br /&gt;
     int i;&lt;br /&gt;
     int lam = 0;&lt;br /&gt;
                                 //&lt;br /&gt;
     for (i = 0; i &amp;lt; count; i++, lam++) {&lt;br /&gt;
       lam = vmeio_CsrRead(myvme, VMEIO_BASE);&lt;br /&gt;
       if (lam)&lt;br /&gt;
         if (!test)&lt;br /&gt;
           return lam;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function interrupt_configure==&lt;br /&gt;
* INT &#039;&#039;&#039;cmd&#039;&#039;&#039; &lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* PTYPE &#039;&#039;&#039;adr&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_INTERRUPT, an interrupt configuration routine called interrupt_configure() must be provided by the user.&lt;br /&gt;
The interrupt configuration routine has the following declaration: &lt;br /&gt;
&lt;br /&gt;
 /*-- Interrupt configuration --------------------------*/&lt;br /&gt;
 INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
 {&lt;br /&gt;
  int vec = 0;&lt;br /&gt;
  switch (cmd) &lt;br /&gt;
  {&lt;br /&gt;
    case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x1);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
      mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
      mvme_interrupt_attach(myvme, INT_LEVEL, INT_VECTOR, &lt;br /&gt;
                (void *)adr, &amp;amp;myinfo);&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+0x10, INT_VECTOR);&lt;br /&gt;
      vec = mvme_read_value(myvme, VLAM_BASE+0x10);&lt;br /&gt;
      printf(&amp;quot;Interrupt Attached to 0x%x for vector:0x%x\n&amp;quot;,&lt;br /&gt;
                     adr, vec&amp;amp;0xFF);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DETACH:&lt;br /&gt;
      printf(&amp;quot;Interrupt Detach\n&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Under the four commands listed above, the user must implement the hardware operation needed to perform the requested action. In the Midas drivers directory examples can be found of such an interrupt code for CAMAC. See source code such as hyt1331.c,ces8210.c &lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user in the frontend.&lt;br /&gt;
&lt;br /&gt;
==Event Readout routine==&lt;br /&gt;
In the case of [[#Polled and Interrupt Events|POLLED or INTERRUPT events]], the event readout routine is called an [[#Polled or Interrupt readout routine|Interrupt readout routine]].&lt;br /&gt;
&lt;br /&gt;
An event readout routine (called when an event occurs) is usually of the form&lt;br /&gt;
&lt;br /&gt;
 INT function_name ( char *pevent ... )&lt;br /&gt;
 {&lt;br /&gt;
   INT event_size;&lt;br /&gt;
   ........  // read data from hardware&lt;br /&gt;
   ........  // pack into banks depending on format&lt;br /&gt;
   ........&lt;br /&gt;
   return (event_size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
where the first argument of the readout function (pevent)  provides the pointer to the newly constructed event, and points to the first valid location for storing the data.&lt;br /&gt;
;NOTE &lt;br /&gt;
* The return value is the event size, and must be the number of bytes collected in this function.&lt;br /&gt;
* The event serial number will be incremented by one for every call to the readout routine, as long as the returned size is non-zero.&lt;br /&gt;
* If the returned value is set to zero, the event will be dismissed and the serial number to that event will be decremented by one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===General readout function===&lt;br /&gt;
If the Equipment type is EQ_INTERRUPT or EQ_POLLED, see also [[#Polled or Interrupt readout routine|Polled or Interrupt readout routine]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the case of a &#039;&#039;&#039;periodic&#039;&#039;&#039; event, the Equipment declaration may have this form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Scaler&amp;quot;,           // equipment name&lt;br /&gt;
         {    &lt;br /&gt;
            ...&lt;br /&gt;
            EQ_PERIODIC     // equipment type&lt;br /&gt;
            0,              // interrupt source (ignored) &lt;br /&gt;
            &amp;quot;MIDAS&amp;quot;,        // event format&lt;br /&gt;
            ...&lt;br /&gt;
            10000,          // period (read every 10s)&lt;br /&gt;
            ...&lt;br /&gt;
            &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
       read_scaler_event,   // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
A readout event will often need to send out data. This is done using one of the supported [[Event Structure|event structures]] usually [[Event Structure#MIDAS Format Event|&amp;quot;MIDAS&amp;quot; format]] data banks.  The bank format (&amp;quot;MIDAS&amp;quot; in the example above) is declared in the [[Equipment List Parameters]]   [[Equipment List Parameters#Format|Format]] field.&lt;br /&gt;
&lt;br /&gt;
An example of a scaler readout routine read_scaler_event() where the data is read out into [[MIDAS Event Construction|MIDAS data banks]] is shown below. &lt;br /&gt;
&lt;br /&gt;
 INT read_scaler_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
    DWORD *pdata, a;&lt;br /&gt;
                               //&lt;br /&gt;
    /* init bank structure; */&lt;br /&gt;
    bk_init(pevent);&lt;br /&gt;
                               //&lt;br /&gt;
    /* create SCLR bank */&lt;br /&gt;
    bk_create(pevent, &amp;quot;SCLR&amp;quot;, TID_DWORD, &amp;amp;pdata);&lt;br /&gt;
                               //&lt;br /&gt;
    /* read scaler bank (CAMAC) */&lt;br /&gt;
    for (a = 0; a &amp;lt; N_SCLR; a++)&lt;br /&gt;
       cam24i(CRATE, SLOT_SCLR, a, 0, pdata++);&lt;br /&gt;
                               //    &lt;br /&gt;
    /* close SCLR bank */&lt;br /&gt;
    bk_close(pevent, pdata);&lt;br /&gt;
                               //&lt;br /&gt;
    /* return event size in bytes */&lt;br /&gt;
    return bk_size(pevent);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Some other examples of event readout routines in this document are&lt;br /&gt;
* [[Event Structure#FIXED Event Construction|FIXED Format event construction]]&lt;br /&gt;
* [[#Polled or Interrupt readout routine|Polled or Interrupt readout routines]]&lt;br /&gt;
* [[Super Event]] construction&lt;br /&gt;
* [[Slow Control System|Slow Control]]&lt;br /&gt;
&lt;br /&gt;
Many other examples of readout routines can be found in the [[#Frontend Templates|frontend templates]] in the MIDAS package.&lt;br /&gt;
&lt;br /&gt;
===Polled or Interrupt readout routine===&lt;br /&gt;
In the case of a [[#Polled and Interrupt Events|Polled or Interrupt event]], the content of the memory location pointed to by &#039;&#039;&#039;pevent&#039;&#039;&#039; (see [[#Event Readout routine|Event Readout routine]]) prior to its use in the readout function, contains the interrupt source bitwise register. This feature can be exploited in order to identify which hardware module has triggered the readout when multiple interrupts have been assigned to the same readout function.&lt;br /&gt;
&lt;br /&gt;
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism.&lt;br /&gt;
The  [[Equipment List Parameters|Equipment declaration]] is of the form: &lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
               //&lt;br /&gt;
    {&amp;quot;Trigger&amp;quot;,  /* equipment name */&lt;br /&gt;
       ...&lt;br /&gt;
 #ifdef USE_INT&lt;br /&gt;
       EQ_INTERRUPT, /* equipment type */&lt;br /&gt;
 #else&lt;br /&gt;
       EQ_POLLED,    /* equipment type */&lt;br /&gt;
 #endif&lt;br /&gt;
   /* interrupt source: crate 0, all stations */&lt;br /&gt;
       LAM_SOURCE(0, 0x0),&lt;br /&gt;
       ....&lt;br /&gt;
       &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,&lt;br /&gt;
     },&lt;br /&gt;
     read_trigger_event, /* readout routine */&lt;br /&gt;
     NULL, NULL,&lt;br /&gt;
     trigger_bank_list,&lt;br /&gt;
 }&lt;br /&gt;
  &lt;br /&gt;
Note that the [[Macros#CAMAC Macros|LAM_SOURCE macro]] simply codes the parameters into a bitwise register.&lt;br /&gt;
&lt;br /&gt;
The readout routine would contains code such as&lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   DWORD  *pdata;&lt;br /&gt;
 #endif&lt;br /&gt;
                        //&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   /* read ADC0 data */&lt;br /&gt;
   v792_EvtCntRead(myvme, VADC0_BASE, &amp;amp;evtcnt);&lt;br /&gt;
   ........&lt;br /&gt;
   /* Read Event */&lt;br /&gt;
   v792_EventRead(myvme, VADC0_BASE, pdata, &amp;amp;nentry);&lt;br /&gt;
   ........&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
 #endif&lt;br /&gt;
                       //&lt;br /&gt;
   ........&lt;br /&gt;
   return (size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The data will be packed into banks as described for the [[General readout function|general readout function]] above.&lt;br /&gt;
The examples fevmemodules.c (VME) and frontend.c (CAMAC) (see [[#Frontend Templates]] contain a complete example of read_trigger_event().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Manual Trigger==&lt;br /&gt;
Another type of [[Frontend Operation#Frontend event trigger|frontend event trigger]] supported is the &amp;quot;manual trigger&amp;quot;,  where the Equipment Flag is [[Equipment Flags#EQ_MANUAL_TRIGGER|EQ_MANUAL_TRIGGER]]. This flag means that an event can be triggered through the URL &amp;lt;code&amp;gt;?cmd=Trigger/&amp;lt;equipment_name&amp;gt;&amp;lt;/code&amp;gt; (e.g. &amp;lt;code&amp;gt;http://my.host:8080/?cmd=Trigger/MyEquipmentName&amp;lt;/code&amp;gt;). If you add this URL to the [[/Alias ODB tree|/Alias]] part of the ODB, then a link will appear on midas webpages that can be clicked to trigger an event.&lt;br /&gt;
&lt;br /&gt;
In some cases, the same readout code may be used for two types of event: a manual trigger and (say) a poll event. It is possible to determine whether the readout of an event was triggered by a manual trigger or a regular trigger by adding a call to the  [[MIDAS Event Header Macros|event header Macro]] DATA_SIZE in the readout routine:&lt;br /&gt;
&lt;br /&gt;
  flag = DATA_SIZE(pevent);&lt;br /&gt;
&lt;br /&gt;
If the result is&lt;br /&gt;
  *  flag = 0 normal call&lt;br /&gt;
  *  flag = 1 manual trigger&lt;br /&gt;
&lt;br /&gt;
It is also possible for a backend MIDAS client (such as an analyzer or custom data archiver) to trigger a manual trigger event. The client controls when an event is sent by means of&lt;br /&gt;
a  function that requests an event by triggering the event sending mechanism with a RPC call.&lt;br /&gt;
&lt;br /&gt;
With a frontend Equipment declaration of a manually triggered event of the form:&lt;br /&gt;
    &lt;br /&gt;
    { &amp;quot;Histo&amp;quot;,             /* equipment name */&lt;br /&gt;
       2, 0,                 /* event ID, trigger mask */&lt;br /&gt;
       &amp;quot;SYSTEM&amp;quot;,             /* event buffer */&lt;br /&gt;
       EQ_MANUAL_TRIG,     /* equipment type */&lt;br /&gt;
       0,    &lt;br /&gt;
       .......&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
the code fragment to manually trigger this event is:&lt;br /&gt;
&lt;br /&gt;
  int main(unsigned int argc,char **argv)&lt;br /&gt;
  {&lt;br /&gt;
      .......&lt;br /&gt;
      bm_request_event(hBufEvent, 2, TRIGGER_ALL, GET_ALL, &amp;amp;request_id, process_event_TD);&lt;br /&gt;
      .......&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
When it is time to save the data during the run, the function below is called:&lt;br /&gt;
&lt;br /&gt;
 BOOL trigger_histo_event(void)&lt;br /&gt;
 {&lt;br /&gt;
   HNDLE hconn;&lt;br /&gt;
   BOOL event_triggered;&lt;br /&gt;
                            //&lt;br /&gt;
   event_triggered = FALSE;&lt;br /&gt;
   ...................&lt;br /&gt;
   if (run_state == STATE_RUNNING) {   // Check the frontend client exists&lt;br /&gt;
        if( cm_exist(ClientName,TRUE))  {    &lt;br /&gt;
            status = cm_connect_client (ClientName, &amp;amp;hconn);&lt;br /&gt;
            if(status != RPC_SUCCESS)&lt;br /&gt;
                cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Cannot connect to frontend \&amp;quot;%s\&amp;quot; (%d)&amp;quot;,&lt;br /&gt;
                       ClientName,status);&lt;br /&gt;
            else  {     // successfully connected to frontend client&lt;br /&gt;
                status = rpc_client_call(hconn, RPC_MANUAL_TRIG, 2); // trigger a histo event&lt;br /&gt;
                if (status != CM_SUCCESS)&lt;br /&gt;
                    cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error triggering event from frontend (%d)&amp;quot;,status);&lt;br /&gt;
                else {  // successfully triggered event&lt;br /&gt;
                    event_triggered=TRUE;&lt;br /&gt;
                    status =cm_disconnect_client(hconn, FALSE);&lt;br /&gt;
                    if (status != CM_SUCCESS)&lt;br /&gt;
                        cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error disconnecting client (%d)&amp;quot;,status);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Frontend client %s not running (%d)&amp;quot;,&lt;br /&gt;
                   ClientName,status);&lt;br /&gt;
    } &lt;br /&gt;
    return(event_triggered);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Deferred Transition==&lt;br /&gt;
This option permits the user to postpone any transition issued by any requester&lt;br /&gt;
until some condition is satisfied.&lt;br /&gt;
For example:&lt;br /&gt;
* It may not be advisable to pause or stop a run until some hardware has turned off a particular valve. &lt;br /&gt;
* The start of the acquisition system should be postponed until the beam rate has been stable for a given period of time.&lt;br /&gt;
* While active, a particular acquisition system should not be interrupted until the &amp;quot;cycle&amp;quot; is completed.&lt;br /&gt;
&lt;br /&gt;
In these examples, any application having access to the state of the hardware can register to be a &amp;quot;transition Deferred&amp;quot; client. The MIDAS system will then catch any transition request and postpone the trigger of such a transition until the condition  is satisfied.&lt;br /&gt;
&lt;br /&gt;
The Deferred transition requires 3 steps for setup&lt;br /&gt;
# Register for the deferred transition &lt;br /&gt;
# Provide a callback function to serve the deferred transition &lt;br /&gt;
# Implement the condition code &lt;br /&gt;
&lt;br /&gt;
The following example demonstrates this process:&lt;br /&gt;
&lt;br /&gt;
  BOOL transition_PS_requested=FALSE; // global&lt;br /&gt;
                                              //&lt;br /&gt;
  INT frontend_init()&lt;br /&gt;
  {&lt;br /&gt;
    // register for deferred transition&lt;br /&gt;
                                              //&lt;br /&gt;
    cm_register_deferred_transition(TR_STOP, wait_end_cycle);&lt;br /&gt;
    cm_register_deferred_transition(TR_PAUSE, wait_end_cycle);&lt;br /&gt;
    ...  &lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */ &lt;br /&gt;
  //-- Deferred transition callback&lt;br /&gt;
  BOOL wait_end_cycle(int transition, BOOL first)&lt;br /&gt;
  {&lt;br /&gt;
    if (first) {&lt;br /&gt;
      transition_PS_requested = TRUE;&lt;br /&gt;
      return FALSE;&lt;br /&gt;
    }&lt;br /&gt;
                        //&lt;br /&gt;
    if (end_of_mcs_cycle){&lt;br /&gt;
      transition_PS_requested = FALSE;&lt;br /&gt;
      end_of_mcs_cycle = FALSE;&lt;br /&gt;
      return TRUE;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
      return FALSE;&lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */&lt;br /&gt;
  INT read_mcs_event(char *pevent, INT offset)&lt;br /&gt;
  {  &lt;br /&gt;
      ...     // read out data at end of cycle&lt;br /&gt;
      ...&lt;br /&gt;
      end_of_mcs_cycle = TRUE; // end of cycle &lt;br /&gt;
                               //&lt;br /&gt;
      if (!transition_PS_requested)&lt;br /&gt;
         start_cycle(); // start a new cycle &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the example above, &lt;br /&gt;
&lt;br /&gt;
* The frontend code is registered for PAUSE and STOP using &#039;&#039;cm_register_deferred_transition()&#039;&#039;. The second argument &#039;&#039;wait_end_cycle&#039;&#039;  is the declaration of the callback function. &lt;br /&gt;
* The callback function &#039;&#039;wait_end_cycle&#039;&#039; will be called as soon as the transition is requested with the Boolean flag &#039;&#039;first&#039;&#039; set to TRUE.&lt;br /&gt;
* By setting &#039;&#039;transition_PS_requested&#039;&#039;  TRUE , the user will be provided with the acknowledgment of the transition request.&lt;br /&gt;
* By returning FALSE from the callback, the transition is prevented from occurring. &lt;br /&gt;
* As soon as the user condition is satisfied (&#039;&#039;end_of_mcs_cycle&#039;&#039; = TRUE), the return code in the  callback will be set to TRUE and the requested transition will be issued.&lt;br /&gt;
&lt;br /&gt;
While the transition is Deferred, the odb key  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; will contain the transition code, and the d [[mhttpd]] webserver main status page will indicate that a deferred transition is in progress.&lt;br /&gt;
&lt;br /&gt;
Once in deferred state, an [[odbedit]] override command can be issued to force the transition to happen,&lt;br /&gt;
&lt;br /&gt;
 &amp;gt;odbedit&lt;br /&gt;
 odb&amp;gt; stop now    (or &amp;quot;start now&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
or  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; can be set to 0. The transition will then take place on the next stop or start command.&lt;br /&gt;
&lt;br /&gt;
= Compilation using CMake =&lt;br /&gt;
&lt;br /&gt;
Here is a &amp;quot;minimal&amp;quot; CMakeLists.txt file that can be used to compile a user-written frontend (called &amp;quot;myfe&amp;quot; in this case) that uses the mfe framework.&lt;br /&gt;
&lt;br /&gt;
  cmake_minimum_required(VERSION 3.0)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  &lt;br /&gt;
  # Check for MIDASSYS environment variable&lt;br /&gt;
  if (NOT DEFINED ENV{MIDASSYS})&lt;br /&gt;
    message(SEND_ERROR &amp;quot;MIDASSYS environment variable not defined.&amp;quot;)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  set(CMAKE_CXX_STANDARD 11)&lt;br /&gt;
  set(MIDASSYS $ENV{MIDASSYS})&lt;br /&gt;
  &lt;br /&gt;
  if (${CMAKE_SYSTEM_NAME} MATCHES Linux)&lt;br /&gt;
    set(LIBS -lpthread -lutil -lrt)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  # Define the executable to be built, and the source code files&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  &lt;br /&gt;
  # Directories to search for &lt;br /&gt;
  target_include_directories(myfe.exe PRIVATE ${MIDASSYS}/include)&lt;br /&gt;
  &lt;br /&gt;
  # Libraries to link to&lt;br /&gt;
  target_link_libraries(myfe.exe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})&lt;br /&gt;
&lt;br /&gt;
One could then build the executable using the folllowing commands:&lt;br /&gt;
&lt;br /&gt;
  mkdir build&lt;br /&gt;
  cd build&lt;br /&gt;
  cmake ..&lt;br /&gt;
  make&lt;br /&gt;
&lt;br /&gt;
Some older operating systems may require the &amp;quot;cmake3&amp;quot; command to be used instead of &amp;quot;cmake&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_Application&amp;diff=3412</id>
		<title>Frontend Application</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_Application&amp;diff=3412"/>
		<updated>2024-01-05T17:26:46Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Arguments */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
== Links ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation#Frontend|Frontend Operation]]&lt;br /&gt;
* [[Frontend user code]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Frontend Task ==&lt;br /&gt;
The purpose of a &amp;lt;span style=&amp;quot;color: green; font-style:italic; &amp;quot;&amp;gt;&#039;&#039;frontend task&#039;&#039;&amp;lt;/span&amp;gt; is to collect data from the hardware and transmit this information to a central place where data logging and &lt;br /&gt;
analysis can be performed. &lt;br /&gt;
&lt;br /&gt;
A  &amp;lt;span style=&amp;quot;color: green; font-style:italic; &amp;quot;&amp;gt;&#039;&#039;frontend task&#039;&#039;&amp;lt;/span&amp;gt; is built by the user&lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Operation]] ) and consists of as &amp;quot;user part&amp;quot; and a &amp;quot;system framework&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Arguments ==&lt;br /&gt;
The system framework code (mfe.cxx) has the following arguments:&lt;br /&gt;
&lt;br /&gt;
  [-h hostname ] : host name (see [[Common Parameters to MIDAS Utilities]])&lt;br /&gt;
  [-e exptname ] : experiment name (see [[Common Parameters to MIDAS Utilities]])&lt;br /&gt;
  [-D ] : Become a Daemon.&lt;br /&gt;
  [-O ] : Become a Daemon but keep stdout &lt;br /&gt;
  [-d ] : Used for debugging the frontend&lt;br /&gt;
  [-v [level]] : Set verbosity level (as an integer). A bare &#039;-v&#039; implies &#039;-v 1&#039;&lt;br /&gt;
  [-i index] : Set frontend index so you can have [[Equipment_List_Parameters#Equipment_Name|multiple instances of the same executable running with different names]] (can be used with [[Mevb]] / [[Event Builder#Principle of the Event Builder and related frontend fragment|event builder]] or standalone).&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Start the application as a daemon, using the default host, experiment and port :&lt;br /&gt;
&lt;br /&gt;
 e.g. fevme -D&lt;br /&gt;
&lt;br /&gt;
Start the application superceding the default host, experiment and port:&lt;br /&gt;
&lt;br /&gt;
 e.g. fegpib -e exp218 -h isdaq10 -p 7077 -D&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3411</id>
		<title>Frontend user code</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_user_code&amp;diff=3411"/>
		<updated>2024-01-05T16:09:51Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation]]&lt;br /&gt;
* [[Frontend Application]] &lt;br /&gt;
* [[Equipment List Parameters]]&lt;br /&gt;
* [[Slow Control System]]&lt;br /&gt;
* [[Equipment Flags]]&lt;br /&gt;
* [[Event Notification (Hot-Link)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This section describes the features of the user-written part of a &amp;quot;C-style&amp;quot; [[Frontend Operation#Frontend|Frontend]], referred to here as &#039;&#039;&#039;frontend.c&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
You can also write frontends using [[Frontend_user_code_(object_oriented_-_TMFE)|object-oriented C++ (TMFE)]] or [[Python]].&lt;br /&gt;
&lt;br /&gt;
= Frontend Templates =&lt;br /&gt;
To make a user-written or custom frontend, users will usually modify one of the &#039;&#039;&#039;templates&#039;&#039;&#039; provided in the MIDAS package&lt;br /&gt;
under [[Environment Variables#MIDASSYS|$MIDASSYS]]/examples/ for their particular hardware and other requirements. Make sure to pick the closest template, e.g. if writing a [[Slow Control System|Slow Control]] frontend, pick a slow-control template. For each example frontend, a Makefile is also provided.&lt;br /&gt;
&lt;br /&gt;
A partial list of the templates provided is shown below:&lt;br /&gt;
{| style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;3&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Hardware &lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Filename&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Directory (MIDAS package)&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Purpose&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Language&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevmemodules.c&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|VME &lt;br /&gt;
|fevme.cxx&lt;br /&gt;
| $MIDASSYS/examples/Triumf/c++/&lt;br /&gt;
| Access to VME modules&lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|CAMAC &lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/experiment/&lt;br /&gt;
| Access to CAMAC modules&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|mtfe.c&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| &#039;&#039;&#039;Multithreaded&#039;&#039;&#039; using ring buffer&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| Wiener CC-USB &lt;br /&gt;
|feccusb.cxx&lt;br /&gt;
| $MIDASSYS/examples/mtfe/&lt;br /&gt;
| CAMAC/USB demo &lt;br /&gt;
| C++&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|RS485 bus &lt;br /&gt;
|mscb_fe.c&lt;br /&gt;
| $MIDASSYS/examples/slowcont/&lt;br /&gt;
| &#039;&#039;&#039;Slow control&#039;&#039;&#039; with [https://midas.psi.ch/mscb/ MSCB]&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|EPICS&lt;br /&gt;
|frontend.c&lt;br /&gt;
| $MIDASSYS/examples/epics/&lt;br /&gt;
| &#039;&#039;&#039;Slow controls&#039;&#039;&#039;&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|Camac&lt;br /&gt;
|ebfe.c&lt;br /&gt;
| $MIDASSYS/examples/eventbuilder/&lt;br /&gt;
| &#039;&#039;&#039;Event Builder&#039;&#039;&#039; (with [[mevb]])&lt;br /&gt;
| C&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The features of a typical frontend program are best explained by reference to examples of the user code &lt;br /&gt;
provided in the Midas Package. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Frontend code=&lt;br /&gt;
Note that there are several kinds of frontend used for different purposes, e.g.&lt;br /&gt;
* frontend to read VME or CAMAC hardware, using interrupt or polling (e.g. to read experimental data)&lt;br /&gt;
* multithreaded frontend where polling can be in a separate thread&lt;br /&gt;
* slow control frontend (can be multithreaded) see also [[Slow Control System]]&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Templates for many types of user frontend code are provided in the MIDAS packages (see [[#Frontend Templates]]). &lt;br /&gt;
&lt;br /&gt;
The following sections refer to these templates.&lt;br /&gt;
Most of the examples are taken from &lt;br /&gt;
[http://ladd00.triumf.ca/~daqweb/doc/midas/examples/Triumf/c/fevmemodules.c fevmemodules.c]. Documentation on the MIDAS library subroutines to access the ODB (some of which are used in the examples below) can be found at&lt;br /&gt;
[http://ladd00.triumf.ca/~daqweb/doc/midas/doc/html/group__odbfunctionc.html odb functions].&lt;br /&gt;
&lt;br /&gt;
The user frontend code is then compiled and linked with the system part and the MIDAS library &lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Task]]).  Makefiles are provided with the templates that can be modified as needed.&lt;br /&gt;
&lt;br /&gt;
== Access to command line parameters ==&lt;br /&gt;
A new function &#039;&#039;&#039;mfe_get_args&#039;&#039;&#039; was created to allow the [[#frontend code|frontend code]] to have access to the [[Frontend Application|Frontend]] command line parameters. &lt;br /&gt;
&lt;br /&gt;
This example shows how to use it in the frontend user code :&lt;br /&gt;
&lt;br /&gt;
  int argc;&lt;br /&gt;
  char **argv; &amp;lt;br&amp;gt;&lt;br /&gt;
  mfe_get_args(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
  for (int i=0 ; i&amp;lt;argc ; i++)&lt;br /&gt;
     puts(argv[i]); &lt;br /&gt;
&lt;br /&gt;
See [[Frontend_Application#Arguments|Frontend Application Arguments]] for the arguments that are handled automatically by the frontend system.&lt;br /&gt;
&lt;br /&gt;
==Include files==&lt;br /&gt;
The following example (from template frontend file &#039;&#039;fevmemodules.c&#039;&#039; - see [[#Frontend code|above]]) shows the standard include files needed for VME access with VMIC. The user may add any other include files as needed. In this case, header files for several device drivers have also been added. &lt;br /&gt;
The optional file &#039;&#039;&#039;[[ODB#experim.h include file|experim.h]]&#039;&#039;&#039; is often included. This is a special include file for ease of communication between the C code and the ODB. The example &#039;&#039;fevmemodules.c&#039;&#039; includes &amp;lt;br&amp;gt;&lt;br /&gt;
http://ladd00.triumf.ca/~daqweb/doc/midas/examples/Triumf/c/experim.h. &amp;lt;br&amp;gt;&lt;br /&gt;
The file experim.h (except when using one of the examples) is generated by the user (see [[ODB#experim.h include file|experim.h]]). The example below shows a typical list of include files for a frontend:&lt;br /&gt;
&lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;     // C standard headers&lt;br /&gt;
 #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
 #include &amp;quot;midas.h&amp;quot;     // MIDAS include file&lt;br /&gt;
 #include &amp;quot;mvmestd.h&amp;quot;   // VME header file&lt;br /&gt;
 #include &amp;quot;vmicvme.h&amp;quot;   // VMIC header file&lt;br /&gt;
 #include &amp;quot;vmeio.h&amp;quot;     // optional hardware header files&lt;br /&gt;
 #include &amp;quot;v1190B.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;v792.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;vf48.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;v1729.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;experim.h&amp;quot;   // user-created with odbedit make command&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;div id=&amp;quot;Frontend Name&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
==Global declarations==&lt;br /&gt;
The following example (from template frontend file fevmemodules.c - see [[#Frontend Templates]]) shows the global declaration. The declarations are system wide.  Some may be changed to suit the user, but none should not be removed.&lt;br /&gt;
&lt;br /&gt;
; frontend_name  : This value can be modified to reflect the purpose of the code &lt;br /&gt;
; frontend_call_loop : If set to TRUE, the function frontend_loop() runs after every equipment loop. If FALSE, frontend_loop() does not run. The user can add suitable code to this routine if desired (e.g. to check for a condition).&lt;br /&gt;
; display_period : The time interval (defined in milliseconds) between the refresh of a frontend status display. The value of zero disables the display. If the frontend is started in the background with the display enabled, the stdout should be redirected to the null device to prevent the process from hanging.&lt;br /&gt;
; max_event_size : specifies the maximum size (in bytes) of the expected event.&lt;br /&gt;
; event_buffer_size : specifies the maximum size (in bytes) of the buffer to be allocated by the system.&lt;br /&gt;
&lt;br /&gt;
See below for an example of global declarations from a frontend.&lt;br /&gt;
&lt;br /&gt;
 /* The frontend name (client name) as seen by other MIDAS clients   */&lt;br /&gt;
 char *frontend_name = &amp;quot;fevmemodules&amp;quot;;&lt;br /&gt;
 /* The frontend file name, don&#039;t change it */&lt;br /&gt;
 char *frontend_file_name = __FILE__;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* frontend_loop is called periodically if this variable is TRUE    */&lt;br /&gt;
 BOOL frontend_call_loop = FALSE;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* a frontend status page is displayed with this frequency in ms */&lt;br /&gt;
 INT display_period = 000;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* maximum event size produced by this frontend */&lt;br /&gt;
 INT max_event_size = 200000;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* maximum event size for fragmented events (EQ_FRAGMENTED) */&lt;br /&gt;
 INT max_event_size_frag = 5 * 1024 * 1024;&lt;br /&gt;
                                                                     //&lt;br /&gt;
 /* buffer size to hold events */&lt;br /&gt;
 INT event_buffer_size = 10 * 100000;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Global User Declarations==&lt;br /&gt;
&lt;br /&gt;
After the global declarations, the user may add his or her own declarations.&lt;br /&gt;
The following example  (from template frontend file fevmemodules.c - see [[#Frontend Templates]])  defines various VME hardware parameters. &lt;br /&gt;
An example of hardware declarations in a frontend are shown below:&lt;br /&gt;
&lt;br /&gt;
 /* Hardware */&lt;br /&gt;
 MVME_INTERFACE *myvme;&lt;br /&gt;
                      //&lt;br /&gt;
 /* VME base addresses */&lt;br /&gt;
 DWORD VMEIO_BASE = 0x780000;&lt;br /&gt;
 DWORD VTDC0_BASE = 0xF10000;&lt;br /&gt;
 .......&lt;br /&gt;
 /* Globals */&lt;br /&gt;
 #define  N_ADC 100&lt;br /&gt;
 #define  N_TDC 100&lt;br /&gt;
 #define  N_PTS 5000&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==System Function Prototypes==&lt;br /&gt;
&lt;br /&gt;
These prototypes declare the pre-defined system functions which should be present. &lt;br /&gt;
 INT frontend_init();&lt;br /&gt;
 INT frontend_exit();&lt;br /&gt;
 INT begin_of_run(INT run_number, char *error);&lt;br /&gt;
 INT end_of_run(INT run_number, char *error);&lt;br /&gt;
 INT pause_run(INT run_number, char *error);&lt;br /&gt;
 INT resume_run(INT run_number, char *error);&lt;br /&gt;
 INT frontend_loop();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Readout Function Prototypes==&lt;br /&gt;
Following the previous group is a second group of prototypes, which define the readout functions. These depend on the defined equipments, and run when the respective equipment is triggered. In this example, two equipments will be defined, so there are two prototypes. The user functions will be described in detail in the following sections. &lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off);&lt;br /&gt;
 INT read_scaler_event(char *pevent, INT off);&lt;br /&gt;
&lt;br /&gt;
If using an interrupt, callback function prototypes are also included&lt;br /&gt;
&lt;br /&gt;
 extern void interrupt_routine(void);&lt;br /&gt;
 void register_cnaf_callback(int debug);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Bank Definition and Equipment List==&lt;br /&gt;
&lt;br /&gt;
See [[Equipment List Parameters#Bank Definition|Bank Definitions]] and [[Equipment List Parameters|Equipment List]] for detailed information.&lt;br /&gt;
&lt;br /&gt;
However, this may be easier to follow if first the sequence of operations, and examples of the the different types of events (polled/interrupt or periodic etc.) and the functions they require in the frontend are introduced.&lt;br /&gt;
&lt;br /&gt;
==Sequence of Operations in the frontend==&lt;br /&gt;
&lt;br /&gt;
The following table shows the sequence of operations of the  [[Frontend Operation#Frontend|Frontend]] System functions.  These subroutines must be present in &#039;&#039;frontend.c&#039;&#039;,&lt;br /&gt;
but the contents are coded by the user. These functions are called by &#039;&#039;mfe.c&#039;&#039; at the appropriate time. The System Transition functions are associated with a particular [[Run States and Transitions|Run Transition]] as shown below:&lt;br /&gt;
&lt;br /&gt;
{|  style=&amp;quot;text-align: left; width: 60%; background-color: rgb(255, 255, 255);&amp;quot; border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | System Transition Function&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Associated [[Run States and Transitions|Transition]]&lt;br /&gt;
| colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;1&amp;quot; style=&amp;quot;vertical-align: top; background-color: rgb(255, 255, 204); font-weight: bold;&amp;quot; | Action&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_init()&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| Runs once after system initialization, before Equipment registration. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| begin_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_START&lt;br /&gt;
| Runs after system statistics reset at each begin-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| pause_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_PAUSE&lt;br /&gt;
| Runs at each pause-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| resume_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_RESUME&lt;br /&gt;
| Runs at each resume-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
| end_of_run()&lt;br /&gt;
| style=&amp;quot;color:blue&amp;quot;|TR_STOP&lt;br /&gt;
| Runs at each end-of-run request. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| frontend_exit()&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Runs once before any Slow Control Equipment exit &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Each defined Equipment has the option to force itself to run at individual transition times (see Equipment [[Equipment List Parameters#ReadOn|ReadOn Flag]]), so that its equipment function will be called on a certain transition (or combination of transitions). &lt;br /&gt;
&lt;br /&gt;
The system transition functions all run &#039;&#039;&#039;prior to&#039;&#039;&#039; the equipment functions. This gives the system the chance to take basic action on the transition request (e.g. enable/disable interrupt) before the equipment runs. All the transition routines run with a [[Run States and Transitions#Default Transition Sequency Numbers|Transition Sequence number]] of 500 (the default). This allows users to add additional functions in the frontend that will run before or after any of the transitions (such as a prestart() or a poststop() function). See [[Run States and Transitions#Run Transition Priority|Run Transition Priority]] for more information.&lt;br /&gt;
&lt;br /&gt;
==Function frontend_init ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
No parameters.&lt;br /&gt;
&lt;br /&gt;
This function runs &#039;&#039;&#039;once only&#039;&#039;&#039; at the application startup. Users may perform hardware checking, loading/setting of global variables, mapping of required ODB structures (see [[ODB#experim.h include file|experim.h include file]]), &lt;br /&gt;
[[Event Notification (Hot-Link)#How to set up a Hot-Link|setting up hot-links]] etc. in frontend_init(), e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT frontend_init()&lt;br /&gt;
 {&lt;br /&gt;
   set_equipment_status(equipment[idx].name, &amp;quot;Initializing...&amp;quot;, &amp;quot;yellow&amp;quot;);&lt;br /&gt;
   ....&lt;br /&gt;
         &amp;lt;br&amp;gt;&lt;br /&gt;
 /* Book Setting space */&lt;br /&gt;
   TRIGGER_SETTINGS_STR(trigger_settings_str);&lt;br /&gt;
         &amp;lt;br&amp;gt; &lt;br /&gt;
   /* Map /equipment/Trigger/settings (structure defined in experim.h) */&lt;br /&gt;
   sprintf(set_str, &amp;quot;/Equipment/Trigger/Settings&amp;quot;);&lt;br /&gt;
   status = db_create_record(hDB, 0, set_str, strcomb(trigger_settings_str));&lt;br /&gt;
   status = db_find_key (hDB, 0, set_str, &amp;amp;hSet);&lt;br /&gt;
   if (status != DB_SUCCESS)&lt;br /&gt;
     cm_msg(MINFO,&amp;quot;FE&amp;quot;,&amp;quot;Key %s not found&amp;quot;, set_str);&lt;br /&gt;
   return status;&lt;br /&gt;
         &amp;lt;br&amp;gt;&lt;br /&gt;
         &amp;lt;br&amp;gt;&lt;br /&gt;
   // Open VME interface&lt;br /&gt;
   status = mvme_open(&amp;amp;myvme, 0);&lt;br /&gt;
           &amp;lt;br&amp;gt;                                 &lt;br /&gt;
   // Set am to A24 non-privileged Data&lt;br /&gt;
   mvme_set_am(myvme, MVME_AM_A24_ND);&lt;br /&gt;
               &amp;lt;br&amp;gt;                             &lt;br /&gt;
   // Set dmode to D32&lt;br /&gt;
   mvme_set_dmode(myvme, MVME_DMODE_D32)&lt;br /&gt;
   ....&lt;br /&gt;
                          &amp;lt;br&amp;gt;                  &lt;br /&gt;
   set_equipment_status(equipment[idx].name, &amp;quot;OK&amp;quot;, &amp;quot;green&amp;quot;);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Reporting Equipment Status===&lt;br /&gt;
If running with the webserver [[mhttpd]], a frontend can send an update to the [[Status Page]], to report on its progress, using the function set_equipment_status() (see above example). This is useful when hardware can take a long time to respond.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function begin_of_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being started&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called every time a run start transition occurs, i.e. at begin-of-run. It allows the updating of user parameters, and the loading/setup/clearing of hardware. At the exit of this function, the acquisition should be armed and ready to test the interrupt (if used), e.g. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT begin_of_run (INT runnumber, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   INT status;&lt;br /&gt;
   status = update_user_parameters(); // update parameters from ODB&lt;br /&gt;
         &amp;lt;br&amp;gt;&lt;br /&gt;
   // Set am to A24 non-privileged Data&lt;br /&gt;
   mvme_set_am(myvme, MVME_AM_A24_ND);&lt;br /&gt;
   // Set dmode to D32&lt;br /&gt;
   mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
                   &amp;lt;br&amp;gt;                            &lt;br /&gt;
   //-------- ADCs -------------------&lt;br /&gt;
   v792_Setup(myvme, VADC0_BASE, 2);&lt;br /&gt;
   v792_ThresholdWrite(myvme, VADC0_BASE,&lt;br /&gt;
       (WORD *)&amp;amp;(ts.v792.threshold1));&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
   csr = v792_CSR1Read(myvme, VADC0_BASE);&lt;br /&gt;
   printf(&amp;quot;Data Ready ADC0: 0x%x\n&amp;quot;, csr);&lt;br /&gt;
   ........&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
   // Reset Latch&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE, 0x1);&lt;br /&gt;
   // Clear pending interrupts&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+8, 0x0);&lt;br /&gt;
   // Enable interrupt&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Accessing variables from the ODB ===&lt;br /&gt;
The  [http://ladd00.triumf.ca/~daqweb/doc/midas/doc/html/group__odbfunctionc.html MIDAS library] contains a number of subroutines to access the ODB.  The ODB keys can easily be accessed from a frontend or other client individually, or by using a saved C structure, which is often used when a large number of keys are to be accessed. &lt;br /&gt;
&lt;br /&gt;
==== Using C structure ====&lt;br /&gt;
&amp;lt;div id=&amp;quot;get_user_parameters&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
The function get_user_parameters() relies on the ODB structure &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/Equipment/Trigger/settings&amp;lt;/span&amp;gt; being mapped in &lt;br /&gt;
[[#Function frontend_init|frontend_init()]] with db_create_record() to ensure that the required ODB structure is present. Then the contents of the ODB keys can be obtained from the C structure (defined in &lt;br /&gt;
[[#Include files|experim.h]]) with a db_get_record() call, as demonstrated in the following example:&lt;br /&gt;
&lt;br /&gt;
 INT get_user_parameters(void)&lt;br /&gt;
 {&lt;br /&gt;
  INT status,size;&lt;br /&gt;
  TRIGGER_SETTINGS ps;  // defined in experim.h&lt;br /&gt;
               &amp;lt;br&amp;gt;&lt;br /&gt;
  /* Get current  settings */&lt;br /&gt;
  size = sizeof(ps);&lt;br /&gt;
  status = db_get_record(hDB, hSet, &amp;amp;ps, &amp;amp;size, 0); // HNDLE hSet obtained in frontend_init()&lt;br /&gt;
  if (status != DB_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      cm_msg(MERROR, &amp;quot;get_exp_settings&amp;quot;, &amp;quot;cannot retrieve trigger settings record (size of ps=%d)&amp;quot;, size);&lt;br /&gt;
      return status;&lt;br /&gt;
    }&lt;br /&gt;
  // Access the required ODB keys in the /equipment/trigger/settings/ subtree from the C structure&lt;br /&gt;
  printf(&amp;quot;Trigger mode = %s \n&amp;quot;, ps.trig_mode); &lt;br /&gt;
  printf(&amp;quot;Trigger delay (ms)= %f \n&amp;quot;,ps.trigger_delay_time__ms_);&lt;br /&gt;
  ...&lt;br /&gt;
  set_trig_delay(ps.trigger_delay_time__ms_);&lt;br /&gt;
  return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
If a number of values in this structure are changed by the frontend, they can be written back into the ODB with one library call, i.e. db_set_record(..).&lt;br /&gt;
 &lt;br /&gt;
==== Accessing individual keys ====&lt;br /&gt;
If experim.h is not included, parameters can be accessed from the ODB using db_get_value() or db_get_data().&lt;br /&gt;
&lt;br /&gt;
   char hostname[20];&lt;br /&gt;
   char str[128];&lt;br /&gt;
   int size,status;&lt;br /&gt;
  /* Get the camp hostname from the mdarc area of odb  */&lt;br /&gt;
  size = sizeof(hostname); &lt;br /&gt;
  sprintf(str,&amp;quot;/Equipment/%s/mdarc/camp/camp hostname&amp;quot;,equipment[FIFO].name); &lt;br /&gt;
  status = db_get_value(hDB, 0, str, hostname, &amp;amp;size, TID_STRING, FALSE);&lt;br /&gt;
  if(status != DB_SUCCESS)&lt;br /&gt;
  {&lt;br /&gt;
    cm_msg(MERROR,&amp;quot;camp_update_params&amp;quot;,&amp;quot;cannot get Camp hostname at %s (%d)&amp;quot;,str,status);&lt;br /&gt;
    return FE_ERR_ODB;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Values can be written into the ODB using db_set_value() or db_set_data().&lt;br /&gt;
&lt;br /&gt;
Note that all the ODB access functions mentioned here can be found in the  [http://ladd00.triumf.ca/~daqweb/doc/midas/doc/html/group__odbfunctionc.html MIDAS library], and more examples can be found in the midas package e.g. under ../midas/examples/ .&lt;br /&gt;
&lt;br /&gt;
==Functions pause/resume_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being paused/resumed.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
These two functions are called upon &amp;quot;Pause&amp;quot; and &amp;quot;Resume&amp;quot; command respectively. Any code relevant to the upcoming run state can be included,e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 INT pause_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   disable_trigger();&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
                            //&lt;br /&gt;
 INT resume_run (INT run_number, char * error)&lt;br /&gt;
 {&lt;br /&gt;
   enable_trigger();&lt;br /&gt;
   inRun = 1;&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Function end_run==&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;run number&#039;&#039;&#039; provides the number of the current run being ended.&lt;br /&gt;
* char * &#039;&#039;&#039;error&#039;&#039;&#039; can be used for returning a message to the system. This message string will be logged into the &#039;&#039;midas.log&#039;&#039;  file (see [[Message System]].&lt;br /&gt;
&lt;br /&gt;
This function is called at every &amp;quot;stop run&amp;quot; transition. It provides the opportunity to disable the hardware, e.g.&lt;br /&gt;
&lt;br /&gt;
 INT end_of_run(INT run_number, char *error)&lt;br /&gt;
 {&lt;br /&gt;
   // Stop DAQ for seting up the parameters&lt;br /&gt;
   vf48_AcqStop(myvme, VF48_BASE);&lt;br /&gt;
                       //&lt;br /&gt;
   done = 0;&lt;br /&gt;
   stop_req = 0;&lt;br /&gt;
   inRun = 0;&lt;br /&gt;
   // Disable interrupt&lt;br /&gt;
   mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
   trig_level = 0;&lt;br /&gt;
   // Close run gate&lt;br /&gt;
   vmeio_AsyncWrite(myvme, VMEIO_BASE, 0x0);&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function frontend_exit==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
The function runs When the frontend program is shut down. Can be used to release any locked resources like memory, communications ports etc. e.g.&lt;br /&gt;
&lt;br /&gt;
  function frontend_exit()&lt;br /&gt;
  {&lt;br /&gt;
     mvme_close(gVme);&lt;br /&gt;
     return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
==Function frontend_loop==&lt;br /&gt;
&lt;br /&gt;
Parameters: none&lt;br /&gt;
&lt;br /&gt;
If the flag frontend_call_loop is set TRUE, this routine is called when the frontend is idle or once between every event. In the following example, it is being used to check for a timeout:&lt;br /&gt;
&lt;br /&gt;
 ...&lt;br /&gt;
 BOOL frontend_call_loop = TRUE;&lt;br /&gt;
 ...&lt;br /&gt;
                                 //&lt;br /&gt;
 INT frontend_loop()&lt;br /&gt;
 {&lt;br /&gt;
   char str[128];&lt;br /&gt;
                   //&lt;br /&gt;
   if (stop_req &amp;amp;&amp;amp; done==0) {&lt;br /&gt;
      db_set_value(hDB,0,&amp;quot;/logger/channels/0/Settings/Event limit&amp;quot;, &amp;amp;evlimit, sizeof(evlimit), 1, TID_DWORD);&lt;br /&gt;
      if (cm_transition(TR_STOP, 0, str, sizeof(str), ASYNC, FALSE) != CM_SUCCESS) {&lt;br /&gt;
         cm_msg(MERROR, &amp;quot;VF48 Timeout&amp;quot;, &amp;quot;cannot stop run: %s&amp;quot;, str);&lt;br /&gt;
      }&lt;br /&gt;
      inRun = 0;&lt;br /&gt;
      // Disable interrupt&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+4, inRun);&lt;br /&gt;
      done = 1;&lt;br /&gt;
      cm_msg(MERROR, &amp;quot;VF48 Timeout&amp;quot;,&amp;quot;VF48 Stop requested&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Event Types and Triggers==&lt;br /&gt;
The frontend supports several different types of [[Frontend Operation#Frontend event triggers|event trigger]]. The event trigger type is specified by the &lt;br /&gt;
[[Equipment Flags|Equipment Flag]]  in the  [[Equipment List Parameters|Equipment Declaration]]. Common event types are &amp;quot;polled events&amp;quot; where the Equipment Flag is  [[Equipment Flags#EQ_POLLED|EQ_POLLED]], &amp;quot;interrupts events&amp;quot; where the Flag is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]], and &amp;quot;periodic events&amp;quot; where the Flag is [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]. The name of the associated readout routine is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type.&lt;br /&gt;
&lt;br /&gt;
Polled  and interrupt events (see [[#Event Types|Event Types]]) require several extra functions to handle the hardware that periodic events do not require. These are described below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function poll_event==&lt;br /&gt;
Parameters:&lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* INT &#039;&#039;&#039;count&#039;&#039;&#039;&lt;br /&gt;
* BOOL &#039;&#039;&#039;test&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment.&lt;br /&gt;
&lt;br /&gt;
In this case, the [[Equipment List Parameters|Equipment declaration]] would have this form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Trigger&amp;quot;,            // equipment name&lt;br /&gt;
        {&lt;br /&gt;
          ...&lt;br /&gt;
          &amp;lt;b&amp;gt;EQ_POLLED&amp;lt;/b&amp;gt;,          // equipment type&lt;br /&gt;
          ...&lt;br /&gt;
          500,                // poll for 500ms &lt;br /&gt;
          ...&lt;br /&gt;
          &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
          read_trigger_event,    // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
         &lt;br /&gt;
The user must provide suitable code in the routine poll_event(), e.g.&lt;br /&gt;
&lt;br /&gt;
 INT poll_event(INT source, INT count, BOOL test)&lt;br /&gt;
 {&lt;br /&gt;
    /* Polling routine for events. Returns TRUE if event  is available. If test equals TRUE, don&#039;t return. &lt;br /&gt;
       The test flag is used to time the polling &lt;br /&gt;
     */&lt;br /&gt;
     int i;&lt;br /&gt;
     int lam = 0;&lt;br /&gt;
                                 //&lt;br /&gt;
     for (i = 0; i &amp;lt; count; i++, lam++) {&lt;br /&gt;
       lam = vmeio_CsrRead(myvme, VMEIO_BASE);&lt;br /&gt;
       if (lam)&lt;br /&gt;
         if (!test)&lt;br /&gt;
           return lam;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Function interrupt_configure==&lt;br /&gt;
* INT &#039;&#039;&#039;cmd&#039;&#039;&#039; &lt;br /&gt;
* INT &#039;&#039;&#039;source&#039;&#039;&#039;&lt;br /&gt;
* PTYPE &#039;&#039;&#039;adr&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the  [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_INTERRUPT, an interrupt configuration routine called interrupt_configure() must be provided by the user.&lt;br /&gt;
The interrupt configuration routine has the following declaration: &lt;br /&gt;
&lt;br /&gt;
 /*-- Interrupt configuration --------------------------*/&lt;br /&gt;
 INT interrupt_configure(INT cmd, INT source, PTYPE adr)&lt;br /&gt;
 {&lt;br /&gt;
  int vec = 0;&lt;br /&gt;
  switch (cmd) &lt;br /&gt;
  {&lt;br /&gt;
    case CMD_INTERRUPT_ENABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x1);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DISABLE:&lt;br /&gt;
      if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x0);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_ATTACH:&lt;br /&gt;
      mvme_set_dmode(myvme, MVME_DMODE_D32);&lt;br /&gt;
      mvme_interrupt_attach(myvme, INT_LEVEL, INT_VECTOR, &lt;br /&gt;
                (void *)adr, &amp;amp;myinfo);&lt;br /&gt;
      mvme_write_value(myvme, VLAM_BASE+0x10, INT_VECTOR);&lt;br /&gt;
      vec = mvme_read_value(myvme, VLAM_BASE+0x10);&lt;br /&gt;
      printf(&amp;quot;Interrupt Attached to 0x%x for vector:0x%x\n&amp;quot;,&lt;br /&gt;
                     adr, vec&amp;amp;0xFF);&lt;br /&gt;
      break;&lt;br /&gt;
                                      //&lt;br /&gt;
    case CMD_INTERRUPT_DETACH:&lt;br /&gt;
      printf(&amp;quot;Interrupt Detach\n&amp;quot;);&lt;br /&gt;
      break;&lt;br /&gt;
   }&lt;br /&gt;
   return SUCCESS;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Under the four commands listed above, the user must implement the hardware operation needed to perform the requested action. In the Midas drivers directory examples can be found of such an interrupt code for CAMAC. See source code such as hyt1331.c,ces8210.c &lt;br /&gt;
&lt;br /&gt;
An [[#Event Readout routine|event readout routine]] must also be provided by the user in the frontend.&lt;br /&gt;
&lt;br /&gt;
==Event Readout routine==&lt;br /&gt;
In the case of [[#Polled and Interrupt Events|POLLED or INTERRUPT events]], the event readout routine is called an [[#Polled or Interrupt readout routine|Interrupt readout routine]].&lt;br /&gt;
&lt;br /&gt;
An event readout routine (called when an event occurs) is usually of the form&lt;br /&gt;
&lt;br /&gt;
 INT function_name ( char *pevent ... )&lt;br /&gt;
 {&lt;br /&gt;
   INT event_size;&lt;br /&gt;
   ........  // read data from hardware&lt;br /&gt;
   ........  // pack into banks depending on format&lt;br /&gt;
   ........&lt;br /&gt;
   return (event_size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
where the first argument of the readout function (pevent)  provides the pointer to the newly constructed event, and points to the first valid location for storing the data.&lt;br /&gt;
;NOTE &lt;br /&gt;
* The return value is the event size, and must be the number of bytes collected in this function.&lt;br /&gt;
* The event serial number will be incremented by one for every call to the readout routine, as long as the returned size is non-zero.&lt;br /&gt;
* If the returned value is set to zero, the event will be dismissed and the serial number to that event will be decremented by one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===General readout function===&lt;br /&gt;
If the Equipment type is EQ_INTERRUPT or EQ_POLLED, see also [[#Polled or Interrupt readout routine|Polled or Interrupt readout routine]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the case of a &#039;&#039;&#039;periodic&#039;&#039;&#039; event, the Equipment declaration may have this form:&lt;br /&gt;
&lt;br /&gt;
    EQUIPMENT equipment[] = {&lt;br /&gt;
      { &amp;quot;Scaler&amp;quot;,           // equipment name&lt;br /&gt;
         {    &lt;br /&gt;
            ...&lt;br /&gt;
            EQ_PERIODIC     // equipment type&lt;br /&gt;
            0,              // interrupt source (ignored) &lt;br /&gt;
            &amp;quot;MIDAS&amp;quot;,        // event format&lt;br /&gt;
            ...&lt;br /&gt;
            10000,          // period (read every 10s)&lt;br /&gt;
            ...&lt;br /&gt;
            &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,},&lt;br /&gt;
       read_scaler_event,   // readout routine &lt;br /&gt;
       ...&lt;br /&gt;
&lt;br /&gt;
A readout event will often need to send out data. This is done using one of the supported [[Event Structure|event structures]] usually [[Event Structure#MIDAS Format Event|&amp;quot;MIDAS&amp;quot; format]] data banks.  The bank format (&amp;quot;MIDAS&amp;quot; in the example above) is declared in the [[Equipment List Parameters]]   [[Equipment List Parameters#Format|Format]] field.&lt;br /&gt;
&lt;br /&gt;
An example of a scaler readout routine read_scaler_event() where the data is read out into [[MIDAS Event Construction|MIDAS data banks]] is shown below. &lt;br /&gt;
&lt;br /&gt;
 INT read_scaler_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
    DWORD *pdata, a;&lt;br /&gt;
                               //&lt;br /&gt;
    /* init bank structure; */&lt;br /&gt;
    bk_init(pevent);&lt;br /&gt;
                               //&lt;br /&gt;
    /* create SCLR bank */&lt;br /&gt;
    bk_create(pevent, &amp;quot;SCLR&amp;quot;, TID_DWORD, &amp;amp;pdata);&lt;br /&gt;
                               //&lt;br /&gt;
    /* read scaler bank (CAMAC) */&lt;br /&gt;
    for (a = 0; a &amp;lt; N_SCLR; a++)&lt;br /&gt;
       cam24i(CRATE, SLOT_SCLR, a, 0, pdata++);&lt;br /&gt;
                               //    &lt;br /&gt;
    /* close SCLR bank */&lt;br /&gt;
    bk_close(pevent, pdata);&lt;br /&gt;
                               //&lt;br /&gt;
    /* return event size in bytes */&lt;br /&gt;
    return bk_size(pevent);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Some other examples of event readout routines in this document are&lt;br /&gt;
* [[Event Structure#FIXED Event Construction|FIXED Format event construction]]&lt;br /&gt;
* [[#Polled or Interrupt readout routine|Polled or Interrupt readout routines]]&lt;br /&gt;
* [[Super Event]] construction&lt;br /&gt;
* [[Slow Control System|Slow Control]]&lt;br /&gt;
&lt;br /&gt;
Many other examples of readout routines can be found in the [[#Frontend Templates|frontend templates]] in the MIDAS package.&lt;br /&gt;
&lt;br /&gt;
===Polled or Interrupt readout routine===&lt;br /&gt;
In the case of a [[#Polled and Interrupt Events|Polled or Interrupt event]], the content of the memory location pointed to by &#039;&#039;&#039;pevent&#039;&#039;&#039; (see [[#Event Readout routine|Event Readout routine]]) prior to its use in the readout function, contains the interrupt source bitwise register. This feature can be exploited in order to identify which hardware module has triggered the readout when multiple interrupts have been assigned to the same readout function.&lt;br /&gt;
&lt;br /&gt;
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism.&lt;br /&gt;
The  [[Equipment List Parameters|Equipment declaration]] is of the form: &lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
               //&lt;br /&gt;
    {&amp;quot;Trigger&amp;quot;,  /* equipment name */&lt;br /&gt;
       ...&lt;br /&gt;
 #ifdef USE_INT&lt;br /&gt;
       EQ_INTERRUPT, /* equipment type */&lt;br /&gt;
 #else&lt;br /&gt;
       EQ_POLLED,    /* equipment type */&lt;br /&gt;
 #endif&lt;br /&gt;
   /* interrupt source: crate 0, all stations */&lt;br /&gt;
       LAM_SOURCE(0, 0x0),&lt;br /&gt;
       ....&lt;br /&gt;
       &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,&lt;br /&gt;
     },&lt;br /&gt;
     read_trigger_event, /* readout routine */&lt;br /&gt;
     NULL, NULL,&lt;br /&gt;
     trigger_bank_list,&lt;br /&gt;
 }&lt;br /&gt;
  &lt;br /&gt;
Note that the [[Macros#CAMAC Macros|LAM_SOURCE macro]] simply codes the parameters into a bitwise register.&lt;br /&gt;
&lt;br /&gt;
The readout routine would contains code such as&lt;br /&gt;
&lt;br /&gt;
 INT read_trigger_event(char *pevent, INT off)&lt;br /&gt;
 {&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   DWORD  *pdata;&lt;br /&gt;
 #endif&lt;br /&gt;
                        //&lt;br /&gt;
 #if defined VADC0_CODE&lt;br /&gt;
   /* read ADC0 data */&lt;br /&gt;
   v792_EvtCntRead(myvme, VADC0_BASE, &amp;amp;evtcnt);&lt;br /&gt;
   ........&lt;br /&gt;
   /* Read Event */&lt;br /&gt;
   v792_EventRead(myvme, VADC0_BASE, pdata, &amp;amp;nentry);&lt;br /&gt;
   ........&lt;br /&gt;
   v792_DataClear(myvme, VADC0_BASE);&lt;br /&gt;
 #endif&lt;br /&gt;
                       //&lt;br /&gt;
   ........&lt;br /&gt;
   return (size);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The data will be packed into banks as described for the [[General readout function|general readout function]] above.&lt;br /&gt;
The examples fevmemodules.c (VME) and frontend.c (CAMAC) (see [[#Frontend Templates]] contain a complete example of read_trigger_event().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Manual Trigger==&lt;br /&gt;
Another type of [[Frontend Operation#Frontend event trigger|frontend event trigger]] supported is the &amp;quot;manual trigger&amp;quot;,  where the Equipment Flag is [[Equipment Flags#EQ_MANUAL_TRIGGER|EQ_MANUAL_TRIGGER]]. This flag means that an event can be triggered through the URL &amp;lt;code&amp;gt;?cmd=Trigger/&amp;lt;equipment_name&amp;gt;&amp;lt;/code&amp;gt; (e.g. &amp;lt;code&amp;gt;http://my.host:8080/?cmd=Trigger/MyEquipmentName&amp;lt;/code&amp;gt;). If you add this URL to the [[/Alias ODB tree|/Alias]] part of the ODB, then a link will appear on midas webpages that can be clicked to trigger an event.&lt;br /&gt;
&lt;br /&gt;
In some cases, the same readout code may be used for two types of event: a manual trigger and (say) a poll event. It is possible to determine whether the readout of an event was triggered by a manual trigger or a regular trigger by adding a call to the  [[MIDAS Event Header Macros|event header Macro]] DATA_SIZE in the readout routine:&lt;br /&gt;
&lt;br /&gt;
  flag = DATA_SIZE(pevent);&lt;br /&gt;
&lt;br /&gt;
If the result is&lt;br /&gt;
  *  flag = 0 normal call&lt;br /&gt;
  *  flag = 1 manual trigger&lt;br /&gt;
&lt;br /&gt;
It is also possible for a backend MIDAS client (such as an analyzer or custom data archiver) to trigger a manual trigger event. The client controls when an event is sent by means of&lt;br /&gt;
a  function that requests an event by triggering the event sending mechanism with a RPC call.&lt;br /&gt;
&lt;br /&gt;
With a frontend Equipment declaration of a manually triggered event of the form:&lt;br /&gt;
    &lt;br /&gt;
    { &amp;quot;Histo&amp;quot;,             /* equipment name */&lt;br /&gt;
       2, 0,                 /* event ID, trigger mask */&lt;br /&gt;
       &amp;quot;SYSTEM&amp;quot;,             /* event buffer */&lt;br /&gt;
       EQ_MANUAL_TRIG,     /* equipment type */&lt;br /&gt;
       0,    &lt;br /&gt;
       .......&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
the code fragment to manually trigger this event is:&lt;br /&gt;
&lt;br /&gt;
  int main(unsigned int argc,char **argv)&lt;br /&gt;
  {&lt;br /&gt;
      .......&lt;br /&gt;
      bm_request_event(hBufEvent, 2, TRIGGER_ALL, GET_ALL, &amp;amp;request_id, process_event_TD);&lt;br /&gt;
      .......&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
When it is time to save the data during the run, the function below is called:&lt;br /&gt;
&lt;br /&gt;
 BOOL trigger_histo_event(void)&lt;br /&gt;
 {&lt;br /&gt;
   HNDLE hconn;&lt;br /&gt;
   BOOL event_triggered;&lt;br /&gt;
                            //&lt;br /&gt;
   event_triggered = FALSE;&lt;br /&gt;
   ...................&lt;br /&gt;
   if (run_state == STATE_RUNNING) {   // Check the frontend client exists&lt;br /&gt;
        if( cm_exist(ClientName,TRUE))  {    &lt;br /&gt;
            status = cm_connect_client (ClientName, &amp;amp;hconn);&lt;br /&gt;
            if(status != RPC_SUCCESS)&lt;br /&gt;
                cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Cannot connect to frontend \&amp;quot;%s\&amp;quot; (%d)&amp;quot;,&lt;br /&gt;
                       ClientName,status);&lt;br /&gt;
            else  {     // successfully connected to frontend client&lt;br /&gt;
                status = rpc_client_call(hconn, RPC_MANUAL_TRIG, 2); // trigger a histo event&lt;br /&gt;
                if (status != CM_SUCCESS)&lt;br /&gt;
                    cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error triggering event from frontend (%d)&amp;quot;,status);&lt;br /&gt;
                else {  // successfully triggered event&lt;br /&gt;
                    event_triggered=TRUE;&lt;br /&gt;
                    status =cm_disconnect_client(hconn, FALSE);&lt;br /&gt;
                    if (status != CM_SUCCESS)&lt;br /&gt;
                        cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Error disconnecting client (%d)&amp;quot;,status);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            cm_msg(MERROR,&amp;quot;trigger_histo_event&amp;quot;,&amp;quot;Frontend client %s not running (%d)&amp;quot;,&lt;br /&gt;
                   ClientName,status);&lt;br /&gt;
    } &lt;br /&gt;
    return(event_triggered);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Deferred Transition==&lt;br /&gt;
This option permits the user to postpone any transition issued by any requester&lt;br /&gt;
until some condition is satisfied.&lt;br /&gt;
For example:&lt;br /&gt;
* It may not be advisable to pause or stop a run until some hardware has turned off a particular valve. &lt;br /&gt;
* The start of the acquisition system should be postponed until the beam rate has been stable for a given period of time.&lt;br /&gt;
* While active, a particular acquisition system should not be interrupted until the &amp;quot;cycle&amp;quot; is completed.&lt;br /&gt;
&lt;br /&gt;
In these examples, any application having access to the state of the hardware can register to be a &amp;quot;transition Deferred&amp;quot; client. The MIDAS system will then catch any transition request and postpone the trigger of such a transition until the condition  is satisfied.&lt;br /&gt;
&lt;br /&gt;
The Deferred transition requires 3 steps for setup&lt;br /&gt;
# Register for the deferred transition &lt;br /&gt;
# Provide a callback function to serve the deferred transition &lt;br /&gt;
# Implement the condition code &lt;br /&gt;
&lt;br /&gt;
The following example demonstrates this process:&lt;br /&gt;
&lt;br /&gt;
  BOOL transition_PS_requested=FALSE; // global&lt;br /&gt;
                                              //&lt;br /&gt;
  INT frontend_init()&lt;br /&gt;
  {&lt;br /&gt;
    // register for deferred transition&lt;br /&gt;
                                              //&lt;br /&gt;
    cm_register_deferred_transition(TR_STOP, wait_end_cycle);&lt;br /&gt;
    cm_register_deferred_transition(TR_PAUSE, wait_end_cycle);&lt;br /&gt;
    ...  &lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */ &lt;br /&gt;
  //-- Deferred transition callback&lt;br /&gt;
  BOOL wait_end_cycle(int transition, BOOL first)&lt;br /&gt;
  {&lt;br /&gt;
    if (first) {&lt;br /&gt;
      transition_PS_requested = TRUE;&lt;br /&gt;
      return FALSE;&lt;br /&gt;
    }&lt;br /&gt;
                        //&lt;br /&gt;
    if (end_of_mcs_cycle){&lt;br /&gt;
      transition_PS_requested = FALSE;&lt;br /&gt;
      end_of_mcs_cycle = FALSE;&lt;br /&gt;
      return TRUE;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
      return FALSE;&lt;br /&gt;
  }&lt;br /&gt;
 /*&lt;br /&gt;
 */&lt;br /&gt;
  INT read_mcs_event(char *pevent, INT offset)&lt;br /&gt;
  {  &lt;br /&gt;
      ...     // read out data at end of cycle&lt;br /&gt;
      ...&lt;br /&gt;
      end_of_mcs_cycle = TRUE; // end of cycle &lt;br /&gt;
                               //&lt;br /&gt;
      if (!transition_PS_requested)&lt;br /&gt;
         start_cycle(); // start a new cycle &lt;br /&gt;
      return bk_size(pevent);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the example above, &lt;br /&gt;
&lt;br /&gt;
* The frontend code is registered for PAUSE and STOP using &#039;&#039;cm_register_deferred_transition()&#039;&#039;. The second argument &#039;&#039;wait_end_cycle&#039;&#039;  is the declaration of the callback function. &lt;br /&gt;
* The callback function &#039;&#039;wait_end_cycle&#039;&#039; will be called as soon as the transition is requested with the Boolean flag &#039;&#039;first&#039;&#039; set to TRUE.&lt;br /&gt;
* By setting &#039;&#039;transition_PS_requested&#039;&#039;  TRUE , the user will be provided with the acknowledgment of the transition request.&lt;br /&gt;
* By returning FALSE from the callback, the transition is prevented from occurring. &lt;br /&gt;
* As soon as the user condition is satisfied (&#039;&#039;end_of_mcs_cycle&#039;&#039; = TRUE), the return code in the  callback will be set to TRUE and the requested transition will be issued.&lt;br /&gt;
&lt;br /&gt;
While the transition is Deferred, the odb key  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; will contain the transition code, and the d [[mhttpd]] webserver main status page will indicate that a deferred transition is in progress.&lt;br /&gt;
&lt;br /&gt;
Once in deferred state, an [[odbedit]] override command can be issued to force the transition to happen,&lt;br /&gt;
&lt;br /&gt;
 &amp;gt;odbedit&lt;br /&gt;
 odb&amp;gt; stop now    (or &amp;quot;start now&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
or  &amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/runinfo/Requested transition&amp;lt;/span&amp;gt; can be set to 0. The transition will then take place on the next stop or start command.&lt;br /&gt;
&lt;br /&gt;
= Compilation using CMake =&lt;br /&gt;
&lt;br /&gt;
Here is a &amp;quot;minimal&amp;quot; CMakeLists.txt file that can be used to compile a user-written frontend (called &amp;quot;myfe&amp;quot; in this case) that uses the mfe framework.&lt;br /&gt;
&lt;br /&gt;
  cmake_minimum_required(VERSION 3.0)&lt;br /&gt;
  project(myfe)&lt;br /&gt;
  &lt;br /&gt;
  # Check for MIDASSYS environment variable&lt;br /&gt;
  if (NOT DEFINED ENV{MIDASSYS})&lt;br /&gt;
    message(SEND_ERROR &amp;quot;MIDASSYS environment variable not defined.&amp;quot;)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  set(CMAKE_CXX_STANDARD 11)&lt;br /&gt;
  set(MIDASSYS $ENV{MIDASSYS})&lt;br /&gt;
  &lt;br /&gt;
  if (${CMAKE_SYSTEM_NAME} MATCHES Linux)&lt;br /&gt;
    set(LIBS -lpthread -lutil -lrt)&lt;br /&gt;
  endif()&lt;br /&gt;
  &lt;br /&gt;
  # Define the executable to be built, and the source code files&lt;br /&gt;
  add_executable(myfe.exe myfe.cxx)&lt;br /&gt;
  &lt;br /&gt;
  # Directories to search for &lt;br /&gt;
  target_include_directories(myfe.exe PRIVATE ${MIDASSYS}/include)&lt;br /&gt;
  &lt;br /&gt;
  # Libraries to link to&lt;br /&gt;
  target_link_libraries(myfe.exe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})&lt;br /&gt;
&lt;br /&gt;
One could then build the executable using the folllowing commands:&lt;br /&gt;
&lt;br /&gt;
  mkdir build&lt;br /&gt;
  cd build&lt;br /&gt;
  cmake ..&lt;br /&gt;
  make&lt;br /&gt;
&lt;br /&gt;
Some older operating systems may require the &amp;quot;cmake3&amp;quot; command to be used instead of &amp;quot;cmake&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_Application&amp;diff=3410</id>
		<title>Frontend Application</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Frontend_Application&amp;diff=3410"/>
		<updated>2024-01-05T16:09:26Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Arguments */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
== Links ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend Operation#Frontend|Frontend Operation]]&lt;br /&gt;
* [[Frontend user code]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Frontend Task ==&lt;br /&gt;
The purpose of a &amp;lt;span style=&amp;quot;color: green; font-style:italic; &amp;quot;&amp;gt;&#039;&#039;frontend task&#039;&#039;&amp;lt;/span&amp;gt; is to collect data from the hardware and transmit this information to a central place where data logging and &lt;br /&gt;
analysis can be performed. &lt;br /&gt;
&lt;br /&gt;
A  &amp;lt;span style=&amp;quot;color: green; font-style:italic; &amp;quot;&amp;gt;&#039;&#039;frontend task&#039;&#039;&amp;lt;/span&amp;gt; is built by the user&lt;br /&gt;
(see [[Frontend Operation#Frontend|Frontend Operation]] ) and consists of as &amp;quot;user part&amp;quot; and a &amp;quot;system framework&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Arguments ==&lt;br /&gt;
The system framework code (mfe.c) has the following arguments:&lt;br /&gt;
&lt;br /&gt;
  [-h hostname ] : host name (see [[Common Parameters to MIDAS Utilities]])&lt;br /&gt;
  [-e exptname ] : experiment name (see [[Common Parameters to MIDAS Utilities]])&lt;br /&gt;
  [-D ] : Become a Daemon.&lt;br /&gt;
  [-O ] : Become a Daemon but keep stdout &lt;br /&gt;
  [-d ] : Used for debugging the frontend&lt;br /&gt;
  [-i index] : Set frontend index so you can have [[Equipment_List_Parameters#Equipment_Name|multiple instances of the same executable running with different names]] (can be used with [[Mevb]] / [[Event Builder#Principle of the Event Builder and related frontend fragment|event builder]] or standalone).&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Start the application as a daemon, using the default host, experiment and port :&lt;br /&gt;
&lt;br /&gt;
 e.g. fevme -D&lt;br /&gt;
&lt;br /&gt;
Start the application superceding the default host, experiment and port:&lt;br /&gt;
&lt;br /&gt;
 e.g. fegpib -e exp218 -h isdaq10 -p 7077 -D&lt;br /&gt;
&lt;br /&gt;
[[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Equipment_List_Parameters&amp;diff=3409</id>
		<title>Equipment List Parameters</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Equipment_List_Parameters&amp;diff=3409"/>
		<updated>2024-01-05T15:19:01Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Equipment Name */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend user code]] &lt;br /&gt;
* [[Frontend Operation]]&lt;br /&gt;
* [[Equipment Flags]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Equipment List ==&lt;br /&gt;
The &#039;&#039;Equipment List&#039;&#039; is a declaration of the equipment(s) in a frontend using the &#039;&#039;EQUIPMENT structure&#039;&#039; defined in &#039;&#039;midas.h&#039;&#039; in the MIDAS package. &lt;br /&gt;
&lt;br /&gt;
Only those parameters that are needed by the frontend to define the equipments are included (see [[#Example Equipment List Declaration|example]] below). The contents of these parameters determine the properties of each equipment. More examples of equipment declarations can be found in the ..midas/examples subdirectories in the MIDAS package. &lt;br /&gt;
&lt;br /&gt;
When the frontend is first run, it will create (using &#039;&#039;db_create_record&#039;&#039;) in the ODB [[/Equipment ODB tree]]  (if they do not already exist) the &#039;&#039;&amp;lt;equipment-name&amp;gt;&#039;&#039; subdirectories for all equipments defined by the frontend.   The initial values of many of the parameters in the ODB [[/Equipment ODB tree#Common subtree|/Equipment/&#039;&#039;&amp;lt;equipment-name&amp;gt;&#039;&#039;/Common subtree]] are taken from the corresponding Equipment List.&lt;br /&gt;
&lt;br /&gt;
The parameters found in a frontend Equipment List is described below under [[#Explanation of Equipment List parameters|Explanation of Equipment List parameters]].&lt;br /&gt;
&lt;br /&gt;
===Example Equipment List Declaration===&lt;br /&gt;
&lt;br /&gt;
An example of an Equipment List declaration in a frontend is shown below. Two Equipments have been defined.&lt;br /&gt;
&lt;br /&gt;
  /*-- Equipment list -----------------------------------------------&lt;br /&gt;
  */                                 &lt;br /&gt;
  EQUIPMENT equipment[] = {   // start of EQUIPMENT structure&lt;br /&gt;
        // &lt;br /&gt;
        { &amp;quot;Trigger&amp;quot;,                       // equipment name&lt;br /&gt;
          {  1,                            // event ID&lt;br /&gt;
             0,                            // trigger mask&lt;br /&gt;
             &amp;quot;SYSTEM&amp;quot;,                     // event buffer&lt;br /&gt;
 #ifdef USE_INT&lt;br /&gt;
             EQ_INTERRUPT,                 // equipment type &lt;br /&gt;
 #else&lt;br /&gt;
             EQ_POLLED,                    // equipment type &lt;br /&gt;
 #endif&lt;br /&gt;
             LAM_SOURCE(0, 0xFFFFFF),      // event source crate 0, all stations&lt;br /&gt;
             &amp;quot;MIDAS&amp;quot;,                      // format&lt;br /&gt;
             TRUE,                         // enabled &lt;br /&gt;
             RO_RUNNING | RO_ODB           // read when running and update ODB&lt;br /&gt;
             500,                          // poll for 500ms  &lt;br /&gt;
             0,                            // stop run after this event limit &lt;br /&gt;
             0,                            // number of sub events &lt;br /&gt;
             0,                            // log history &lt;br /&gt;
             &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,  &amp;quot;&amp;quot;,                  // Reserved&lt;br /&gt;
          },                        &lt;br /&gt;
          read_trigger_event,           // Readout Routine    &lt;br /&gt;
          NULL,                         // class driver routine&lt;br /&gt;
          NULL,                         // device driver list&lt;br /&gt;
          NULL,                         // bank list/init string&lt;br /&gt;
        },&lt;br /&gt;
        { &amp;quot;Scaler&amp;quot;,                       // equipment name&lt;br /&gt;
          { 2,                            // event ID&lt;br /&gt;
            0,                            // trigger mask&lt;br /&gt;
            &amp;quot;SYSTEM&amp;quot;,                     // buffer&lt;br /&gt;
            EQ_PERIODIC | EQ_MANUAL_TRIG, // equipment type &lt;br /&gt;
            0,                            // event source&lt;br /&gt;
            &amp;quot;MIDAS&amp;quot;,                      // format&lt;br /&gt;
            TRUE,                         // enabled &lt;br /&gt;
            RO_RUNNING | RO_TRANSITIONS | // read when running and on transitions       &lt;br /&gt;
            RO_ODB,                       // and update ODB &lt;br /&gt;
            10000,                        // read every 10 sec&lt;br /&gt;
            0,                            // stop run after this event limit &lt;br /&gt;
            0,                            // number of sub events &lt;br /&gt;
            0,                            // log history&lt;br /&gt;
            &amp;quot;&amp;quot;, &amp;quot;&amp;quot;,  &amp;quot;&amp;quot;,                  // Reserved&lt;br /&gt;
          },                         &lt;br /&gt;
          read_scaler_event,              // readout routine &lt;br /&gt;
          NULL,                           // class driver routine&lt;br /&gt;
          NULL,                           // device driver list&lt;br /&gt;
          NULL,                           // bank list/init string&lt;br /&gt;
        },&lt;br /&gt;
      {&amp;quot;&amp;quot;}&lt;br /&gt;
    };              // end of EQUIPMENT structure&lt;br /&gt;
&lt;br /&gt;
== Explanation of Equipment List parameters ==&lt;br /&gt;
===Equipment Name===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] defines the name of the equipment. Each equipment name must be unique (i.e. no two equipments in the same experiment may have the same name). The name will be the reference name of the equipment generating the event. Examples of typical Equipment Names are &amp;quot;Trigger&amp;quot;,&amp;quot;Scaler&amp;quot;,&amp;quot;Epics&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
As of October 2019, you may use the string &amp;lt;code&amp;gt;${HOSTNAME}&amp;lt;/code&amp;gt; in your equipment name, and the frontend framework will automatically substitute the name of your machine.&lt;br /&gt;
&lt;br /&gt;
You may also use a format specifier like &amp;lt;code&amp;gt;%i&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;%02d&amp;lt;/code&amp;gt; in the equipment name. This will be replaced by the &amp;quot;frontend index&amp;quot;, which is specified either by the [[MIDAS_environment_variables#MIDAS_FRONTEND_INDEX|MIDAS_FRONTEND_INDEX]] environment variable or the &amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt; command-line flag when starting the frontend. This allows you to run multiple instances of the same frontend executable, each with a different name and ODB directory. For example, if your equipment name is &amp;lt;code&amp;gt;CRATE%02i&amp;lt;/code&amp;gt; and the frontend_index is 7, the corresponding MIDAS equipment name will be &amp;lt;code&amp;gt;CRATE07&amp;lt;/code&amp;gt; with settings in the ODB at &amp;lt;code&amp;gt;/Equipment/CRATE07/Common&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
&lt;br /&gt;
===EventID===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] defines the event ID for this equipment. Each equipment has to be associated with a  unique event ID . The event ID will be part of the event header of that particular equipment. &lt;br /&gt;
&lt;br /&gt;
The EventID (and the [[#TriggerMask]]) can be used to sort events into different streams, e.g. in  [[Event Builder|Event Building]] or [[Event Filtering]].&lt;br /&gt;
&lt;br /&gt;
===TriggerMask===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] can be used to sort events using a unique TriggerMask. It may be used when the &lt;br /&gt;
[[#Type|Equipment Type]] is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]] or [[Equipment Flags#EQ_MULTITHREAD|EQ_MULTITHREAD]] and/or&lt;br /&gt;
in [[Event Builder|Event Building]] and [[Event Filtering]] to sort the events.&lt;br /&gt;
 &lt;br /&gt;
When in use,  each equipment is associated with a unique TriggerMask.  The TriggerMask can be modified dynamically by the [[#Readout Routine|Readout Routine]] e.g. to define a  [[Super Event|sub-event type]] on an event-by-event basis. This can be used to mix &amp;quot;physics events&amp;quot; (from a physics trigger) and &amp;quot;calibration events&amp;quot; (from a clock for example) in one run and identify them later. TriggerMasks are declared as 16-bit values. If the TriggerMask is used in a single bit-wise mode, 16 is the maximum number of different masks available.  &lt;br /&gt;
&lt;br /&gt;
Set TriggerMask to 0 if not used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;MirroredInODB&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
===Buffer===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] specifies the name of the Buffer to which the event will be sent. After composition of an equipment, the Midas  [[Frontend Operation#Frontend|Frontend]] &#039;&#039;mfe.c&#039;&#039;&lt;br /&gt;
takes over the sending of this event to the buffer (usually the SYSTEM buffer) on the back-end computer  where it may be sent to the data logger [[mlogger|mlogger]]) and the [[analyser]].&lt;br /&gt;
&lt;br /&gt;
*  If this field is left empty, (i.e. set to &amp;quot;&amp;quot;), the readout function associated with that equipment will still be performed, but the  actual event won&#039;t be sent to the buffer. Instead, that particular equipment can be mirrored in the ODB if the [[#RO_ODB|RO_ODB]] flag is turned on (see [[example]]).  The ODB is updated with a new event approximately every second. Note that this feature is generally used only for testing or monitoring, as writing large amounts of data to the ODB takes time. &lt;br /&gt;
* By using a buffer other than the SYSTEM buffer,  [[Event Filtering]] can be implemented. &lt;br /&gt;
* If using an [[Event Builder]] ( i.e. a secondary stage on the back-end to collect and assemble events coming from different buffers in order to compose a larger event) , a dedicated buffer can be specified.  In this case the [[#EQ_EB|EQ_EB]] flag must be turned on, and the events coming from the  [[Frontend Operation#Frontend|Frontend]] are called  &#039;&#039;fragments&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Type===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] specifies the type of equipment, and should be set to one of the Equipment Flags EQ_XXX selected from the [[Equipment Flags|list of valid Equipment Flags]], i.e. one of&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:4;-moz-column-count:4;-webkit-column-count:4&amp;quot;&amp;gt;&lt;br /&gt;
* [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]&lt;br /&gt;
* [[Equipment Flags#EQ_POLLED|EQ_POLLED]]&lt;br /&gt;
* [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]]&lt;br /&gt;
* [[Equipment Flags#EQ_MULTITHREAD|EQ_MULTITHREAD]]&lt;br /&gt;
* [[Equipment Flags#EQ_SLOW|EQ_SLOW]]&lt;br /&gt;
* [[Equipment Flags#EQ_MANUAL_TRIG|EQ_MANUAL_TRIG]]&lt;br /&gt;
* [[Equipment Flags#EQ_EB|EQ_EB]]&lt;br /&gt;
* [[Equipment Flags#EQ_FRAGMENTED|EQ_FRAGMENTED]] &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Source===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]]  (i.e. the  interrupt or event source) is only used if the equipment [[#Type|Type]] is set to  [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]] or [[Equipment Flags#EQ_MULTITHREAD|EQ_MULTITHREAD]]. The interrupt Source field is a bit-wise pattern representing the interrupt that is allowed to trigger the call to the  [[#Readout Routine|Readout Routine]]. The   [[Frontend user code#Polled or Interrupt readout routine|example]]  shows a CAMAC or VME source for the interrupt. A MACRO (LAM_SOURCE) calculates the bit pattern from the parameters (Crate,Slot). &lt;br /&gt;
&lt;br /&gt;
If not using an interrupt, this field is set to 1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Format===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] specifies the data format used for generating the event. Only [[Event Structure#MIDAS Format Event|&amp;quot;MIDAS&amp;quot;]] and  [[Event Structure#FIXED Format Event|&amp;quot;FIXED&amp;quot;]] formats are valid in the frontend. The format must agree with the way the event is composed in the equipment [[#Readout Routine|Readout Routine]].  It tells the system how to interpret an event when it is copied to the ODB or displayed in a user-readable form. Data in MIDAS and/or FIXED format from the frontend can be saved by the MIDAS data logger [[Mlogger|mlogger]] in a number of formats (see   [[Keys in the ODB /Logger/Channels tree #/Logger/Channels/0/Settings/Format | mlogger format]] ).&lt;br /&gt;
&#039;&#039;&#039;NOTE&#039;&#039;&#039;&lt;br /&gt;
*MIDAS or FIXED data format can be mixed at the frontend level, but &amp;lt;span style=&amp;quot;color:darkcyan;&amp;quot;&amp;gt;&#039;&#039;mlogger&#039;&#039;&amp;lt;/span&amp;gt; is not able to handle this format diversity on a event-by-event basis. &lt;br /&gt;
: In practice, the data format (MIDAS/FIXED) should be identical throughout one equipment definition. Different equipments can, of course, have different formats in the same frontend.&lt;br /&gt;
* ROOT format cannot be specified in the equipment definition. To save raw data in ROOT format,  the [[#Bank Definition|Bank Definition]] must be supplied, and a conversion to ROOT format is done by the [[Keys in the ODB /Logger/Channels tree #/Logger/Channels/0/Settings/Format | data logger]] ).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Enabled===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] is the enable switch for the equipment. Only when enabled (i.e. TRUE) is this equipment active.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===ReadOn===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] specifies when the read-out of an event occurs or is enabled (i.e. on which transition state(s) and/or on which run state(s) respectively) by using a combination of &lt;br /&gt;
[[ReadOn Flags]].&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:5;-moz-column-count:5;-webkit-column-count:5&amp;quot;&amp;gt;&lt;br /&gt;
* [[ReadOn Flags|RO_RUNNING]]&lt;br /&gt;
* [[ReadOn Flags|RO_STOPPED ]]&lt;br /&gt;
* [[ReadOn Flags|RO_PAUSED ]]&lt;br /&gt;
* [[ReadOn Flags|RO_BOR ]]&lt;br /&gt;
* [[ReadOn Flags|RO_EOR]] &lt;br /&gt;
* [[ReadOn Flags|RO_PAUSE]] &lt;br /&gt;
* [[ReadOn Flags|RO_RESUME ]]&lt;br /&gt;
* [[ReadOn Flags|RO_TRANSITIONS]] &lt;br /&gt;
* [[ReadOn Flags|RO_ALWAYS ]]&lt;br /&gt;
* [[ReadOn Flags|RO_ODB]] &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
;NOTE : These flags can be combined with the logical OR operator,e.g. RO_RUNNING | RO_TRANSITIONS means that the event is read out when&lt;br /&gt;
running and additionally on all transitions.&lt;br /&gt;
&lt;br /&gt;
===Period===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] specifies the time interval for [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]] equipment or time out value in the case of [[Equipment Flags#EQ_POLLED|EQ_POLLED]] or [[Equipment Flags#EQ_MULTITHREAD|EQ_MULTITHREAD]] equipments (units are milliseconds).&lt;br /&gt;
&lt;br /&gt;
===Event limit===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] specifies the number of events to be taken prior to forcing an End-of-Run  [[Run States and Transitions#transition|Transition]]. The value 0 disables this option.&lt;br /&gt;
&lt;br /&gt;
===Number of subevents===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] enables the [[Super Event]] capability if &amp;amp;gt; 0 . Specifies the maximum number of sub-events in the [[Super Event]]. (Not applicable to [[Event Structure#FIXED Format Event|FIXED format]] events.)&lt;br /&gt;
&lt;br /&gt;
===Log History===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] enables/disables the [[History System]] for that equipment. The value (positive in seconds) also controls how frequently the history events are generated. A reasonable value to set for the History value is &amp;quot;60&amp;quot;, so that the history events are generated once per minute.&lt;br /&gt;
A positive value enables history logging, in which case the event data will also be sent automatically to the ODB in the &lt;br /&gt;
&amp;lt;span style=&amp;quot;color: purple; font-style:italic;&amp;quot;&amp;gt;/equipment/&amp;lt;equipment-name&amp;gt;/variables&amp;lt;/span&amp;gt; tree. &lt;br /&gt;
The frequency of ODB writes is limited by ODB_UPDATE_TIME (1/sec in mfe.c rev 4298).&lt;br /&gt;
To disable history logging of this event, set this parameter to 0. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;EventTrigger&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;div id=&amp;quot;ManualTriggerMode&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Readout Routine===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]]  contains the name of the user-written function to be called when the event is triggered. The name of this routine is specified by the user. This Readout Routine must be present in the frontend code. It will be called at an &lt;br /&gt;
event trigger, which will occur under one of these conditions depending on the [[Equipment List Parameters#Type|Type]] parameter.&lt;br /&gt;
&lt;br /&gt;
;polled mode : the poll_event() routine has detected a trigger request while polling on a trigger source.&lt;br /&gt;
;interrupt mode : an interrupt has occurred whose source is pre-defined in the interrupt_configure() routine.&lt;br /&gt;
;periodic modes : trigger occurs periodically at a time interval specified by the [[Equipment List Parameters#Period|Period]] parameter.&lt;br /&gt;
;manual trigger mode : trigger occurs when &lt;br /&gt;
:: 1. the  manual trigger button is pressed on the web interface or&lt;br /&gt;
:: 2. a client requests an event by triggering the event via RPC.&lt;br /&gt;
&lt;br /&gt;
See [[Frontend user code]] for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Class Driver===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] must be present for all Equipments. This parameter is only for use by [[Slow Control System|Slow Control]] Equipments, i.e. when the  [[Equipment List Parameters#Type|Equipment Type]] is set to  [[Equipment Flags#EQ_SLOW|EQ_SLOW]]. For all other Types, this field is set to NULL.&lt;br /&gt;
&lt;br /&gt;
For [[Slow Control System|Slow Control]]  Equipments, this parameter is set to the main routine for the Class Driver of the hardware. See also [[#Device Driver List|Device Driver List]].&lt;br /&gt;
&lt;br /&gt;
===Device Driver List===&lt;br /&gt;
&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] must be present for all Equipments. This parameter is only for use by [[Slow Control System|Slow Control]] Equipments, i.e. when the  [[Equipment List Parameters#Type|Equipment Type]] is set to   [[Equipment Flags#EQ_SLOW|EQ_SLOW]]. For all other Types, this field is set to NULL.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For [[Slow Control System|Slow Control]]  Equipments, this parameter is set to the Device Driver List. This is defined using the DEVICE_DRIVER structure defined in midas.h in the MIDAS package. To turn on multithread support for a device, the flag DF_MULTITHREAD must be used in the frontend Device Driver List [https://midas.triumf.ca/elog/Midas/289].&lt;br /&gt;
&lt;br /&gt;
==== Example of Slow Control Equipment ====&lt;br /&gt;
In a real frontend, the device driver &amp;quot;nulldev&amp;quot; has to be replaced with a real device driver.&lt;br /&gt;
&lt;br /&gt;
Single thread&lt;br /&gt;
&lt;br /&gt;
 /* device driver list */&lt;br /&gt;
 DEVICE_DRIVER hv_driver[] = {&lt;br /&gt;
   {&amp;quot;Dummy Device&amp;quot;, nulldev, 16, null},&lt;br /&gt;
   {&amp;quot;&amp;quot;}&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;For multithread support&lt;br /&gt;
 /* multithread device driver list */&lt;br /&gt;
 DEVICE_DRIVER multi_driver[] = {&lt;br /&gt;
   {&amp;quot;Input&amp;quot;, nulldev, 2, null, DF_INPUT | DF_MULTITHREAD},&lt;br /&gt;
   {&amp;quot;Output&amp;quot;, nulldev, 2, null, DF_OUTPUT | DF_MULTITHREAD},&lt;br /&gt;
   {&amp;quot;&amp;quot;}&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [[#Class Driver|Class Driver]] parameter must also be defined. The Slow Control Equipment Definition is shown below, taken from the example ../midas/examples/slowcont/frontend.c in the MIDAS package. In this case, the class driver is $MIDASSYS/drivers/class/hv.c which contains the routines cd_hv_read(..) and cd_hv(..).&lt;br /&gt;
&lt;br /&gt;
 EQUIPMENT equipment[] = {&lt;br /&gt;
                                //&lt;br /&gt;
   {&amp;quot;HV&amp;quot;,                       /* equipment name */&lt;br /&gt;
    {3, 0,                       /* event ID, trigger mask */&lt;br /&gt;
     &amp;quot;SYSTEM&amp;quot;,                  /* event buffer */&lt;br /&gt;
     EQ_SLOW,                   /* equipment type */&lt;br /&gt;
     0,                         /* event source */&lt;br /&gt;
     &amp;quot;FIXED&amp;quot;,                   /* format */&lt;br /&gt;
     TRUE,                      /* enabled */&lt;br /&gt;
     RO_RUNNING | RO_TRANSITIONS,        /* read when running and on transitions */&lt;br /&gt;
     60000,                     /* read every 60 sec */&lt;br /&gt;
     0,                         /* stop run after this event limit */&lt;br /&gt;
     0,                         /* number of sub events */&lt;br /&gt;
     10000,                         /* log history every event */&lt;br /&gt;
     &amp;quot;&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;} ,&lt;br /&gt;
    cd_hv_read,                 /* readout routine */&lt;br /&gt;
    cd_hv,                      /* class driver main routine */&lt;br /&gt;
    hv_driver,                  /* device driver list */&lt;br /&gt;
    NULL,                       /* not used */&lt;br /&gt;
    },&lt;br /&gt;
   {&amp;quot;&amp;quot;}&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Bank Definition/Init String===&lt;br /&gt;
This [[#Equipment List|Equipment List Parameter]] must be present. It may be set to NULL, unless performing a ROOT format conversion or a FIXED format event.&lt;br /&gt;
&lt;br /&gt;
If the raw MIDAS/FIXED format data to be converted to ROOT format (by the data logger [[Mlogger|mlogger]]), the frontend is required to contain the definition of the bank(s) using the BANK_LIST structure defined in midas.h in the MIDAS package. This procedure is equivalent to the bank declaration in the [[analyser]]. A code fragment is shown below&lt;br /&gt;
&lt;br /&gt;
====Example ROOT format conversion ====&lt;br /&gt;
&lt;br /&gt;
The formats for the ADC0 bank is &amp;quot;fixed&amp;quot;, and for the TDC0 bank &amp;quot;MIDAS&amp;quot;.&lt;br /&gt;
This structure definition must be declared in the parameter Bank Definition in the equipment list.&lt;br /&gt;
&lt;br /&gt;
   ADC0_BANK_STR(adc0_bank_str);&lt;br /&gt;
   BANK_LIST trigger_bank_list[] = {&lt;br /&gt;
      {&amp;quot;ADC0&amp;quot;, TID_STRUCT, sizeof(ADC0_BANK), adc0_bank_str},&lt;br /&gt;
      {&amp;quot;TDC0&amp;quot;, TID_WORD, N_TDC, NULL},&lt;br /&gt;
      {&amp;quot;&amp;quot;},&lt;br /&gt;
   };&lt;br /&gt;
   BANK_LIST scaler_bank_list[] = {&lt;br /&gt;
      {&amp;quot;SCLR&amp;quot;, TID_DWORD, N_ADC, NULL},&lt;br /&gt;
      {&amp;quot;&amp;quot;},&lt;br /&gt;
   };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The NULL Bank Definition parameter in the [[#Example Equipment List declaration| equipment definition example]] would be replaced by the name of the defined structure, e.g.&lt;br /&gt;
          ...........    &lt;br /&gt;
          read_scaler_event,              // readout routine &lt;br /&gt;
          NULL,                           // not used  &lt;br /&gt;
          NULL,                           // not used&lt;br /&gt;
          scaler_bank_list,               // bank list&lt;br /&gt;
          ............&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See [[Event Structure#FIXED Event Construction|FIXED format event construction]] for example of the use of the &amp;quot;init string&amp;quot; parameter.&lt;br /&gt;
&lt;br /&gt;
[[Category:Equipment]] [[Category:Event]] [[Category:Frontend]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3406</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3406"/>
		<updated>2024-01-04T01:34:53Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduciton =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram if several histograms are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram if serveral are used&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-xy || 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-xy must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, 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.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3405</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3405"/>
		<updated>2024-01-03T19:57:47Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduciton =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || ODB path to X data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || ODB path to Y data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || ODB path to X data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram if several histograms are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || Label for &amp;lt;n&amp;gt;-th graph or histogram if serveral are used&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-xy || 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-xy must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| 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.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, 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.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3376</id>
		<title>Odbxx</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3376"/>
		<updated>2023-10-30T21:23:51Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Callback functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a &amp;quot;magic&amp;quot; map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.&lt;br /&gt;
&lt;br /&gt;
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].&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
== Basic usage ==&lt;br /&gt;
&lt;br /&gt;
The simplest usage is like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a bit of the ODB&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Simple read&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The current transition timeout is &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
// Make a change. The new value is automatically sent to the ODB.&lt;br /&gt;
// Most C++ operators are supported (++, += etc), or you can do a simple&lt;br /&gt;
// re-assignment like `exp[&amp;quot;Transition timeout&amp;quot;] = 12345;`.&lt;br /&gt;
exp[&amp;quot;Transition timeout&amp;quot;] += 100;&lt;br /&gt;
&lt;br /&gt;
// Read the new value&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The transition timeout is now &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;int curr_timeout = exp[&amp;quot;Transition timeout&amp;quot;];&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The ODB directory you connect to (&amp;quot;/Experiment&amp;quot; in the above example), has to start with a &amp;quot;/&amp;quot; to tell the midas::odb object&lt;br /&gt;
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.&lt;br /&gt;
&lt;br /&gt;
== Automatic refreshing ==&lt;br /&gt;
&lt;br /&gt;
You may temporarily disable the automatic updating to/from the ODB&lt;br /&gt;
using &amp;lt;code&amp;gt;odb::set_auto_refresh_write(false)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;odb::set_auto_refresh_read(false)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If auto-refresh is enabled (the default), your new values are sent to&lt;br /&gt;
the ODB as soon as you touch the value in the &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object. The ODB&lt;br /&gt;
is queried for new values whenever you access the value. In the above&lt;br /&gt;
example, the ODB is queried 4 times (during construction of &amp;lt;code&amp;gt;exp&amp;lt;/code&amp;gt;, and&lt;br /&gt;
each time &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is mentioned), and written to 1&lt;br /&gt;
time (when &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is assigned to).&lt;br /&gt;
&lt;br /&gt;
See the [[#Callback functions]] section below for details on how to have a function&lt;br /&gt;
called when a value changes.&lt;br /&gt;
&lt;br /&gt;
== Arrays/vectors ==&lt;br /&gt;
&lt;br /&gt;
ODB arrays are represented by std vectors.&lt;br /&gt;
&lt;br /&gt;
You can access/edit individual elements using []:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;][1] = 1.2;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can completely re-assign content using a std::vector or std::array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(10);&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can resize arrays using &amp;lt;code&amp;gt;odb::resize()&amp;lt;/code&amp;gt;. If the existing array is longer,&lt;br /&gt;
it will be truncated; if shorter it will be extended with default values&lt;br /&gt;
(0 or an empty string).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;].resize(5); // Now is 5 elements long&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that arithmetic operators are supported for arrays, and will apply&lt;br /&gt;
the operation to ALL ELEMENTS IN THE ARRAY:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Create the vector&lt;br /&gt;
std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(2);&lt;br /&gt;
vec[0] = 3;&lt;br /&gt;
vec[1] = 5;&lt;br /&gt;
&lt;br /&gt;
// Assign in ODB&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&lt;br /&gt;
// Multiply ALL elements by 2&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] *= 2;&lt;br /&gt;
&lt;br /&gt;
// odb[&amp;quot;Example&amp;quot;] now contains {6, 10}.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can directly iterate over arrays/vectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
for (auto it = o[&amp;quot;Int Array&amp;quot;].begin(); it != o[&amp;quot;Int Array&amp;quot;].end(); it++) {&lt;br /&gt;
   int val = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (int val : o[&amp;quot;Int Array&amp;quot;]) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Strings ==&lt;br /&gt;
&lt;br /&gt;
Strings in the ODB are returned as std::string (unlike the midas.h &amp;lt;code&amp;gt;db_get_value()&amp;lt;/code&amp;gt;&lt;br /&gt;
family of functions, where strings are returned as char*). You may have vectors of strings.&lt;br /&gt;
&lt;br /&gt;
== Creating new bits of the ODB ==&lt;br /&gt;
&lt;br /&gt;
You can automatically create bits of the ODB by passing a struct to the&lt;br /&gt;
&amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; constructor, then calling &amp;lt;code&amp;gt;odb::connect()&amp;lt;/code&amp;gt;, like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Define the ODB structure&lt;br /&gt;
midas::odb new_bit = {&lt;br /&gt;
   {&amp;quot;Int32 Key&amp;quot;, 42},&lt;br /&gt;
   {&amp;quot;Bool Key&amp;quot;, true},&lt;br /&gt;
   {&amp;quot;Subdir&amp;quot;, {&lt;br /&gt;
      {&amp;quot;Float key&amp;quot;, 1.2f},     // floats must be explicitly specified&lt;br /&gt;
   }},&lt;br /&gt;
   {&amp;quot;Int Array&amp;quot;, {1, 2, 3}},&lt;br /&gt;
   {&amp;quot;Double Array&amp;quot;, {1.2, 2.3, 3.4}},&lt;br /&gt;
   {&amp;quot;String Array&amp;quot;, {&amp;quot;Hello1&amp;quot;, &amp;quot;Hello2&amp;quot;, &amp;quot;Hello3&amp;quot;}},&lt;br /&gt;
   {&amp;quot;Large Array&amp;quot;, std::array&amp;lt;int, 10&amp;gt;{} },   // array with explicit size&lt;br /&gt;
   {&amp;quot;Large String&amp;quot;, std::string(63, &#039;\0&#039;) },  // string with explicit size&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Then sync the structure. This function&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// If you make the `write_defaults` argument true, then the function&lt;br /&gt;
// - overwrites the value of any keys that are in the ODB with the value in your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
// - deletes any keys that are in the ODB but not your code&lt;br /&gt;
// - updates the order of keys in the ODB to match your code&lt;br /&gt;
o.connect_and_fix_structure(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the ODB path in teh odb::connect() call must start with a &#039;/&#039;. The ODB root path like &amp;lt;code&amp;gt;o.connect(&amp;quot;/&amp;quot;);&amp;lt;/code&amp;gt; is not allowed in this call.&lt;br /&gt;
&lt;br /&gt;
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
existing_key[&amp;quot;MyNewSubKey&amp;quot;] = 1.23;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also create new keys by providing a default value when reading a value. If the key doesn&#039;t already exist, the default value will be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
double val = existing_key[&amp;quot;MyNewSubKey&amp;quot;](1.23);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Iterating over subkeys ==&lt;br /&gt;
&lt;br /&gt;
You can iterate over subkeys using normal iterator functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
for (auto it = exp.begin(); it != exp.end(); it++) {&lt;br /&gt;
   midas::odb&amp;amp; subkey = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (midas::odb&amp;amp; subkey : exp) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check whether a subkey exists using &amp;lt;code&amp;gt;odb::is_subkey()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Callback functions ==&lt;br /&gt;
&lt;br /&gt;
You may also set up callback functions that are called whenever a value&lt;br /&gt;
changes, using the &amp;lt;code&amp;gt;odb::watch()&amp;lt;/code&amp;gt; function. Note that you must call&lt;br /&gt;
&amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; (from midas.h) periodically for this to work - deep down it&lt;br /&gt;
is &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; itself that calls your callback function.&lt;br /&gt;
&lt;br /&gt;
The callback functions can either be a &amp;quot;normal&amp;quot; function, a C++ lambda, or a member function of a C++ class.&lt;br /&gt;
In all cases it should accept one argument - a &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object (passed&lt;br /&gt;
by reference) that contains the new state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a lambda:&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch([](midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a &amp;quot;normal&amp;quot; function:&lt;br /&gt;
void my_function(midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch(my_function);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a member function of a class:&lt;br /&gt;
#include &amp;lt;functional&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void MyClass::my_function(midas::odb&amp;amp; arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MyClass::some_initialisation_code() {&lt;br /&gt;
  // Arguments to std::bind() are: &amp;quot;member function to call&amp;quot;, &amp;quot;object to call that function on&amp;quot;, &amp;quot;placeholder for midas::odb arg&amp;quot;&lt;br /&gt;
  std::function&amp;lt;void(midas::odb&amp;amp;)&amp;gt; callback = std::bind(&amp;amp;MyClass::my_function, this, std::placeholders::_1);&lt;br /&gt;
&lt;br /&gt;
  midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
  to_watch.watch(callback);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utility functions ==&lt;br /&gt;
&lt;br /&gt;
There are various utility functions which can be used:&lt;br /&gt;
&lt;br /&gt;
==== void odb::create(const char *name, int type) ====&lt;br /&gt;
&lt;br /&gt;
Simple wrapper around db_create_key() to create a single key in the ODB. &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is one of TID_xxx.&lt;br /&gt;
&lt;br /&gt;
==== void odb::delete_key() ====&lt;br /&gt;
&lt;br /&gt;
This member function of a midas::odb object deletes that object from the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/Some/ODB/Path&amp;quot;);&lt;br /&gt;
o.delete_key();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== int odb::delete_key(const std::string &amp;amp;name) ====&lt;br /&gt;
&lt;br /&gt;
This function deletes a key or a subtree in the ODB passed by its path in the &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.&lt;br /&gt;
&lt;br /&gt;
==== bool odb::exists(const std::string *name) ====&lt;br /&gt;
&lt;br /&gt;
This boolean function checks if a key given by its name exists in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::exists(const std::string *name) ====&lt;br /&gt;
&lt;br /&gt;
This boolean function checks if a key given by its name exists in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====&lt;br /&gt;
&lt;br /&gt;
These functions set and retrieve the debug flag. If the debug flag is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.&lt;br /&gt;
&lt;br /&gt;
== Example code ==&lt;br /&gt;
&lt;br /&gt;
A full working example exploring most of the features can be found in&lt;br /&gt;
&amp;lt;code&amp;gt;odbxx/odbxx_test.cxx&amp;lt;/code&amp;gt;. The test executable will be compiled as&lt;br /&gt;
&amp;lt;code&amp;gt;build/odbxx/odbxx_test&amp;lt;/code&amp;gt; (it is not installed in the `bin` directory).&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Mjsonrpc&amp;diff=3373</id>
		<title>Mjsonrpc</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Mjsonrpc&amp;diff=3373"/>
		<updated>2023-10-17T21:29:30Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Mhttpd.js|MIDAS Javascript library]]&lt;br /&gt;
* [[Custom Page]]&lt;br /&gt;
* [[Custom Page Features]]&lt;br /&gt;
* [[mhttpd]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= MIDAS JSON-RPC interface =&lt;br /&gt;
This page described the MIDAS JSON-RPC interface found in the MIDAS Javascript library [[mhttpd.js]].&lt;br /&gt;
&lt;br /&gt;
== JSON general information ==&lt;br /&gt;
&lt;br /&gt;
JSON is a lightweight data-interchange format usually associated with Javascript and web programming. It is a popular choice as replacement for older general purpose data formats such as XML. JSON is defined by [https://tools.ietf.org/html/rfc7159 RFC-7159] (read more at http://www.json.org/). When necessary, the overhead of text encoded JSON is reduced by compressing JSON documents (using gzip), or by using binary-encoded JSON.&lt;br /&gt;
&lt;br /&gt;
JSON documents look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{ &amp;quot;Runinfo&amp;quot; : { &amp;quot;State&amp;quot; : 1, &amp;quot;Online Mode&amp;quot; : 1, &amp;quot;Run number&amp;quot; : 13585 } }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the JSON standard is incompatible with [https://en.wikipedia.org/wiki/IEEE_floating_point IEEE Standard for Floating-Point Arithmetic (IEEE 754)], specifically, there is no standard way to encode the special numerical values +Infinity, -Infinity and NaN (&amp;quot;-0.0&amp;quot; is ok).&lt;br /&gt;
&lt;br /&gt;
In MIDAS, JSON support is provided by an ODB data encoder (in odb.c), an ODB &amp;quot;JSON paste&amp;quot; decoder (in json_paste.cxx) and a general purpose JSON encoder/decoder (mjson.h, mjson.cxx, see https://daq.triumf.ca/~daqweb/doc/midas-devel/html/mjson_8h.html and https://daq.triumf.ca/~daqweb/doc/midas-devel/html/class_m_json_node.html).&lt;br /&gt;
&lt;br /&gt;
The MIDAS implementation of JSON has following variances from the JSON standard:&lt;br /&gt;
* numerical values +Infinity, -Infinity and NaN are encoded and decoded as JSON strings &amp;quot;Infinity&amp;quot;, &amp;quot;-Infinity&amp;quot; and &amp;quot;NaN&amp;quot;. This is compatible with most in-browser JSON implementations. See also http://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript and similar.&lt;br /&gt;
* numerical values that are usually presented as hex numbers, such a DWORD values, are encoded as JSON strings, i.e. &amp;quot;0x55b961c8&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== JSON encoding of ODB data ==&lt;br /&gt;
&lt;br /&gt;
MIDAS has 3 way to encode ODB data into JSON:&lt;br /&gt;
* &amp;quot;save&amp;quot; format for saving all ODB data and metadata, ODB can be fully reloaded or restored from an ODB JSON save file. Links are preserved as links, upper and lower case in names of ODB keys is preserved.&lt;br /&gt;
* &amp;quot;db_values&amp;quot; format for exporting ODB data to web pages: links are followed to their final values, &#039;&#039;&#039;ODB key names are converted to lower case&#039;&#039;&#039; for use with case-sensitive languages such as Javascript.&lt;br /&gt;
* &amp;quot;list&amp;quot; format encodes a single ODB directory and returns the full information printed by the odbedit &amp;quot;ls -l&amp;quot; command.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
=== JSON &amp;quot;save&amp;quot; format: db_copy_json_save() ===&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;save&amp;quot; format encodes all ODB data and metadata. ODB can be fully reloaded or restored from ODB JSON save files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ odbedit&lt;br /&gt;
odbedit&amp;gt; cd runinfo&lt;br /&gt;
[local:testexpt:S]/Runinfo&amp;gt;json&lt;br /&gt;
status: 1, json: {&lt;br /&gt;
  &amp;quot;State/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212553 },&lt;br /&gt;
  &amp;quot;State&amp;quot; : 1,&lt;br /&gt;
  &amp;quot;Online Mode/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1436830326 },&lt;br /&gt;
  &amp;quot;Online Mode&amp;quot; : 1,&lt;br /&gt;
  &amp;quot;Run number/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212481 },&lt;br /&gt;
  &amp;quot;Run number&amp;quot; : 13585,&lt;br /&gt;
  &amp;quot;Transition in progress/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212553 },&lt;br /&gt;
  &amp;quot;Transition in progress&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;Start abort/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212552 },&lt;br /&gt;
  &amp;quot;Start abort&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;Requested transition/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1436923816 },&lt;br /&gt;
  &amp;quot;Requested transition&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;Start time/key&amp;quot; : { &amp;quot;type&amp;quot; : 12, &amp;quot;item_size&amp;quot; : 32, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212481 },&lt;br /&gt;
  &amp;quot;Start time&amp;quot; : &amp;quot;Wed Jul 29 16:28:01 2015&amp;quot;,&lt;br /&gt;
  &amp;quot;Start time binary/key&amp;quot; : { &amp;quot;type&amp;quot; : 6, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212481 },&lt;br /&gt;
  &amp;quot;Start time binary&amp;quot; : &amp;quot;0x55b96181&amp;quot;,&lt;br /&gt;
  &amp;quot;Stop time/key&amp;quot; : { &amp;quot;type&amp;quot; : 12, &amp;quot;item_size&amp;quot; : 32, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212552 },&lt;br /&gt;
  &amp;quot;Stop time&amp;quot; : &amp;quot;Wed Jul 29 16:29:12 2015&amp;quot;,&lt;br /&gt;
  &amp;quot;Stop time binary/key&amp;quot; : { &amp;quot;type&amp;quot; : 6, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1438212552 },&lt;br /&gt;
  &amp;quot;Stop time binary&amp;quot; : &amp;quot;0x55b961c8&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
[local:testexpt:S]/Runinfo&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== JSON &amp;quot;db_values&amp;quot; format: db_copy_json_values() ===&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;db_values&amp;quot; format is intended for easy web page development - main thing is to return easy to parse ODB values with minimal metadata (only ODB last_written is provided).&lt;br /&gt;
&lt;br /&gt;
Because most programming languages and most JSON parser (including Javascript) are case sensitive, but ODB key names are not, this JSON encoding normalizes ODB key names by converting them to lower-case. (Converting spaces to underscores is also a possibility). Only the function &amp;quot;db_get_values&amp;quot; returns lower-case names - this remains the simplest way to have simple parsing of ODB JSON data in Javascript. &amp;quot;db_get_values&amp;quot; now returns an additional field for true names of ODB entries from before they were converted to lower case. This can be turned off with the &amp;quot;omit_names&amp;quot; RPC parameter.&lt;br /&gt;
&lt;br /&gt;
This allows easy use of parsed JSON in Javascript, i.e. runinfo.state or runinfo[&amp;quot;run number&amp;quot;] while avoiding case-matching getter functions (i.e. get_case_insensitive_property(runinfo, &amp;quot;state&amp;quot;)).&lt;br /&gt;
&lt;br /&gt;
Symlinks are followed to their final values and subdirectories are recursed.&lt;br /&gt;
&lt;br /&gt;
An option is provided to &#039;&#039;&#039;transfer the minimum amount of data&#039;&#039;&#039; by suppressing  the &#039;last_written&#039; value and the true names by setting the RPC parameters &amp;quot;omit_last_written&amp;quot; and &amp;quot;omit_names&amp;quot;. See $MIDASSYS/examples/javascript1/example.html. &lt;br /&gt;
&lt;br /&gt;
Also you can specify a timestamp and only data that is newer will be returned (caveats: no way to get rid of empty directories, for now, and no way to specify timestamps for individual array elements - you get the whole array or none of it).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ odbedit&lt;br /&gt;
odbedit&amp;gt; cd runinfo&lt;br /&gt;
[local:testexpt:S]/Runinfo&amp;gt;jsvalues&lt;br /&gt;
status: 1, json: {&lt;br /&gt;
  &amp;quot;state/last_written&amp;quot; : 1438212553,&lt;br /&gt;
  &amp;quot;state&amp;quot; : 1,&lt;br /&gt;
  &amp;quot;online mode/last_written&amp;quot; : 1436830326,&lt;br /&gt;
  &amp;quot;online mode&amp;quot; : 1,&lt;br /&gt;
  &amp;quot;run number/last_written&amp;quot; : 1438212481,&lt;br /&gt;
  &amp;quot;run number&amp;quot; : 13585,&lt;br /&gt;
  &amp;quot;transition in progress/last_written&amp;quot; : 1438212553,&lt;br /&gt;
  &amp;quot;transition in progress&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;start abort/last_written&amp;quot; : 1438212552,&lt;br /&gt;
  &amp;quot;start abort&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;requested transition/last_written&amp;quot; : 1436923816,&lt;br /&gt;
  &amp;quot;requested transition&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;start time/last_written&amp;quot; : 1438212481,&lt;br /&gt;
  &amp;quot;start time&amp;quot; : &amp;quot;Wed Jul 29 16:28:01 2015&amp;quot;,&lt;br /&gt;
  &amp;quot;start time binary/last_written&amp;quot; : 1438212481,&lt;br /&gt;
  &amp;quot;start time binary&amp;quot; : &amp;quot;0x55b96181&amp;quot;,&lt;br /&gt;
  &amp;quot;stop time/last_written&amp;quot; : 1438212552,&lt;br /&gt;
  &amp;quot;stop time&amp;quot; : &amp;quot;Wed Jul 29 16:29:12 2015&amp;quot;,&lt;br /&gt;
  &amp;quot;stop time binary/last_written&amp;quot; : 1438212552,&lt;br /&gt;
  &amp;quot;stop time binary&amp;quot; : &amp;quot;0x55b961c8&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
[local:testexpt:S]/Runinfo&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== JSON &amp;quot;ls&amp;quot; format: db_copy_json_ls() ===&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;ls&amp;quot; format returns all the data printed by odbedit command &amp;quot;ls -l&amp;quot; and shown by the mhttpd ODB editor web page. It is intended for implementing web ODB editor functions.&lt;br /&gt;
&lt;br /&gt;
Note how subdirectories are encoded as empty JSON objects &amp;quot;{}&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ odbedit&lt;br /&gt;
odbedit&amp;gt; cd experiment&lt;br /&gt;
[local:testexpt:S]/Experiment&amp;gt;jsls&lt;br /&gt;
jsls &amp;quot;/Experiment&amp;quot;, status: 1, json: {&lt;br /&gt;
  &amp;quot;Name/key&amp;quot; : { &amp;quot;type&amp;quot; : 12, &amp;quot;item_size&amp;quot; : 32, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1448038414 },&lt;br /&gt;
  &amp;quot;Name&amp;quot; : &amp;quot;testexpt&amp;quot;,&lt;br /&gt;
  &amp;quot;Buffer sizes&amp;quot; : { },&lt;br /&gt;
  &amp;quot;edit on start&amp;quot; : { },&lt;br /&gt;
  &amp;quot;Security&amp;quot; : { },&lt;br /&gt;
  &amp;quot;Transition debug flag/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1436830326 },&lt;br /&gt;
  &amp;quot;Transition debug flag&amp;quot; : 0,&lt;br /&gt;
  &amp;quot;Transition timeout/key&amp;quot; : { &amp;quot;type&amp;quot; : 7, &amp;quot;access_mode&amp;quot; : 7, &amp;quot;last_written&amp;quot; : 1436830326 },&lt;br /&gt;
  &amp;quot;Transition timeout&amp;quot; : 30000,&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Access to ODB arrays ==&lt;br /&gt;
&lt;br /&gt;
Some (not all) JSON-RPC methods allow access to individual array elements in ODB, i.e. {{Odbpath|path=/equipment/rpcexample/settings/a[10]}}.&lt;br /&gt;
&lt;br /&gt;
RPC methods that can do this are:&lt;br /&gt;
* db_copy&lt;br /&gt;
* db_get_values&lt;br /&gt;
* db_paste&lt;br /&gt;
&lt;br /&gt;
=== Supported array index syntax ===&lt;br /&gt;
An array index syntax has been implemented to make it easier to write individual array elements (Table 1)&lt;br /&gt;
;Table 1&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Array Syntax !! Array Elements !! Number of elements/data values&lt;br /&gt;
|-&lt;br /&gt;
| a[1] || 1 || 1 &lt;br /&gt;
|-&lt;br /&gt;
| a[1,2,3] || 1,2,3 || 3 &lt;br /&gt;
|-&lt;br /&gt;
| a[1-3] || 1,2,3 || 3 &lt;br /&gt;
|-&lt;br /&gt;
| a[3-1] || 3,2,1 || 3 &lt;br /&gt;
|-&lt;br /&gt;
| a[1,2,3-5,6] || 1,2,3,4,5,6 || 6 &lt;br /&gt;
|-&lt;br /&gt;
|  a[1-3,4-6,7-9]|| 1,2,3,4,5,6,7,8,9 || 9 &lt;br /&gt;
|-&lt;br /&gt;
| a[3-0,6-4,9-7] || 3,2,1,6,5,4,9,8,7 || 9  &lt;br /&gt;
|-&lt;br /&gt;
| a[4,2,5-6,8] || 4,2,5,6,8 || 5&lt;br /&gt;
|-&lt;br /&gt;
| a || 0-n || n &amp;lt;sup&amp;gt;**&amp;lt;/sup&amp;gt; &lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;sup&amp;gt;**&amp;lt;/sup&amp;gt; &amp;lt;small&amp;gt;n=number of data values supplied - see [[#array name only]].&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See [[#Writing an array]] and [[#Reading an array]] for examples.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== JSON-RPC general information ==&lt;br /&gt;
&lt;br /&gt;
* JSON-RPC standard: https://daq.triumf.ca/~daqweb/doc/midas-devel/doc/jsonrpc/JSON-RPC%202.0%20Specification.html&lt;br /&gt;
* JSON-RPC home page: http://www.jsonrpc.org/specification&lt;br /&gt;
* JSON-RPC via HTTP transport: https://daq.triumf.ca/~daqweb/doc/midas-devel/doc/jsonrpc/simple%20is%20better%20-%20JSON-RPC%202.0%20Transport_%20HTTP.html&lt;br /&gt;
* http://www.simple-is-better.org/json-rpc/index.html&lt;br /&gt;
&lt;br /&gt;
== Javascript client library ==&lt;br /&gt;
&lt;br /&gt;
MIDAS provides a simple client library:&lt;br /&gt;
* documentation, see the mjsonrpc functions: https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__mjsonrpc__js.html&lt;br /&gt;
* source code, see the mjsonrpc functions: https://daq.triumf.ca/~daqweb/doc/midas-devel/resources/mhttpd.js&lt;br /&gt;
&lt;br /&gt;
The MIDAS JSON RPC client library is based on the &#039;&#039;&#039;Javascript Promise&#039;&#039;&#039; pattern:&lt;br /&gt;
&lt;br /&gt;
* http://www.html5rocks.com/en/tutorials/es6/promises/&lt;br /&gt;
* https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise&lt;br /&gt;
&lt;br /&gt;
A simple example using the Promise API to display an ODB value on a web page:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mjsonrpc_db_get_values([&amp;quot;/runinfo/run number&amp;quot;]).then(function(rpc) {&lt;br /&gt;
   document.getElementById(&amp;quot;run_number&amp;quot;).innerHTML = rpc.result.data[0];&lt;br /&gt;
}).catch(function(error) {&lt;br /&gt;
   mjsonrpc_error_alert(error);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A partial list of available javascript functions. For the full list, and for full documentation, please go to the doxygen-generated documentation at&lt;br /&gt;
https://daq.triumf.ca/~daqweb/doc/midas-devel/html/mhttpd_8js.html&lt;br /&gt;
&lt;br /&gt;
* function 	mjsonrpc_set_url (url) - set the URL of the MIDAS JSON-RPC server, if different from web page URL. Cross-site access is fully supported (see CORS).&lt;br /&gt;
* function 	mjsonrpc_call (method, params, id) - call arbitrary RPC method&lt;br /&gt;
* function 	mjsonrpc_start_program (name, id, callback, error_callback)&lt;br /&gt;
* function 	mjsonrpc_stop_program (name, unique, id, callback, error_callback)&lt;br /&gt;
* function 	mjsonrpc_db_get_values (paths, id, callback, error_callback) - read ODB values&lt;br /&gt;
* function 	mjsonrpc_db_paste (paths, values, id, callback, error_callback) - write ODB values&lt;br /&gt;
&lt;br /&gt;
=== Batch Requests ===&lt;br /&gt;
&lt;br /&gt;
The MIDAS mjson_rpc supports making combining together multiple different requests into the same HTTP request.  This batch mode requesting is much more efficient and should be used whenever possible.  An example of a mjson_rpc batch request for the transition status and ODB values is shown here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var req = new Array;&lt;br /&gt;
req.push(mjsonrpc_make_request(&amp;quot;cm_transition_status&amp;quot;));&lt;br /&gt;
req.push(mjsonrpc_make_request(&amp;quot;db_get_values&amp;quot;, {&amp;quot;paths&amp;quot;: [&amp;quot;/runinfo&amp;quot;]}));&lt;br /&gt;
mjsonrpc_send_request(req).then(function (rpc) {&lt;br /&gt;
   USER CODE HERE&lt;br /&gt;
}).catch(function (error) {&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
Only a few examples are given below. For more examples:&lt;br /&gt;
* look elsewhere on this page&lt;br /&gt;
* look at the example experiment examples/javascript1/example.html&lt;br /&gt;
* look at the doxygen-generated documentation for mjsonrpc_xxx functions&lt;br /&gt;
* look at the implementation of the functions&lt;br /&gt;
* look at the implementation of mhttpd internal web pages (functions mhttpd_xxx in mhttpd.js)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ curl -H &amp;quot;Content-Type: application/json&amp;quot; --data &#039;{&amp;quot;jsonrpc&amp;quot;:&amp;quot;2.0&amp;quot;,&amp;quot;id&amp;quot;:null,&amp;quot;method&amp;quot;:&amp;quot;db_get_values&amp;quot;,&amp;quot;params&amp;quot;:{&amp;quot;paths&amp;quot;:[&amp;quot;/runinfo/run number&amp;quot;]}}&#039; &#039;http://localhost:8080?mjsonrpc&#039;&lt;br /&gt;
{&amp;quot;jsonrpc&amp;quot;: &amp;quot;2.0&amp;quot;,&amp;quot;result&amp;quot;:{&amp;quot;data&amp;quot;:[324],&amp;quot;status&amp;quot;:[1],&amp;quot;last_written&amp;quot;:[1443570804]},&amp;quot;id&amp;quot;:null}&lt;br /&gt;
$ &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ODB data ===&lt;br /&gt;
The following code illustrates how to use function &#039;&#039;&#039;mjsonrpc_db_get_values()&#039;&#039;&#039; (see [https://daq.triumf.ca/~daqweb/doc/midas-devel/htm/group__mjsonrpc__js.html#ga80cf049d190f57f6a3f6004791d7f0e9| rpc get_values]) to read data from the ODB. Some of the data are displayed using InnerHTML. The data are read every 10 seconds. The time changes and a counter increments each time the data are read. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;title&amp;gt;My Title&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;script src=&#039;mhttpd.js&#039;&amp;gt; &lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
var updatePeriod = 10000; // in msec&lt;br /&gt;
var updateTimerId = 0;&lt;br /&gt;
var counter = 0;&lt;br /&gt;
&lt;br /&gt;
function update()  {&lt;br /&gt;
   clearTimeout(updateTimerId);&lt;br /&gt;
   load();&lt;br /&gt;
   if (updatePeriod &amp;gt; 0)&lt;br /&gt;
   updateTimerId = setTimeout(&#039;update()&#039;, updatePeriod);&lt;br /&gt;
}&lt;br /&gt;
function load()   {&lt;br /&gt;
    counter++;&lt;br /&gt;
    document.getElementById(&#039;LastUpdated&#039;).innerHTML = &amp;quot;Updating...&amp;quot; + new Date;&lt;br /&gt;
    document.getElementById(&#039;counter&#039;).innerHTML = &#039;Counter: &#039;+ counter&lt;br /&gt;
&lt;br /&gt;
    mjsonrpc_db_get_values([&amp;quot;/Runinfo&amp;quot;,&amp;quot;/Experiment/name&amp;quot;]).then(function(rpc) {&lt;br /&gt;
       var runinfo= rpc.result.data[0]&lt;br /&gt;
       var name =  rpc.result.data[1]&lt;br /&gt;
       document.getElementById(&amp;quot;name&amp;quot;).innerHTML =&#039;Experiment name =&#039;+ name&lt;br /&gt;
       document.getElementById(&amp;quot;state&amp;quot;).innerHTML =&#039;Run State=&#039;+ runinfo.state&lt;br /&gt;
       document.getElementById(&amp;quot;rn&amp;quot;).innerHTML =&#039;Run number=&#039;+ runinfo[&amp;quot;run number&amp;quot;]&lt;br /&gt;
       document.getElementById(&amp;quot;status&amp;quot;).innerHTML = &#039;Status: &#039;+ rpc.result.status&lt;br /&gt;
    }).catch(function(error) {&lt;br /&gt;
       mjsonrpc_error_alert(error);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&amp;lt;h2&amp;gt; Javascript code example using mjson_db_get_values with Promises &amp;lt;/h2&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;LastUpdated&amp;quot;&amp;gt;Last updated: never&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;rn&amp;quot;&amp;gt;Run Number : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;state&amp;quot;&amp;gt;Run State : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;name&amp;quot;&amp;gt;Experiment name : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;status&amp;quot;&amp;gt;Status : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;counter&amp;quot;&amp;gt;Counter: zero&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
update()&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When run as a [[Custom Page]] using the web server [[mhttpd]], the above html code looks like this:&lt;br /&gt;
[[File: mjson_example1.jpg|center|frame|Figure 1: code to read from ODB run as a Custom Page]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;   &amp;lt;!-- clear wraparound after image --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The status value (rpc.result.status) is an array. &lt;br /&gt;
* rpc.result.status[0] is the status from reading &amp;quot;/Runinfo&amp;quot;, and &lt;br /&gt;
* rpc.result.status[1] is the status from reading &amp;quot;/Experiment/name&amp;quot;. &lt;br /&gt;
A value of 1 means success.&lt;br /&gt;
If the path does not exist, the status value will be 312, corresponding to the error DB_NO_KEY defined in midas.h.&lt;br /&gt;
&lt;br /&gt;
=== Write ODB data and read it back ===&lt;br /&gt;
The following example illustrates how to write ODB data using the function &#039;&#039;&#039;mjsonrpc_db_paste()&#039;&#039;&#039; (see [https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__mjsonrpc__js.html#ga2a1da2269d82fbb9edf410a9241997b8| rpc db_paste]). In this example, when the &amp;quot;Set&amp;quot; button is pressed, data are written to the ODB keys {{Odbpath|path=test}}(integer), {{Odbpath|path=pi}}(float) and {{Odbpath|path=my_string}}(string), all in the ODB subdirectory {{Odbpath|path=/equipment/rpcexample/settings/}}. When the &amp;quot;Read&amp;quot; button is pressed, the three ODB keys are read and the data written to the screen (Figure 2). For convenience, in function set(), the paths of the ODB keys to be written are stored in the array &amp;quot;paths&amp;quot;.  Unlike the example above, there is no automatic update.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;title&amp;gt;My Custom Page Title&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;script src=&#039;mhttpd.js&#039;&amp;gt; &lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
var counter = 0;&lt;br /&gt;
&lt;br /&gt;
function load()  {&lt;br /&gt;
      counter++;&lt;br /&gt;
      document.getElementById(&#039;LastUpdated&#039;).innerHTML = &amp;quot;Updating...&amp;quot; + new Date;&lt;br /&gt;
      mjsonrpc_db_get_values([&amp;quot;/equipment/rpcexample/settings/&amp;quot;]).then(function(rpc) {&lt;br /&gt;
         settings= rpc.result.data[0]&lt;br /&gt;
         document.getElementById(&amp;quot;test&amp;quot;).innerHTML =&#039;Integer test =&#039;+  settings.test&lt;br /&gt;
         document.getElementById(&amp;quot;pi&amp;quot;).innerHTML =&#039;Float pi =&#039;+  settings.pi&lt;br /&gt;
         document.getElementById(&amp;quot;string&amp;quot;).innerHTML =&#039;String my_string=&#039;+ settings[&amp;quot;my string&amp;quot;]&lt;br /&gt;
         document.getElementById(&amp;quot;status&amp;quot;).innerHTML = &#039;Read Status: &#039;+ rpc.result.status&lt;br /&gt;
      }).catch(function(error) {&lt;br /&gt;
         mjsonrpc_error_alert(error);&lt;br /&gt;
      });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function set()  {&lt;br /&gt;
      var paths=[&amp;quot;/equipment/rpcexample/settings/test&amp;quot;,&amp;quot;/equipment/rpcexample/settings/pi&amp;quot;,&amp;quot;/equipment/rpcexample/settings/my string&amp;quot;];&lt;br /&gt;
      mjsonrpc_db_paste(paths, [10,3.1416,&amp;quot;hallo world&amp;quot;]).then(function(rpc) {&lt;br /&gt;
	 result=rpc.result;	      &lt;br /&gt;
         document.getElementById(&amp;quot;wstatus&amp;quot;).innerHTML = &#039;Write status &#039;+rpc.result.status	      &lt;br /&gt;
      }).catch(function(error) {&lt;br /&gt;
          mjsonrpc_error_alert(error);&lt;br /&gt;
      });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
&amp;lt;h2&amp;gt; Example code using mjsonrpc calls to write and read &amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;LastUpdated&amp;quot;&amp;gt;Last updated: never&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;test&amp;quot;&amp;gt; Test : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;pi&amp;quot;&amp;gt;Pi : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;string&amp;quot;&amp;gt;String : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;status&amp;quot;&amp;gt;Read Status : unknown&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;wstatus&amp;quot;&amp;gt;Write Status: zero&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt; &amp;lt;input type=button value=&#039;Set values&#039; onClick=&#039;set();&#039;&amp;gt; &amp;lt;/input&amp;gt;&lt;br /&gt;
       &amp;lt;input type=button value=&#039;Read values&#039; onClick=&#039;load();&#039;&amp;gt;&amp;lt;/input&amp;gt; &amp;gt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When run as a [[Custom Page]] using the web server [[mhttpd]], the output from the above html code is shown in Figure 2 below.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Figure 2a: Initial custom page !! Figure 2b: After pressing &amp;quot;Set&amp;quot; button !! Figure 2c: After pressing &amp;quot;Read&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
|[[File: mjson_example2a.jpg|frame]] || [[File: mjson_example2b.jpg|frame ]] || [[File: mjson_example2c.jpg|frame]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;   &amp;lt;!-- clear wraparound after image --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the data is written (Figure 2b) the write status is shown. &lt;br /&gt;
The status value (rpc.result.status) is an array with each element showing the status of the three write operations to the three keys, i.e. &lt;br /&gt;
* rpc.result.status[0] is the status from writing &amp;quot;test&amp;quot;, and &lt;br /&gt;
* rpc.result.status[1] is the status from writing &amp;quot;pi&amp;quot;. &lt;br /&gt;
* rpc.result.status[2] is the status from writing &amp;quot;my_string&amp;quot; &lt;br /&gt;
A value of 1 means success.&lt;br /&gt;
After the data is read (Figure 2c), the values read and the read status is shown. In this case, the read status only has one value (1=success) because one read operation of the whole subdirectory is performed.&lt;br /&gt;
&lt;br /&gt;
==== Error handling ====&lt;br /&gt;
If one of the paths to write does not exist, there will be an error reported in the status value. For example, if the path to write the variable &amp;quot;pi&amp;quot; does not exist, the write status will be&lt;br /&gt;
: Write status 1,312,1&lt;br /&gt;
The value 312 is that of the error DB_NO_KEY defined in midas.h.&lt;br /&gt;
&lt;br /&gt;
If the number of data values supplied to the function mjsonrpc_db_paste() does not equal the number of paths (i.e. lengths of paths and data arrays are not equal) an error popup will result (Figure 3). The error message also contains the response ID - see [https://daq.triumf.ca/~daqweb/doc/midas-devel/html/group__mjsonrpc__js.html#ga2a1da2269d82fbb9edf410a9241997b8| rpc db_paste] for more information.&lt;br /&gt;
[[File: mjson_slerr.jpg|center|frame|Figure 3: Error popup when lengths of paths array and data array are not equal]]&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;   &amp;lt;!-- clear wraparound after image --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Writing an array ===&lt;br /&gt;
The following example shows using mjsonrpc_db_paste() to write to an array in ODB. See above for [[#supported array index syntax]].&lt;br /&gt;
Because each array element is accessed individually, each array element value comes with a corresponding status value of odb db_get_data_index(), encoded as an array (i.e. rpc.result.status in example below). Note that the data to be written to the array elements must be enclosed by their own square brackets [] and the number of data values must match the number of array elements listed (except when the array name is used alone -  see [[#array name only]]). Too few data values will throw an error, too many will be ignored. &lt;br /&gt;
If the array in the ODB is not large enough for the number of elements and data to be written, it will be expanded automatically.&lt;br /&gt;
&lt;br /&gt;
==== Writing to a variable and an array ====&lt;br /&gt;
The following example shows using mjsonrpc_db_paste()  to&lt;br /&gt;
* write &amp;quot;9&amp;quot; to an integer variable called &amp;quot;test&amp;quot;, and to &lt;br /&gt;
* write values to a number of elements of the array &amp;quot;array&amp;quot;&lt;br /&gt;
Both &amp;quot;test&amp;quot; and &amp;quot;array&amp;quot; are keys in the ODB subdirectory {{Odbpath|path=/equipment/rpcexample/settings/}}.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/test&amp;quot;,&amp;quot;/equipment/rpcexample/settings/array[3-1,4,5,8-10]&amp;quot;], [9,[1,2,3,4,5,8,9,10]]).then(function(rpc) {&lt;br /&gt;
       document.getElementById(&amp;quot;wstatus&amp;quot;).innerHTML =&#039;Set Status=&#039;+ rpc.result.status&lt;br /&gt;
  }).catch(function(error) {&lt;br /&gt;
       mjsonrpc_error_alert(error);&lt;br /&gt;
    });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Assuming the array was filled with zeroes to start with, after writing to the array  and [[#reading an array|reading it back]], it would now contain the values&lt;br /&gt;
: Array= 0,3,2,1,4,5,0,0,8,9,10,11 &lt;br /&gt;
: Set Status = 1,1,1,1,1,1,1,1,1   (rpc.result.status)&lt;br /&gt;
There are 9 elements in the rpc.result.status array, where 1=success. The first element is the status after writing &amp;quot;test&amp;quot;, the next 8 the status after writing each array element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  Automatically expand the array ====&lt;br /&gt;
If the array in the ODB contains 12 elements, writing&lt;br /&gt;
 mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/test&amp;quot;,&amp;quot;/equipment/rpcexample/settings/array[3-1,4,5,8-10,14]&amp;quot;], [9,[1,2,3,4,5,8,9,10,14] ]).then(function(rpc) { ...&lt;br /&gt;
will automatically expand the array to 15 elements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Too few data values ====&lt;br /&gt;
Supplying too few data values, e.g. writing&lt;br /&gt;
 mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/array[4,5,8-10]&amp;quot;], [ [4,5,8,9] ]).then(function(rpc) { ...&lt;br /&gt;
results in an error (rpc.result.status= 315) i.e. DB_TYPE_MISMATCH because there aren&#039;t enough data words supplied (4) for the number of indices (5).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;array name only&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
==== Array name only supplied ====&lt;br /&gt;
If you supply just the array name, then you do not need to supply the same number of elements as the array length. For example,&lt;br /&gt;
 mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/array&amp;quot;],[ [7,6,0] ]).then(function(rpc){ ...&lt;br /&gt;
will write the first three elements of the array to 7,6,0. Other array elements will be untouched. In this case, there is only one status value for the whole array. &lt;br /&gt;
: Array= 7,6,0,1,4,5,0,0,8,9,10,11 &lt;br /&gt;
: Set Status = 1   (rpc.result.status)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Set all elements to one value ====&lt;br /&gt;
If you want to set all 19 elements of an array to 1, you can write&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/array[0-18]&amp;quot;], [1]).then(function(rpc) { ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
or to set a group of indices to 999, you can write&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mjsonrpc_db_paste([&amp;quot;/equipment/rpcexample/settings/array[1-3,4,5,8-10]&amp;quot;], [999]).then(function(rpc) { ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the single value is written as [999] rather than as an array with single value, i.e. [ [999] ].&lt;br /&gt;
&lt;br /&gt;
=== Reading an array ===&lt;br /&gt;
In the same way, you can read all or part of an array.  See above for [[#Supported array index syntax]].&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var array = new Array();&lt;br /&gt;
mjsonrpc_db_get_values([&amp;quot;/Runinfo&amp;quot;,&amp;quot;/Experiment/name&amp;quot;,&amp;quot;/Equipment/rpcexample/Settings/array&amp;quot;,&amp;quot;/Equipment/rpcexample/Settings/array[3-6,11]&amp;quot;]).then(function(rpc) {&lt;br /&gt;
      document.getElementById(&amp;quot;status&amp;quot;).innerHTML = &#039;Status : &#039;+ result.status  &lt;br /&gt;
      var runinfo= rpc.result.data[0] &lt;br /&gt;
      var name =  rpc.result.data[1]&lt;br /&gt;
      array = rpc.result.data[2]   // whole array&lt;br /&gt;
      document.getElementById(&amp;quot;array&amp;quot;).innerHTML =&#039;Array=&#039;+ array&lt;br /&gt;
      array=rpc.result.data[3]    // array indices&lt;br /&gt;
      document.getElementById(&amp;quot;array_ele&amp;quot;).innerHTML =&#039;Array elements=&#039;+ array&lt;br /&gt;
}).catch(function(error) {&lt;br /&gt;
   mjsonrpc_error_alert(error);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This example gives &lt;br /&gt;
: Array=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0&lt;br /&gt;
: Array elements=4,5,6,7,12&lt;br /&gt;
: Status :  1,1,1,1,1,1,1,1  (rpc.result.status)&lt;br /&gt;
Note that the &amp;quot;Array elements&amp;quot; line has read the contents of indices 3-6 and 11 as requested (5 indices in total). The status array contains 8 values. The last 5 values are the status from reading the 5 array elements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Schema (List of all RPC methods) ==&lt;br /&gt;
&lt;br /&gt;
The MIDAS JSON RPC API is documented via an automatically generated JSON Schema.&lt;br /&gt;
&lt;br /&gt;
The JSON Schema (&amp;quot;describes your JSON data format&amp;quot;) is not a well established standard (there is no final RFC) but seems to be in common use. Multiple 3rd party tools exist&lt;br /&gt;
to visualize, explore and interface with JSON documents described by JSON Schemas. For more general information go here:&lt;br /&gt;
* http://json-schema.org/&lt;br /&gt;
* https://tools.ietf.org/html/draft-zyp-json-schema-04&lt;br /&gt;
&lt;br /&gt;
The MIDAS JSON RPC Schema is automatically generated by mhttpd and is &amp;lt;b&amp;gt;linked by the mhttpd &amp;quot;Help&amp;quot; page in JSON and in text format&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For reference, a recent copy of the JSON RPC API Schema is pasted here from the mhttpd &amp;quot;help&amp;quot; page. For final, complete and up-to-date schema, please get your own copy from the midas &amp;quot;help&amp;quot; page.&lt;br /&gt;
&lt;br /&gt;
In this schema, &amp;quot;?&amp;quot; means an optional parameter, &amp;quot;[]&amp;quot; means an array parameter. (There is an artefact: all method names have question marks because they are optional as far as the schema generator is concerned).&lt;br /&gt;
&lt;br /&gt;
=== MIDAS JSON RPC API Schema ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
Autogenerated schema for all MIDAS JSON-RPC methods&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
al_reset_alarm?             | reset alarms&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | alarms[]             | array of alarm names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of al_reset_alarm() for each alarm&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
al_trigger_alarm?           | trigger an alarm&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | name                 | string         | alarm name&lt;br /&gt;
                            |          | message              | string         | alarm message&lt;br /&gt;
                            |          | class                | string         | alarm class&lt;br /&gt;
                            |          | condition            | string         | alarm condition&lt;br /&gt;
                            |          | type                 | integer        | alarm type (AT_xxx)&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of al_trigger_alarm()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
al_trigger_class?           | trigger an alarm&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | class                | string         | alarm class&lt;br /&gt;
                            |          | message              | string         | alarm message&lt;br /&gt;
                            |          | first?               | bool           | see al_trigger_class() in midas.c&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of al_trigger_class()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
bm_receive_event?           | read event buffers&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | buffer_name          | string         | name of event buffer&lt;br /&gt;
                            |          | event_id?            | integer        | requested event id, -1 means any event id&lt;br /&gt;
                            |          | trigger_mask?        | integer        | requested trigger mask, -1 means any trigger mask&lt;br /&gt;
                            |          | get_recent?          | bool           | get last available event that matches this event request&lt;br /&gt;
                            |          | last_event_header[]? | do not resend an event we already received: event header of last received event [event_id,trigger_mask,serial_number,time_stamp]&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | timeout_millisec?    | number         | how long to wait for an event&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | binary data          | arraybuffer    | binary event data&lt;br /&gt;
                            |          | status               | integer        | return status of bm_open_buffer(), bm_request_event(), bm_set_cache_size(), bm_receive_alloc()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_exist?                   | calls MIDAS cm_exist() to check if given MIDAS program is running&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | name                 | string         | name of the program, corresponding to ODB /Programs/name&lt;br /&gt;
                            |          | unique?              | bool           | bUnique argument to cm_exist()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_exist()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_msg1?                    | Generate a midas message using cm_msg1()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | facility?            | string         | message facility, default is &amp;quot;midas&amp;quot;&lt;br /&gt;
                            |          | user?                | string         | message user, default is &amp;quot;javascript_commands&amp;quot;&lt;br /&gt;
                            |          | type?                | integer        | message type, MT_xxx from midas.h, default is MT_INFO&lt;br /&gt;
                            |          | message              | string         | message text&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_msg1()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_msg_facilities?          | get message facilities using cm_msg_facilities()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_msg_facilities()&lt;br /&gt;
                            |          | facilities[]         | array of facility names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_msg_retrieve?            | Retrieve midas messages using cm_msg_retrieve2()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | facility?            | string         | message facility, default is &amp;quot;midas&amp;quot;&lt;br /&gt;
                            |          | min_messages?        | integer        | get at least this many messages, default is 1&lt;br /&gt;
                            |          | time?                | number         | start from given timestamp, value 0 means give me newest messages, default is 0&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | num_messages         | integer        | number of messages returned&lt;br /&gt;
                            |          | messages             | string         | messages separated by \n&lt;br /&gt;
                            |          | status               | integer        | return status of cm_msg_retrieve2()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_shutdown?                | calls MIDAS cm_shutdown() to stop given MIDAS program&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | name                 | string         | name of the program, corresponding to ODB /Programs/name&lt;br /&gt;
                            |          | unique?              | bool           | bUnique argument to cm_shutdown()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_shutdown()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
cm_transition?              | start and stop runs&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | transition           | string         | requested transition: TR_START, TR_STOP, TR_PAUSE, TR_RESUME&lt;br /&gt;
                            |          | run_number?          | integer        | New run number, value 0 means /runinfo/run_number + 1, default is 0&lt;br /&gt;
                            |          | async_flag?          | integer        | Transition type. Default is multithreaded transition TR_MTHREAD&lt;br /&gt;
                            |          | debug_flag?          | integer        | See cm_transition(), value 1: trace to stdout, value 2: trace to midas.log&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_transition()&lt;br /&gt;
                            |          | error_string?        | string         | return error string from cm_transition()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_copy?                    | get complete ODB data in the &amp;quot;save&amp;quot; json encoding, suitable for reloading with odbedit command &amp;quot;load&amp;quot;&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB subtree paths&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | data[]               | keys and values of ODB data for each path&lt;br /&gt;
                            |          |                      | array of       | object     &lt;br /&gt;
                            |          | status[]             | return status of db_copy_json_save() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_create?                  | Create new ODB entries&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params[] | array of ODB paths to be created&lt;br /&gt;
                            |          | array of             | arguments to db_create_key() and db_set_num_values()&lt;br /&gt;
                            |          |                      | path           | string      | ODB path to be created&lt;br /&gt;
                            |          |                      | type           | integer     | MIDAS TID_xxx type&lt;br /&gt;
                            |          |                      | array_length?  | integer     | optional array length, default is 1&lt;br /&gt;
                            |          |                      | string_length? | integer     | for TID_STRING, optional string length, default is NAME_LENGTH&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_create_key(), db_set_num_values() and db_set_data() (for TID_STRING) for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_delete?                  | delete ODB keys&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths to delete&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_delete_key() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_get_values?              | get values of ODB data from given subtrees&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB subtree paths, see note on array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | omit_names?          | bool           | omit the /name entries&lt;br /&gt;
                            |          | omit_last_written?   | bool           | omit the /last_written entries and the last_written[] result&lt;br /&gt;
                            |          | omit_tid?            | bool           | omit the tid[] result&lt;br /&gt;
                            |          | omit_old_timestamp?  | number         | omit data older than given ODB timestamp&lt;br /&gt;
                            |          | preserve_case?       | bool           | preserve the capitalization of ODB key names (WARNING: ODB is not case sensitive); note that this will also have side effect of setting the omit_names option&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | data[]               | values of ODB data for each path, all key names are in lower case, all symlinks are followed&lt;br /&gt;
                            |          |                      | array of       | any        &lt;br /&gt;
                            |          | status[]             | return status of db_copy_json_values() or db_copy_json_index() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | tid[]?               | odb type id for each path, absent if omit_tid is true&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | last_written[]?      | last_written value of the ODB subtree for each path, absent if omit_last_written is true&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_key?                     | get ODB keys&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_key() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | key data for each path&lt;br /&gt;
                            |          |                      | array of       | object     &lt;br /&gt;
                            |          | keys[]               | key type TID_xxx&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | array length, 1 for normal entries&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | key name&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | keys[]               | data total size in bytes&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | array element size, string length for TID_STRING&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | access mode bitmap of MODE_xxx&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | number of hotlinks attached to this key&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | keys[]               | timestamp when data was last updated&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_link?                    | Create ODB symlinks&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | new_links[]          | array of new symlinks to be created&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | target_paths[]       | array of existing ODB paths for each link&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_create_link() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_load?                    | Save ODB subtree to file&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | filename             | string         | Filename to read ODB contents from&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of db_load&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_ls?                      | get contents of given ODB subdirectory in the &amp;quot;ls&amp;quot; json encoding - similar to odbedit command &amp;quot;ls -l&amp;quot;&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB subtree paths&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | data[]               | keys and values of ODB data for each path&lt;br /&gt;
                            |          |                      | array of       | object     &lt;br /&gt;
                            |          | status[]             | return status of db_copy_json_ls() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_paste?                   | write data into ODB&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB subtree paths, see note on array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | values[]             | array of data values written to ODB via db_paste_json() for each path&lt;br /&gt;
                            |          |                      | array of       | any        &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | array of return status of db_paste_json() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_rename?                  | Change size of ODB arrays&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths to rename&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | new_names[]          | array of new names for each ODB path&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_rename_key() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_reorder?                 | Change order of ODB keys in a subdirectory&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of new symlinks to be created&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | indices[]            | array of existing ODB paths for each link&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_reorder_key() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_resize?                  | Change size of ODB arrays&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths to resize&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | new_lengths[]        | array of new lengths for each ODB path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_set_num_values() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_resize_string?           | Change size of ODB string arrays&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | paths[]              | array of ODB paths to resize&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | new_lengths[]        | array of new lengths for each ODB path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | new_string_lengths[] | array of new string lengths for each ODB path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status[]             | return status of db_resize_string() for each path&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_save?                    | Save ODB subtree to file&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | path                 | string         | ODB path&lt;br /&gt;
                            |          | filename             | string         | Filename so save ODB contents to&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of db_save&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_scl?                     | Show ODB clients&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | scl                  | json           | return value of db_scl()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
db_sor?                     | Show ODB open records starting from given ODB path&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | path?                | string         | ODB path&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | sor                  | json           | return value of db_sor()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
el_delete?                  | Delete elog message&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | tag                  | string         | tag of message to delete&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of el_delete&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
el_query?                   | Query elog messages&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | last_n_hours?        | integer        | return messages from the last N hours&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of el_retrieve&lt;br /&gt;
                            |          | msg[]                | message tag&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
el_retrieve?                | Get an elog message&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | tag                  | string         | elog message tag&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of el_retrieve&lt;br /&gt;
                            |          | msg.tag              | string         | message tag&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
exec_script?                | execute custom script defined in ODB /Script (scripts show in the menu) or /CustomScript (scripts from custom pages)&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | script?              | string         | Execute ODB /Script/xxx&lt;br /&gt;
                            |          | customscript?        | string         | Execute ODB /CustomScript/xxx&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of cm_exec_script()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
ext_list_files?             | js_ext_list_files&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | subdir               | string         | List files in experiment_directory/userfiles/subdir&lt;br /&gt;
                            |          | fileext              | string         | Filename extension&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | path                 | string         | Search path&lt;br /&gt;
                            |          | subdirs[]            | list of subdirectories&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | files[]              | script filename&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | files[]              | script description&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
ext_read_file?              | js_ext_read_script&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | filename             | string         | File name, read from experiment_directory/userfiles/filename&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | content              | string         | ASCII file content&lt;br /&gt;
                            |          | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | error                | string         | error text&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
ext_save_file?              | js_ext_save_file&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | filename             | string         | File name, save in experiment_directory/userfiles/filename&lt;br /&gt;
                            |          | script               | string         | ASCII content&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | error                | string         | error text&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_alarms?                 | get alarm data&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | get_all?             | bool           | get all alarms, even in alarm system not active and alarms not triggered&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | alarm_system_active  | bool           | value of ODB &amp;quot;/Alarms/alarm system active&amp;quot;&lt;br /&gt;
                            |          | alarms               | object         | alarm data, keyed by alarm name&lt;br /&gt;
                            |          | alarms[]             | alarm is triggered&lt;br /&gt;
                            |          |                      | array of       | bool       &lt;br /&gt;
                            |          | alarms[]             | alarm is enabled&lt;br /&gt;
                            |          |                      | array of       | bool       &lt;br /&gt;
                            |          | alarms[]             | alarm class&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | alarm type AT_xxx&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | alarms[]             | display background color&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | display foreground color&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | alarm ODB message field&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | alarm ODB condition field&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | evaluated alarm condition (AT_EVALUATED alarms only)&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | next time the periodic alarm will fire (AT_PERIODIC alarms only)&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | time when alarm was triggered&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | alarms[]             | final alarm text shown to user by mhttpd&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_debug?                  | get current value of mjsonrpc_debug&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of mjsonrpc_debug&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_http_trace?             | get current value of mhttpd http_trace&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of http_trace&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_schema?                 | Get the MIDAS JSON-RPC schema JSON object&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | object               | returns the MIDAS JSON-RPC schema JSON object&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_sleep?                  | get current value of mjsonrpc_sleep&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of mjsonrpc_sleep&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_time?                   | get current value of mjsonrpc_time&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of mjsonrpc_time&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
get_timezone?               | get current server timezone offset in seconds&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | offset in seconds&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_active_events?       | get list of active history events using hs_read_event_list()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of hs_read_event_list()&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_channels?            | get list of history channels in /Logger/History&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of hs_get_events()&lt;br /&gt;
                            |          | default_channel      | string         | name of the default logger history channel in /History/LoggerHistoryChannel&lt;br /&gt;
                            |          | channels[]           | logger history channel names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_events?              | get list of history events that existed at give time using hs_get_events()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | time?                | number         | timestamp, value 0 means current time, default is 0&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of hs_get_events()&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_last_written?        | get list of history tags for given history events that existed at give time using hs_get_last_written()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | time?                | number         | timestamp, value 0 means current time, default is 0&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | last_written[]       | array of last-written times for each history event&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_get_tags?                | get list of history tags for given history events that existed at give time using hs_get_tags()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | time?                | number         | timestamp, value 0 means current time, default is 0&lt;br /&gt;
                            |          | events[]?            | array of history event names, default is get all events using hs_get_events()&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | events[]             | array of history event names for each history event&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | events[]             | array of status ohistory tags for each history event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | events[]             | array of history tags for each history event&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | events[]             | history tag name&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | events[]             | history tag midas data type&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | events[]             | history tag number of array elements, omitted if 1&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_image_retrieve?          | Get a list of history image files&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | image?               | string         | image name as defined under /History/Images/&amp;lt;image&amp;gt;&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | time[]               | array of time stamps in seconds&lt;br /&gt;
                            |          |                      | array of       | arraybuffer&lt;br /&gt;
                            |          | filename[]           | array of file names&lt;br /&gt;
                            |          |                      | array of       | arraybuffer&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_read?                    | get history data for given history events that existed at give time using hs_read_buffer()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | data[]               | array of history data&lt;br /&gt;
                            |          |                      | array of       | array      &lt;br /&gt;
                            |          | data[]               | status for each event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | number of data for each event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | time data&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | value data&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_read_arraybuffer?        | get history data for given history events that existed at give time using hs_read_buffer()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | binary data          | arraybuffer    | binary data, see documentation&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_read_binned?             | get history data for given history events that existed at give time using hs_read_buffer()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            |          | num_bins             | integer        | number of time bins&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
                            |          | data[]               | array of history data&lt;br /&gt;
                            |          |                      | array of       | array      &lt;br /&gt;
                            |          | data[]               | status for each event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | number of data points for each event&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | number of data points for each bin&lt;br /&gt;
                            |          |                      | array of       | integer    &lt;br /&gt;
                            |          | data[]               | mean for each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | rms for each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | minimum value for each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | maximum value for each bin&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | time of last data entry&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
                            |          | data[]               | value of last data entry&lt;br /&gt;
                            |          |                      | array of       | number     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_read_binned_arraybuffer? | get history data for given history events that existed at give time using hs_read_buffer()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            |          | start_time           | number         | start time of the data&lt;br /&gt;
                            |          | end_time             | number         | end time of the data&lt;br /&gt;
                            |          | num_bins             | integer        | number of time bins&lt;br /&gt;
                            |          | events[]             | array of history event names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | tags[]               | array of history event tag names&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | index[]              | array of history event tag array indices&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | binary data          | arraybuffer    | binary data, see documentation&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
hs_reopen?                  | reopen the history channel to make sure we see the latest list of events using hs_clear_cache()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | channel?             | string         | midas history channel, default is the default reader channel&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of hs_get_events()&lt;br /&gt;
                            |          | channel              | string         | logger history channel name&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
jrpc?                       | make RPC call into frontend program via RPC_JRPC&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | client_name          | string         | Connect to this MIDAS client, see cm_connect_client()&lt;br /&gt;
                            |          | cmd                  | string         | Command passed to client&lt;br /&gt;
                            |          | args                 | string         | Parameters passed to client as a string, could be JSON encoded&lt;br /&gt;
                            |          | max_reply_length?    | integer        | Optional maximum length of client reply. MIDAS RPC does not support returning strings of arbitrary length, maximum length has to be known ahead of time.&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | reply                | string         | Reply from client as a string, could be JSON encoded&lt;br /&gt;
                            |          | status               | integer        | return status of cm_connect_client() and rpc_client_call()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
null?                       | RPC method always returns null&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | method parameters are ignored&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | null                 | always returns null&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
seq_list_files?             | js_seq_list_files&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | subdir               | string         | List files in /Seq/State/Path/subdir&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | path                 | string         | Search path&lt;br /&gt;
                            |          | subdirs[]            | list of subdirectories&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | files[]              | script filename&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
                            |          | files[]              | script description&lt;br /&gt;
                            |          |                      | array of       | string     &lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
seq_save_script?            | js_seq_save_script&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | filename             | string         | Script file name&lt;br /&gt;
                            |          | script               | string         | Script text&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of midas library calls&lt;br /&gt;
                            |          | error                | string         | error text&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
set_debug?                  | set new value of mjsonrpc_debug&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | integer              | new value of mjsonrpc_debug&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | new value of mjsonrpc_debug&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
set_http_trace?             | set new value of mhttpd http_trace&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | integer              | new value of http_trace&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | new value of http_trace&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
set_sleep?                  | set new value of mjsonrpc_sleep&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | integer              | new value of mjsonrpc_sleep&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | new value of mjsonrpc_sleep&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
set_time?                   | set new value of mjsonrpc_time&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | integer              | new value of mjsonrpc_time&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | new value of mjsonrpc_time&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
ss_millitime?               | get current MIDAS time using ss_millitime()&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | any                  | there are no input parameters&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | integer              | current value of ss_millitime()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
start_program?              | start MIDAS program defined in ODB /Programs/name&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | name                 | string         | name of the program, corresponding to ODB /Programs/name&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | return status of ss_system()&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
user_example1?              | example of user defined RPC method that returns up to 3 results&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | arg                  | string         | example string argment&lt;br /&gt;
                            |          | optional_arg?        | integer        | optional example integer argument&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | string               | string         | returns the value of &amp;quot;arg&amp;quot; parameter&lt;br /&gt;
                            |          | integer              | integer        | returns the value of &amp;quot;optional_arg&amp;quot; parameter&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
user_example2?              | example of user defined RPC method that returns more than 3 results&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | arg                  | string         | example string argment&lt;br /&gt;
                            |          | optional_arg?        | integer        | optional example integer argument&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | string1              | string         | returns the value of &amp;quot;arg&amp;quot; parameter&lt;br /&gt;
                            |          | string2              | string         | returns &amp;quot;hello&amp;quot;&lt;br /&gt;
                            |          | string3              | string         | returns &amp;quot;world!&amp;quot;&lt;br /&gt;
                            |          | value1               | integer        | returns the value of &amp;quot;optional_arg&amp;quot; parameter&lt;br /&gt;
                            |          | value2               | number         | returns 3.14&lt;br /&gt;
-----------------------------------------------------------------------------------------------&lt;br /&gt;
user_example3?              | example of user defined RPC method that returns an error&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | params   | arg                  | integer        | integer value, if zero, throws a JSON-RPC error&lt;br /&gt;
                            | -----------------------------------------------------------------&lt;br /&gt;
                            | result   | status               | integer        | returns the value of &amp;quot;arg&amp;quot; parameter&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:Javascript library]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=/Equipment_ODB_tree&amp;diff=3371</id>
		<title>/Equipment ODB tree</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=/Equipment_ODB_tree&amp;diff=3371"/>
		<updated>2023-10-11T22:21:35Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: /* Common subtree */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* [[Frontend user code]] &lt;br /&gt;
* [[Event Structure]]&lt;br /&gt;
* [[Equipment List Parameters]]&lt;br /&gt;
* &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Purpose  ==&lt;br /&gt;
The ODB /Equipment tree contains user and system information related to a frontend.  &lt;br /&gt;
&lt;br /&gt;
== Creating the /Equipment tree ==&lt;br /&gt;
An empty /Equipment tree will be created by mlogger.&lt;br /&gt;
&lt;br /&gt;
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&lt;br /&gt;
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]]. &lt;br /&gt;
&lt;br /&gt;
== Example of /Equipment tree ==&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&lt;br /&gt;
 [local:pol:S]Settings&amp;gt;ls /Equipment -l&lt;br /&gt;
 Key name                        Type    #Val  Size  Last Opn Mode Value&lt;br /&gt;
 ---------------------------------------------------------------------------&lt;br /&gt;
 POL_ACQ                         DIR&lt;br /&gt;
 Scaler                          DIR&lt;br /&gt;
 Trigger                         DIR&lt;br /&gt;
 VME                             DIR&lt;br /&gt;
 SisPulser                       DIR&lt;br /&gt;
 INFO                            DIR&lt;br /&gt;
 HISTO                           DIR&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following is a listing of one of the equipments in the /Equipment tree shown above :&lt;br /&gt;
&lt;br /&gt;
 [local:pol:S]Settings&amp;gt;ls /Equipment/info/ -lr                &lt;br /&gt;
 Key name                        Type    #Val  Size  Last Opn Mode Value&lt;br /&gt;
 ---------------------------------------------------------------------------&lt;br /&gt;
 INFO                            DIR&lt;br /&gt;
    Common                      DIR&lt;br /&gt;
        Event ID                WORD    1     2     9h   0   RWD  3&lt;br /&gt;
        Trigger mask            WORD    1     2     9h   0   RWD  8&lt;br /&gt;
        Buffer                  STRING  1     32    9h   0   RWD  SYSTEM&lt;br /&gt;
        Type                    INT     1     4     9h   0   RWD  1&lt;br /&gt;
        Source                  INT     1     4     9h   0   RWD  0&lt;br /&gt;
        Format                  STRING  1     8     9h   0   RWD  MIDAS&lt;br /&gt;
        Enabled                 BOOL    1     4     9h   0   RWD  y&lt;br /&gt;
        Read on                 INT     1     4     9h   0   RWD  257&lt;br /&gt;
        Period                  INT     1     4     9h   0   RWD  100&lt;br /&gt;
        Event limit             DOUBLE  1     8     9h   0   RWD  0&lt;br /&gt;
        Num subevents           DWORD   1     4     9h   0   RWD  0&lt;br /&gt;
        Log history             INT     1     4     2h   0   RWD  60&lt;br /&gt;
        Frontend host           STRING  1     32    9h   0   RWD  lxpol.triumf.ca&lt;br /&gt;
        Frontend name           STRING  1     32    9h   0   RWD  pol_fevme&lt;br /&gt;
        Frontend file name      STRING  1     256   9h   0   RWD  pol_fevme.cxx&lt;br /&gt;
        Status                  STRING  1     256   9h   0   RWD  pol_fevme@lxpol.triumf.ca&lt;br /&gt;
        Status color            STRING  1     32    9h   0   RWD  #00FF00&lt;br /&gt;
        Hidden                  BOOL    1     4     9h   0   RWD  n&lt;br /&gt;
&amp;lt;div id=&amp;quot;Example Variables subtree&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    Variables                   DIR&lt;br /&gt;
        DBUG                    FLOAT   9     4     2h   0   RWD&lt;br /&gt;
                                        [0]             0&lt;br /&gt;
                                        [1]             101&lt;br /&gt;
                                        [2]             10201&lt;br /&gt;
                                        [3]             10201&lt;br /&gt;
                                        [4]             101&lt;br /&gt;
                                        [5]             2&lt;br /&gt;
                                        [6]             4&lt;br /&gt;
                                        [7]             1&lt;br /&gt;
                                        [8]             1&lt;br /&gt;
&lt;br /&gt;
        CYCL                    FLOAT   15    4     2h   0   RWD&lt;br /&gt;
                                        [0]             1&lt;br /&gt;
                                        [1]             9800&lt;br /&gt;
                                        [2]             98&lt;br /&gt;
                                        [3]             100&lt;br /&gt;
                                        [4]             9&lt;br /&gt;
                                        [5]             98&lt;br /&gt;
                                        [6]             9800&lt;br /&gt;
                                        [7]             9&lt;br /&gt;
                                        [8]             9&lt;br /&gt;
                                        [9]             8.9682&lt;br /&gt;
                                        [10]            8.966&lt;br /&gt;
                                        [11]            0.0504&lt;br /&gt;
                                        [12]            0&lt;br /&gt;
                                        [13]            8.7906&lt;br /&gt;
                                        [14]            0&lt;br /&gt;
        SUMS                    DOUBLE  4     8     2h   0   RWD&lt;br /&gt;
                                        [0]             0&lt;br /&gt;
                                        [1]             50002&lt;br /&gt;
                                        [2]             0&lt;br /&gt;
                                        [3]             0&lt;br /&gt;
&amp;lt;div id=&amp;quot;Example Statistics subtree&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    Statistics                  DIR&lt;br /&gt;
        Events sent             DOUBLE  1     8     8h   0   RWD  176&lt;br /&gt;
        Events per sec.         DOUBLE  1     8     8h   0   RWD  0&lt;br /&gt;
        kBytes per sec.         DOUBLE  1     8     8h   0   RWD  0&lt;br /&gt;
&amp;lt;div id=&amp;quot;Example Settings subtree&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    Settings                    DIR&lt;br /&gt;
        Names DBUG              STRING  12    32    9h   0   RWD  &lt;br /&gt;
                                        [0]             Data still buffered&lt;br /&gt;
                                        [1]             Num SIS LNE events&lt;br /&gt;
                                        [2]             LNE count from SIS&lt;br /&gt;
                                        [3]             LNE preset value&lt;br /&gt;
                                        [4]             number of bins&lt;br /&gt;
                                        [5]             data bytes code&lt;br /&gt;
                                        [6]             num enabled channels&lt;br /&gt;
                                        [7]             discard first bin&lt;br /&gt;
                                        [8]             discard first cycle&lt;br /&gt;
                                        [9]             not used&lt;br /&gt;
                                        [10]            not used&lt;br /&gt;
                                        [11]            not used&lt;br /&gt;
        Names CYCL              STRING  15    32    9h   0   RWD  &lt;br /&gt;
                                        [0]             Scan type code&lt;br /&gt;
                                        [1]             Number good cycles&lt;br /&gt;
                                        [2]             Number supercycles&lt;br /&gt;
                                        [3]             Cycle num within supercycle&lt;br /&gt;
                                        [4]             Sweep number&lt;br /&gt;
                                        [5]             Num cycles skipped&lt;br /&gt;
                                        [6]             Num cycles histogrammed&lt;br /&gt;
                                        [7]             DAC Increment number&lt;br /&gt;
                                        [8]             DAC Set Value (volts)&lt;br /&gt;
                                        [9]             DAC Readback (volts)&lt;br /&gt;
                                        [10]            ADC1 Average&lt;br /&gt;
                                        [11]            ADC2 Average&lt;br /&gt;
                                        [12]            ADC3 Average&lt;br /&gt;
                                        [13]            ADC4 Average&lt;br /&gt;
                                        [14]            spare&lt;br /&gt;
&lt;br /&gt;
        Names SUMS              STRING  4     32    9h   0   RWD  &lt;br /&gt;
                                        [0]             SC Sum ch17&lt;br /&gt;
                                        [1]             SC Sum ch18&lt;br /&gt;
                                        [2]             SC Sum ch19&lt;br /&gt;
                                        [3]             SC Sum ch20&lt;br /&gt;
&lt;br /&gt;
== Keys in the  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;/Equipment&#039;&#039;&amp;lt;/span&amp;gt; ODB tree  ==&lt;br /&gt;
&lt;br /&gt;
===  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;&amp;lt;equipment-name&amp;gt;&#039;&#039;&amp;lt;/span&amp;gt; subtree  ===&lt;br /&gt;
This subtree in the [[#top|/Equipment ODB tree]] The [[#top|/Equipment ODB tree]] &lt;br /&gt;
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 &amp;lt;equipment-name&amp;gt; subtree will be present for every defined equipment in all the frontend(s) run on the experiment. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;initial values&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
This subtree will be created (if it does not already exist) the first time the defining frontend is run, and the &#039;&#039;&#039;initial values&#039;&#039;&#039; set are defined in the  [[Equipment List Parameters]] in the [[Frontend user code]] that defines this particular equipment.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; that if these parameters are later changed in the ODB, they will &#039;&#039;&#039;not&#039;&#039;&#039; be overwritten by the values in the frontend, if the frontend is restarted.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Common&#039;&#039;&amp;lt;/span&amp;gt; subtree  ====&lt;br /&gt;
This subtree in an [[#&amp;lt;equipment-name&amp;gt; subtree|/Equipment/&amp;lt;equipment-name&amp;gt; subtree]] contains the parameters used by the system to process the  [[Frontend Operation#Equipments and Events|Equipment]]. &lt;br /&gt;
&lt;br /&gt;
The initial values of the Common parameters are set when this tree is created (see [[#initial value|initial values]]).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Event ID&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; WORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] contains the Event ID of this equipment. See &#039;&#039;&#039;[[Equipment List Parameters#EventID|EventID]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Trigger mask&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; WORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;   See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] contains the Trigger mask of this equipment.   See &#039;&#039;&#039;[[Equipment List Parameters#TriggerMask|TriggerMask]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Buffer&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] contains the Buffer parameter of this equipment.   See &lt;br /&gt;
&#039;&#039;&#039;[[Equipment List Parameters#Buffer|Buffer]]&#039;&#039;&#039; for details.&lt;br /&gt;
For Events that are to be logged as banks, Buffer is set to &amp;quot;SYSTEM&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Type&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] contains the Type of this equipment.  See &#039;&#039;&#039;[[Equipment List Parameters#Type|Type]]&#039;&#039;&#039; for details.&lt;br /&gt;
The Type is one of the defined &#039;&#039;&#039;Equipment Flags&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Source&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] contains the Interrupt or Event Source of this equipment.   See &#039;&#039;&#039;[[Equipment List Parameters#Source|Source]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Format&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] contains the data format used for generating the event of this equipment. See &#039;&#039;&#039;[[Equipment List Parameters#Format|Format]]&#039;&#039;&#039; for details.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Enabled&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] indicated whether this equipment is active. See &#039;&#039;&#039;[[Equipment List Parameters#Enabled|Enabled]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Read on&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 &#039;&#039;&#039;[[Equipment List Parameters#ReadOn|Read on]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Period&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] specifies the time interval or time-out (units are milliseconds). See &#039;&#039;&#039;[[Equipment List Parameters#Period|Period]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Event limit&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DOUBLE&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] specifies the time interval or time-out. See &#039;&#039;&#039;[[Equipment List Parameters#Event limit|Event limit]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Num subevents&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DWORD&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] enables the super-event capability. See &#039;&#039;&#039;[[Equipment List Parameters#Number of subevents|Number of subevents]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Log history&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; See [[#initial value|initial value]]&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] enables the [[History System]] for this equipment. See &#039;&#039;&#039;[[Equipment List Parameters#Log History|Log History]]&#039;&#039;&#039; for details.&lt;br /&gt;
A value of 0 disables history logging. This parameter also controls how frequently the history events are generated.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Frontend host&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
It is filled by the system.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Frontend name&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
It is filled by the system.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Frontend file name&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] contains the filename of the [[Frontend user code]] that defines this particular equipment.&lt;br /&gt;
It is filled by the system.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Status&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039; &lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Status color&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;#00FF00&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Hidden&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; BOOL&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  &amp;quot;n&amp;quot;&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This key in the [[#Common subtree|Common subtree]] if set to &amp;quot;y&amp;quot; will prevent the display of this equipment on the  [[Status Page#mhttpd status page]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Write cache size&#039;&#039;&amp;lt;/span&amp;gt;  =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; INT&lt;br /&gt;
* &#039;&#039;&#039;Default:&#039;&#039;&#039;  10000000&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
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&#039;re sending large events. The minimum (and default) value is 10MB (10000000 bytes).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Variables&#039;&#039;&amp;lt;/span&amp;gt; subtree ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This is a subtree  in the [[#Top|/Equipment ODB Tree]].&lt;br /&gt;
&lt;br /&gt;
If either&lt;br /&gt;
* the [[#Read on|Read on]] parameter contains the [[ReadOn Flags|RO_ODB flag]] or &lt;br /&gt;
* [[#Log history|Log history]] is non-zero, i.e. History logging is enabled &lt;br /&gt;
the event data will be copied to the ODB and listed in this subtree. &lt;br /&gt;
&lt;br /&gt;
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]].&lt;br /&gt;
See [[#Example Variables subtree|example]] above.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Statistics&#039;&#039;&amp;lt;/span&amp;gt; subtree ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This subtree  in the [[#Top|/Equipment ODB Tree]] contains statistics written by the system.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Events sent&#039;&#039;&amp;lt;/span&amp;gt; =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DOUBLE&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
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.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Events per sec&#039;&#039;&amp;lt;/span&amp;gt; =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DOUBLE&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;kBytes per sec&#039;&#039;&amp;lt;/span&amp;gt; =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DOUBLE&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
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]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Settings&#039;&#039;&amp;lt;/span&amp;gt; subtree ====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; DIR&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This subtree  in the [[#Top|/Equipment ODB Tree]] contains user-defined settings, some of which may be used by the system.&lt;br /&gt;
Any values defined in this subtree will appear in [[Event Notification (Hot-Link)#experim_dot_h|experim.h]] files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Names&#039;&#039;&amp;lt;/span&amp;gt; and &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Names &amp;lt;variable&amp;gt;&#039;&#039;&amp;lt;/span&amp;gt; =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING  (ARRAY)&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
These optional key(s) in the [[#Settings subtree|Settings]] subtree are used to define the names of variables in the [[#Variables subtree|Variables subtree]].&lt;br /&gt;
&lt;br /&gt;
Say you have an entry in Variables called &amp;quot;CURR&amp;quot; that contains 3 floats. You can then specify the names of these 3 elements here (i.e. if you&#039;re storing the current of 3 channels, you use Names to define the name of each channel).&lt;br /&gt;
&lt;br /&gt;
You can either create a key called &amp;quot;Names&amp;quot; that will apply to all Variables, or create separate keys for each Variable (e.g. &amp;quot;Names CURR&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
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.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Unit &amp;lt;variable&amp;gt;&#039;&#039;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
These optional key(s) in the [[#Settings subtree|Settings]] subtree are used to define the units of variables in the [[#Variables subtree|Variables subtree]].&lt;br /&gt;
&lt;br /&gt;
Say you have an entry in Variables called &amp;quot;CURR&amp;quot;. You can specify that the current is measured in mA by creating a key called &amp;quot;Unit CURR&amp;quot; and setting it to &amp;quot;mA&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
These keys (if present) will be used by the [[Equipment Page]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
---------&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====  &amp;lt;span  style=&amp;quot;color: purple;&amp;quot;&amp;gt;&#039;&#039;Editable&#039;&#039;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; =====&lt;br /&gt;
&amp;lt;div style=&amp;quot;column-count:3;-moz-column-count:3;-webkit-column-count:3&amp;quot;&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Type:&#039;&#039;&#039; STRING&lt;br /&gt;
&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This optional key in the [[#Settings subtree|Settings]] subtree is used to by the [[Equipment Page]] to decide whether to show an &amp;quot;editable&amp;quot; interface for each variable, or just show the present value.&lt;br /&gt;
&lt;br /&gt;
It should contain a comma-separated list of Variables that are allowed to be edited on the [[Equipment Page]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:ODB Tree]] [[Category:Equipment]] [[Category:History]]&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Equipment_Page&amp;diff=3370</id>
		<title>Equipment Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Equipment_Page&amp;diff=3370"/>
		<updated>2023-10-11T22:05:45Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: Created page with &amp;quot;{{Pagelinks}}  = Links = {{mhttpdpages}}  = Purpose = The purpose of the mhttpd equipment pages is to allow the user to view any Variables related to an equipment.   = Acc...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
The purpose of the [[mhttpd]] equipment pages is to allow the user to view any Variables related to an equipment. &lt;br /&gt;
&lt;br /&gt;
= Access the equipment page =&lt;br /&gt;
&lt;br /&gt;
The main [[Status Page]] contains a list of all the equipment in an experiment (with each equipment being defined by a frontend). The name of each equipment on that page is a link to the relevant equipment page.&lt;br /&gt;
&lt;br /&gt;
= Configuring the equipment page =&lt;br /&gt;
&lt;br /&gt;
There are several ODB keys that can be defined in {{Odbpath|path=/Equipment/&amp;lt;equip_name&amp;gt;/}} that affect the behaviour of this webpage. See [[/Equipment_ODB_tree#Settings_subtree|the Equipment ODB tree]] documentation for full details, but in brief:&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Variables/&amp;quot; contains the Variables to show on this page (and to record in the history system).&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Names&amp;quot; or &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Names &amp;lt;variable&amp;gt;&amp;quot; contains the name of each element in the Variables arrays (e.g. channel names if recording the current of multiple channels).&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Unit &amp;lt;variable&amp;gt;&amp;quot; contains the units in which a variable is measured.&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Editable&amp;quot; contains a comma-separated list of variables that can be edited on the Equipment page.&lt;br /&gt;
&lt;br /&gt;
All the settings are optional, and must be created by the user if desired.&lt;br /&gt;
&lt;br /&gt;
= Example configuration and display =&lt;br /&gt;
&lt;br /&gt;
Click to enlarge each image...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery widths=&amp;quot;300px&amp;quot; mode=&amp;quot;packed&amp;quot;&amp;gt;&lt;br /&gt;
Equipment_Variables.png|Variables&lt;br /&gt;
Equipment_Settings.png|Settings&lt;br /&gt;
Equipment_Page.png|Resulting display&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Equipment_Settings.png&amp;diff=3369</id>
		<title>File:Equipment Settings.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Equipment_Settings.png&amp;diff=3369"/>
		<updated>2023-10-11T22:01:44Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Equipment_Variables.png&amp;diff=3368</id>
		<title>File:Equipment Variables.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Equipment_Variables.png&amp;diff=3368"/>
		<updated>2023-10-11T22:01:31Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Equipment_Page.png&amp;diff=3367</id>
		<title>File:Equipment Page.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Equipment_Page.png&amp;diff=3367"/>
		<updated>2023-10-11T22:00:05Z</updated>

		<summary type="html">&lt;p&gt;Bsmith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Bsmith</name></author>
	</entry>
</feed>