Skip to content

See the Python API overview for an introduction to the midas python package. This page contains the full API reference of the midas.client module, which is the main one you will interact with.

Create your client with a with statement

with midas.client.MidasClient("my_program") as client: guarantees that your program disconnects cleanly from midas, and that any disconnect callbacks are called. See Creating a client.

midas.client

This module contains user-friendly pythonic wrappers around the midas C library.

Classes:

  • MidasClient –

    The main class that most users will interact with.

Attributes:

midas.client.MidasClient

This is the main class that contains pythonic wrappers around the midas C library. Most users will interact with this class, the definitions in the main midas module (e.g. midas.TID_WORD), and optionally the midas.frontend and midas.sequencer modules.

Most common midas functions have wrappers defined in this class. For example, the odb_set function allows you to pass normal python values as arguments:

Example

import midas.client

client = midas.client.MidasClient("NormalExample")
client.odb_set("/my/odb_path", 4.56)
Recipe for adding support for more functions

If you want to use a function that doesn't have a python wrapper, you will need to:

  • Define an appropriate C-compatible function in include/midas_c_compat.h and src/midas_c_compat.cxx (e.g. c_my_func()).
  • Recompile using cmake (cd $MIDASSYS/build && make && make install)
  • Call the function from python like client.lib.c_my_func(). You will need to convert any arguments to ctypes values. If the C function doesn't return an integer, then there is even more work to do - see the midas.MidasLib class for details.

Attributes:

  • name (str) –

    The name of this client.

  • lib (MidasLib) –

    The ctypes wrapper of the midas library.

  • hDB (c_int) –

    HNDLE to the experiment's ODB

  • hClient (c_int) –

    HNDLE to this client's entry in the ODB

  • event_buffers (dict of {int) –

    ctypes.c_char_p}) - Character buffers that have been opened, keyed by buffer handle.

brpc_client_call(connection, cmd, args, as_event=False, use_numpy=False, max_len=1000000)

Call the BRPC (aka "binary RPC") function exposed by another client.

For BRPC, the other client returns arbitrary binary data (possibly encoded as a midas event). Use jrpc_client_call if the other client will be returning a string.

Parameters:

  • connection (int) –

    The connection handle returned to you when connecting.

  • cmd (str) –

    The command to execute. The other client's BRPC handler must know what to do with this.

  • args (str) –

    Any other arguments to pass to the other client. Specify as a single string (probably separating arguments with spaces).

  • as_event (bool, default: False ) –

    Whether you expect the data from the other client to be arbitrary binary data, or encoded as a midas event. If True, we'll automatically decode the event for you.

  • use_numpy (bool, default: False ) –

    Is as_event is True, whether to use numpy arrays or regular python tuples for bank data in the received Event.

  • max_len (int, default: 1000000 ) –

    The maximum length you expect the returned data to be (in bytes). This is an imposition of the underlying C++ code, not the python side.

Null bytes

Note that if your cmd or args parameter contains an embedded null byte, the argument will be truncated at that point.

Raises:

  • MidasError –

    if the other client returns a non-SUCCESS status

Returns:

  • ctypes.Array of ctypes.char_c –

    the data populated by the other client if as_event is False.

  • Event –

    the data populated by the other client if as_event is True.

check_deferred_transition()

If we're currently in a deferred transition, call the user's callback function to see if the work has been completed. If so, proceed with the actual transition.

If you are writing a program that registers a deferred transition, but is NOT using the frontend framework, you must call this function repeatedly in your main loop. In the frontend framework it is called for you automatically.

client_exists(client_name, exact_match=True)

Check whether a client/program is running.

Note that this function does not consider the client that is performing the request (i.e. if program "mytest" asks if "mytest" is running, this function will return False!).

Parameters:

  • client_name (str) –

    name of the other client

  • exact_match (bool, default: True ) –

    If False, we'll look for all clients that start with this name. E.g. "myfe" would match "myfe_1", "myfetest" etc. If True, we require an exact match of the name.

Returns:

  • bool –

    Whether this client is running

clients_exist(client_names, return_as_dict=False, exact_match=True)

Check whether multiple clients/programs are running.

Parameters:

  • client_names (list of str) –

    names of the clients to check.

  • return_as_dict (bool, default: False ) –

    whether to return a single boolean or a dict with the per-client status.

  • exact_match (bool, default: True ) –

    If False, we'll look for all clients that start with the name. E.g. "myfe" would match "myfe_1", "myfetest" etc. If True, we require an exact match of the name.

Returns:

  • dict of {str: bool} –

    If return_as_dict is True: whether each client is running or not.

  • boolean –

    If return_as_dict is False: whether all the clients/ programs are running or not. If any client/program is not running, we'll return False.

communicate(time_ms, return_immediately_if_work_done=False)

If you have a long-running script, this function should be called repeatedly in a loop. It serves several purposes:

  • letting midas know the client is still working
  • allowing users to stop your program from the "Programs" webpage
  • telling your client about any ODB changes that it opened a hotlink for
  • letting your client know about run transitions
  • and more...

Parameters:

  • time_ms (int) –

    How many millisecs to wait before returning from this function. If negative, will block until some work is done, or we're told to shut down.

  • return_immediately_if_work_done (bool, default: False ) –

    Return immediately if we did some work(). By default we'll keep waiting for more work until the full user-specified time has expired.

connect_to_other_client(other_client_name)

Connect to a different midas client, so that you can later call RPC functions exposed by that client.

Parameters:

  • other_client_name (str) –

    name of the other client (e.g. as shown in the Programs webpage)

Returns:

create_alarm_class(class_name, execute_command='', execute_interval_secs=0, stop_run=False)

Alarm classes define what to do when an alarm is triggered. All alarms belong to a single class. You can create as many classes as you like. The alarm classes "Alarm" and "Warning" come pre-defined by midas.

Parameters:

  • class_name (str) –

    The name of this alarm class. An entry will be created in the ODB at /Alarms/Classes/<class_name> if it doesn't already exist. If the entry does exist, it will be overwritten with the values you provide here.

  • execute_command (str, default: '' ) –

    System command to run when an alarm with this class is triggered (e.g. "python /path/to/some/script.py" or "~/path/to/script.sh")

  • execute_interval_secs (int, default: 0 ) –

    How often to run the above command, in seconds. NOTE - if this is 0, THE COMMAND WILL NOT BE RUN AT ALL! If you just want the command to be run once, when the alarm is first raised, set a very high value like 1e9 (but < 2^31 as the value is a signed 32-bit int in the ODB).

  • stop_run (bool, default: False ) –

    Whether to stop the run when an alarm of this class is triggered.

create_evaluated_alarm(alarm_name, odb_condition, message=None, alarm_class='Alarm', activate_immediately=True)

Create an alarm that will trigger if a condition in the ODB is met.

To deactivate this alarm, set /Alarms/Alarms/<alarm_name>/Active to False in the ODB. To completely delete this alarm, just delete /Alarms/Alarms/<alarm_name> from the ODB.

Parameters:

  • alarm_name (str) –

    The name of this alarm. An entry will be created in the ODB at /Alarms/Alarms/<alarm_name> if it doesn't already exist. Max 31 characters, and no "/" characters allowed.

  • odb_condition (str) –

    What to check for. Examples are "/Path/to/float < 0.3" and "/Path/to/boolean == y". Max 256 characters.

  • message (str, default: None ) –

    The message to be displayed to users. Max 80 characters. If not set, the message will be the same as odb_condition.

  • alarm_class (str, default: 'Alarm' ) –

    The "class" of this alarm (i.e. what to do when this alarm is triggered). See create_alarm_class for more about alarm classes.

  • activate_immediately (bool, default: True ) –

    Whether to activate this alarm immediately or not. (This doesn't mean it will actually trigger, just that midas will start evaluating whether to trigger - "active" and "triggered" have different meanings).

Raises:

  • ValueError –

    If invalid alarm or class name provided

deregister_disconnect_callback()

De-register a function that was previously passed to register_disconnect_callback.

deregister_event_request(buffer_handle, request_id)

Cancel an event request that had previously been opened with register_event_request.

Parameters:

deregister_message_callback()

Unregister a callback that was registered with register_message_callback.

deregister_transition_callback(transition)

Deregister functions previously registered with register_transition_callback, so they are no longer called.

Parameters:

  • transition (int) –

    One of midas.TR_START, midas.TR_STOP, midas.TR_PAUSE, midas.TR_RESUME, midas.TR_STARTABORT.

disconnect()

Nicely disconnect from midas. If this function isn't called at the end of a program, midas will complain in the message log and it might take a while for our disappearance to be noted.

This function will be called automatically from the destructor of this class, HOWEVER some implementations of python do not guarantee that del will be called at system exit.

To be sure of a trouble-free disconnection from midas, you should either:

  • Call this function at the end of your program.
  • Wrap your usage in a with statement: with midas.client.MidasClient('name') as c: .......

disconnect_from_other_client(connection, shutdown_other_client=False)

Disconnect from a client that was previously connected to using connect_to_other_client.

Parameters:

  • connection (int) –

    The connection handle returned to you when connecting.

  • shutdown_other_client (bool, default: False ) –

    Whether to cause the other client to exit.

exec(command)

Execute a command in a separate process.

Uses fork() and execl() system calls to run the subprocess.

Parameters:

  • command (str) –

    Command to execute, e.g. the path to a frontend executable.

Returns:

  • int –

    the PID of the spawned process. Most users won't care about this.

get_all_required_program_names()

Get all the clients/programs listed in /Programs in the ODB that have the "Required" key set to True.

Returns:

get_experiment_dir()

Get the "experiment directory" on disk. Same as cm_get_path() in C++ code.

Returns:

  • str –

    Location

get_message_facilities()

Get a list of "facilties" that send messages to the message log. By default the "midas" facility is used when sending messages, but others can be used. The facility name can be used when calling get_recent_messages.

Returns:

  • list of string –

get_midas_version()

Get the version of midas.

Returns:

  • str –

    midas version

  • str –

    git revision

get_recent_messages(min_messages=1, before=None, facility='midas')

Get a list of messages that were sent to the midas message log before a certain time.

The number of messages returned is based on:
  • Search until we find min_messages
  • If there are more messages with the same UNIX timestamp (to 1s resolution), include those messages as well.
  • If log files are being split by date, and we've had to look back through several files without finding anything, give up.

Parameters:

  • min_messages (int, default: 1 ) –

    Minimum number of messages to try to retrieve.

  • before ((datetime, number or None), default: None ) –

    Start searching for messages before the specified time. None means get the most recent. A number is interpreted as a UNIX timestamp.

  • facility (str, default: 'midas' ) –

    The "facility" to search in. By default most messages are written to the "midas" facility.

Returns:

  • list of string –

    messages, with the most recent one first

get_triggered_alarms()

Get all the alarms that have been triggered but not yet reset.

Returns:

  • dict of {str: str} –

    key is the alarm name, value is the alarm message.

Raises:

  • KeyError –

    if alarms system is not correctly configured in ODB.

hist_create_plot(group_name, panel_name, variables, labels=None)

Helper function for adding a plot to the midas history display. It requires you to know how midas refers to different variables within the history system, so is an expert-level function (or create one plot manually, see the pattern of variable names used in the ODB for that plot, then infer what the names would be for future plots).

Parameters:

  • group_name (str) –

    The group this plot belongs to (used to organise plots on the main "History" webpage).

  • panel_name (str) –

    The name of this plot (called a panel name as you can view all the plots in a group on a single page with multiple panels).

  • variables (list of str) –

    The variables to show, as referred to by the internal midas history system. Combines both the "event name" and "tag name" into a single string. E.g. "Lakeshore:L336[0]" or "MyEvent:Custom tag name".

  • labels (list of str, default: None ) –

    Human readable name for each variable (optional).

Raises:

  • KeyError –

    if a plot already exists with this name

  • ValueError –

    if there is a problem with the other arguments

  • TypeError –

    if there is a problem with the other arguments

hist_get_data(start_time, end_time, interval_secs, event_name, tag_name, index=None, timestamps_as_datetime=False)

Read data from the history system.

Parameters:

  • start_time (int / float / datetime) –

    Start of the time period to retrieve data for. If a number, it's interpreted as a UNIX timestamp.

  • end_time (int / float / datetime) –

    End of the time period to retrieve data for. If a number, it's interpreted as a UNIX timestamp.

  • interval_secs (int / float) –

    Return at most one data point per N seconds.

  • event_name (str) –

    Name of the midas history "event" to query. Use hist_get_events to find valid event names.

  • tag_name (str) –

    Name of the midas history "tag" to query. Use hist_get_tags to find valid tag names for a given event.

  • index (int / None, default: None ) –

    If the tag represents an array, you can ask to get data for a specific element in that array. If None, you'll get data for all the elements in the array. The length of the array is returned as part of hist_get_tags.

  • timestamps_as_datetime (bool, default: False ) –

    Whether to convert timestamps in the return value to datetime.datetime objects, or leave them as integer UNIX timestamps (the default).

Integer timestamps

Note that the midas history system works at 1-second resolution. If you pass start/end/interval times that are fractional, they will be rounded down to the nearest integer. Intervals must be at least 1 second long.

Returns:

  • list of dicts –

    One dict per event/tag/index combination. The dict has the following keys:

    {
        "event_name": str, 
        "tag_name": str, 
        "index": int, 
        "num_entries": int,
        "status": int,
        "timestamps": list of int/datetime.datetime,
        "values": list of float
    }
    

    • "index" is the element index if a tag represents an array of data.
    • "num_entries" is how many timestamp/value pairs were found.
    • "status" is a midas status code for this event/tag/index combination. 1 indicates SUCCESS. 702 indicates data we couldn't find any data. 707 indicates either the variable does not exist at all, or it didn't exist during the time period you asked for (the different backend drivers treat this slightly differently). Any other status codes will result in an exception being thrown.
    • "timestamps" is the timestamp of each data point found.
    • "values" is the value of each data point found.

    The timestamps and values arrays will be the same length.

Raises:

  • ValueError –

    If there's a problem with the time/index arguments.

  • MidasError –

    If the C++ side had a problem reading data.

hist_get_events()

Get a list of valid history "events". Use the event name when calling hist_get_tags or hist_get_data.

Returns:

  • list of string –

hist_get_recent_data(num_hours, interval_secs, event_name, tag_name, index=None, timestamps_as_datetime=False)

Convenience wrapper around hist_get_data for getting the most recent N hours of data from the history system.

Parameters:

  • num_hours (int / float) –

    The number of hours of data to retrieve

For all other parameters, see hist_get_data

Returns:

hist_get_tags(event_name)

Get a list of valid history "tags" for a given history "event". Find valid event names using hist_get_events. Use event name and tag name when calling hist_get_data.

Returns:

  • list of dicts –

    like [{"name": string, "type": int, "n_data": int}] where

    • "name" is the tag name that can be passed to
    • "type" is the midas type (e.g. TID_DOUBLE = 10),
    • "n_data" is the number of entries if it's an array.

jrpc_client_call(connection, cmd, args, max_len=1024)

Call the JRPC (aka "javascript RPC") function exposed by another client.

For JRPC, the other client returns a string of data. Use brpc_client_call if the other client will be returning arbitrary binary data.

Parameters:

  • connection (int) –

    The connection handle returned to you when connecting.

  • cmd (str) –

    The command to execute. The other client's JRPC handler must know what to do with this.

  • args (str) –

    Any other arguments to pass to the other client. Specify as a single string (probably separating arguments with spaces, or encoding your arguments as JSON).

  • max_len (int, default: 1024 ) –

    The maximum length you expect the returned string to be. This is an imposition of the underlying C++ code, not the python side.

Null bytes

Note that if your cmd or args parameter contains an embedded null byte, the argument will be truncated at that point.

Returns:

  • str –

    the string populated by the other client. Note that an exception will be raised if the other client returns a non-SUCESSS status.

manual_trig_client_call(connection, event_id)

Call the MANUAL_TRIG function exposed by another client.

Parameters:

  • connection (int) –

    The connection handle returned to you when connecting with connect_to_other_client().

  • event_id (int) –

    Equipment event ID to trigger on other client.

msg(message, is_error=False, facility='midas', from_logging_record=None)

Send a message into the midas message log.

These messages are stored in a text file, and are visible on the the "messages" webpage.

Parameters:

  • message (str) –

    The actual message.

  • is_error (bool, default: False ) –

    Whether this message is informational or an error message. Error messages are highlighted in red on the message page.

  • facility (str, default: 'midas' ) –

    The log file to write to. Vast majority of messages are written to the "midas" facility.

  • from_logging_record (LogRecord, default: None ) –

    If this is coming from a log handler, we can grab the filename/line number of the caller from that record, rather than via stack inspection. Users should almost certainly leave this as None.

odb_chmod(path, mode, recurse=True)

Set permissions on an ODB key, and optionally all sub-keys if path is a directory.

The default permissions of new ODB keys have read/write/delete enabled.

Example

To allow read/write, but not delete:

client.odb_chmod("/Some/path", midas.MODE_READ|midas.MODE_WRITE)

which is equivalent to

client.odb_chmod("/Some/path", 3)

Parameters:

  • path (str) –

    e.g. "/Runinfo/State"

  • mode (int) –

    Combination of midas.MODE_READ/midas.MODE_WRITE/midas.MODE_DELETE to allow

  • recurse (bool, default: True ) –

    Recurse into sub-keys if path is a directory

odb_delete(path, follow_links=False)

Delete the given location in the ODB (and all of its children if the specified path points to a directory).

Parameters:

  • path (str) –

    e.g. "/Runinfo/State"

  • follow_links (bool, default: False ) –

    Whether to follow symlinks in the ODB or not. If False, we'll delete the link itself, rather than the target of the link.

odb_exists(path)

Whether the given path exists in the ODB.

Parameters:

  • path (str) –

    e.g. "/Runinfo/State"

Returns:

Raises:

  • `midas.MidasError` –

    (with code midas.status_codes["DB_INVALID_LINK"]) if the path exists as a link, but the target of the link does not exist any more.

odb_get(path, recurse_dir=True, include_key_metadata=False, just_key_list=False)

Get the content of an ODB entry, with automatic conversion to appropriate python types. You can request either a single ODB key or an entire directory (in which case a dict will be returned).

Dictionary ordering

Midas directories have an associated order to them (you can rearrange the order of entries in a midas directory by calling db_reorder_key in the C library). Until python3.7, basic python dicts did not have a well-defined order. Therefore we return a collections.OrderedDict rather than a basic dict, so the order in which you iterate over the keys will match the order in which you see them on the midas status page, for example.

ODB links

odb_get will always try to follow any symlinks in the ODB.

  • If the target of the link exists, the result will be transparent (e.g. the key metadata will show the info of the target, not the link itself).
  • If you try to read a broken link, a midas.MidasError exception will be raised (with code midas.status_codes["DB_INVALID_LINK"]).
  • If you try to read an ODB directory that contains a broken link, an exception will NOT be raised, and the returned dict will contain the details of the link, rather than the target.

Parameters:

  • path (str) –

    e.g. "/Runinfo". You may specify a single array index if desired (e.g. "/Path/To/My/Array[1]").

  • recurse_dir (bool, default: True ) –

    If path points to a directory, whether to recruse down and get all entries beneath that directory, or just the entries immediately below the specified path. recurse_dir being False acts more like an ls of the directory, while it being True acts more like a full dump of the content.

  • include_key_metadata (bool, default: False ) –

    Whether to include extra information in the return value containing metadata about each ODB key. Examples metadata include when the ODB key was last written, the data type (e.g. TID_INT) etc. The metadata is stored in extra dict entries named as "/key" (e.g. if the actual ODB content is in "State", the metadata is in "State/key").

  • just_key_list (bool, default: False ) –

    Just "ls" an ODB directory - don't recurse, don't return values - just list the key names.

Returns:

  • dict –

    If path points to a directory and/or include_key_metadata is True. Dicts are keyed by the ODB key name. If recurse_dir is False and one of the entries in path is also a directory, it's value will be an empty dict. If include_key_metadata is True, the keys at the top level will be the "directory_name" and "directory_name/key"

  • int/float/bool etc, or a list of them –

    If path points to a single ODB key and include_key_metadata is False.

  • list of strings –

    If just_key_list is True, and path points to a directory.

Raises:

  • KeyError –

    if path doesn't exist in the ODB

  • MidasError –

    for other midas-related issues

  • ValueError –

    if just_key_list is True and path isn't a directory.

Get where in the ODB a link points to.

Parameters:

  • link_path (str) –

    The ODB path for the link name

Returns:

  • str –

    the ODB path the link points to

odb_last_update_time(path)

Get when an ODB key was last written to.

Parameters:

  • path (str) –

    The ODB path

Returns:

Create or edit a link within the ODB.

To remove the link call odb_delete with the follow_links flag set to False.

Parameters:

  • link_path (str) –

    The ODB path for the link name

  • destination_path (str) –

    Where in the ODB the link points to

Raises:

  • ValueError –

    if link_path already exists, but isn't already a link

odb_rename(current_path, new_name)

Rename an existing ODB entry. This function does not allow you to change which directory the entry is in, just the name of it.

Parameters:

  • current_path (str) –

    Full ODB path of an existing ODB entry.

  • new_name (str) –

    The new name of the entry (do not include any / characters; the directory cannot be changed).

odb_set(path, contents, create_if_needed=True, remove_unspecified_keys=True, resize_arrays=True, lengthen_strings=True, explicit_new_midas_type=None, update_structure_only=False)

Set the value of an ODB key, or an entire directory. You may pass in normal python values, lists and dicts and they will be converted to appropriate midas ODB key types (e.g. int becomes midas.TID_INT, bool becomes midas.TID_BOOL).

Sensible defaults have been chosen for converting python types to the C types used internally in the midas ODB. However if you want more control over the ODB type, you may use the types defined in the ctypes library. For example, regular python integers become a midas.TID_INT, but you can use a ctypes.c_uint32 to get a midas.TID_DWORD.

If you pass a list/tuple of values, they will be converted to an ODB array. The data type of the first element in the list/tuple is used to determine the ODB type. If you pass [1, 2, 3.14] it will be stored in the ODB as [1, 2, 3]. Conversely, passing [3.14, 2, 1] will give [3.14, 2.0, 1.0].

If you are setting the content of a directory and care about the order in which the entries appear in that directory, contents should be a collections.OrderedDict rather than a basic python dict. See the note in odb_get for more about dictionary ordering.

Integer overflow

If you pass a value that is too large for the ODB type, the value will either overflow or be set to 0, depending on how the ctypes library handles it.

E.g. midas.TID_INT has a range of -231 to 231-1. Passing 233 will result in 0. Passing (231)+5 will result in a large negative number (overflow).

If you want to store 2**33 in the ODB, make sure to pass it as a ctypes.c_int64 or ctypes.c_uint64, not as a regular python int!

Passing None

If you pass a contents of None, the behaviour depends on whether we know the ODB type (either because the key already exists, or because you passed the explicit_new_midas_type parameter).

  • If we know the type and it's a string, None will be converted to "".
  • If we know the type and it's numeric, None will be converted to 0.
  • If we don't know the type, None will cause a ValueError to be raised.

Parameters:

  • path (str) –

    The ODB entry to set. You may specify a single array index if desired (e.g. "/Path/To/My/Array[1]").

  • contents (int/float/string/tuple/list/dict etc) –

    The new value to set

  • create_if_needed (bool, default: True ) –

    Automatically create the ODB entry (and parent directories) if needed.

  • remove_unspecified_keys (bool, default: True ) –

    If path points to a directory and contents is a dict, remove any ODB keys within path that aren't present in contents. This means that the ODB will exactly match the dict you passed in. You may want to set this to False if you want to only update a few entries within a directory, and want to do so with only a single call to odb_set().

  • resize_arrays (bool, default: True ) –

    Automatically resize any ODB arrays to match the length of lists present in contents. Arrays will be both lengthened and shortened as needed.

  • lengthen_strings (bool, default: True ) –

    Automatically increase the storage size of a TID_STRING entry in the ODB if it is not long enough to store the value specified. We will include enough space for a final null byte. If this is False, and your string is longer than the current capacity, it will be silently truncated.

  • explicit_new_midas_type (one of midas.TID_xxx, default: None ) –

    If you're setting the value of a single ODB entry, you can explicitly specify the type to use when creating the ODB entry (if needed).

  • update_structure_only (bool, default: False ) –

    If you want to add/remove entries in an ODB directory, but not change the value of any entries that already exist. Only makes sense if contents is a dict / collections.OrderedDict. Think of it like db_check_record from the C library.

Raises:

  • KeyError –

    if create_if_needed is False and the ODB entry does not already exist.

  • TypeError –

    if there is a problem converting contents to the C type we must pass to the midas library (e.g. the ODB entry is a TID_STRING but you passed in a float).

  • ValueError –

    if there is a non-type-related problem with contents (e.g. if resize_arrays is False and you provided a list that doesn't match the size of the existing ODB array).

  • MidasError –

    if there is some other midas issue, including if you try to write to a symlink where the target of the link no longer exists (use odb_link to fix the link first).

odb_stop_watching(path)

Stop watching any ODB entries that you previously registered through odb_watch.

Parameters:

  • path (str) –

    The ODB path you no longer want to watch.

Raises:

  • KeyError –

    If ODB path wasn't already being watched.

odb_watch(path, callback, pass_changed_value_only=False)

Register a function that will be called when a value in the ODB changes. You must later call communicate so that midas can inform your client about any ODB changes (if you're using the midas.frontend framework, then communicate will be called for you automatically).

You can only have one function per process watching an ODB path - if you register a second function for the exact same path, it will overwrite the first one. You can however have functions watching both an ODB path and its parent directory.

Parameters:

  • path (str) –

    The ODB path to watch (including all its children).

  • callback (function) –

    See below. You can register the same callback function to watch multiple ODB entries if you wish.

  • pass_changed_value_only (bool, default: False ) –

    Whether your callback function expects to be passed:

    • True: the full ODB entry that is being watched
    • False: only the value that has changed
Callback function details if pass_changed_value_only is FALSE
  • Arguments it should accept:
    • client (midas.client.MidasClient)
    • path (str): The ODB path being watched
    • odb_value (float/int/dict etc) - The new ODB state of the path being watched
  • Value it should return:
    • Anything or nothing, we don't do anything with it
Callback function details if pass_changed_value_only is TRUE
  • Arguments it should accept:
    • client (midas.client.MidasClient)
    • path (str): The ODB path that changed (possibly a child if you're watching an entire ODB directory)
    • index (int/None): The array index that changed (or None if the entry that changed is not an array)
    • odb_value (float/int/dict etc) - The new ODB value (the single element that changed if you're watching an array)
  • Value it should return:
    • Anything or nothing, we don't do anything with it

Example

See examples/basic_client.py for more.

import midas.client

def my_odb_callback(client, path, odb_value):
    print("New ODB content at %s is %s" % (path, odb_value))

my_client = midas.client.MidasClient("pytest")

# Note we pass in a reference to the function - we don't call it!
# (i.e. we pass `my_odb_callback` not `my_odb_callback()`).
my_client.odb_watch("/Runinfo", my_odb_callback)

while True:
    my_client.communicate(100)

open_event_buffer(buffer_name, buf_size=None, max_event_size=None)

Open a buffer that can be used to send or receive midas events.

Parameters:

  • buffer_name (str) –

    The name of the buffer to open. "SYSTEM" is the main midas buffer, but other clients can read/write to/from their own buffers if desired.

  • buf_size (int, default: None ) –

    The size of the buffer to open, in bytes. If not specified, defaults to 10 * max_event_size. Maximum value is 2^31 - 1 bytes (2.14 GB)

  • max_event_size (int, default: None ) –

    The size of the largest event we expect to receive/send. Defaults to the value of the ODB setting "/Experiment/MAX_EVENT_SIZE".

Returns:

  • int –

    the "buffer handle" that must be passed to other buffer/event- related functions.

pause_run(async_flag=False)

Pause a run.

Parameters:

  • async_flag (bool, default: False ) –

    If True, this function will return as soon as the transition has started; if False, it will wait until the transition has completed (or failed).

Raises:

receive_event(buffer_handle, async_flag=True, use_numpy=False)

Receive an event from a buffer. You must previously have called open_event_buffer to open the buffer and register_event_request to define which events you want to be told about.

See examples/event_receiver.py for a full example.

Parameters:

  • buffer_handle (int) –

    The return value from a previous call to open_event_buffer.

  • async_flag (bool, default: True ) –

    If True, we'll return None if there is not currently an event in the buffer. If False, we'll wait until an event is ready and then return.

  • use_numpy (bool, default: False ) –

    Whether to use numpy arrays or regular python tuples for bank data in the received Event.

Returns:

register_brpc_callback(callback)

Register a function that can be called from mhttpd, custom webpages, and other clients (using the "javascript RPC" system). You should register a single function, but can do multiple things in that function based on the parameters provided by the javascript call.

For brpc you return arbitrary binary data. See register_jrpc_callback (jrpc not brpc) for a version where you jsut want to return a string.

Parameters:

  • callback (function) –

    See below.

Callback function details
  • Arguments it should accept:
    • client (midas.client.MidasClient)
    • cmd (str): The command user wants to execute
    • args (str): Other arguments the user supplied
    • max_len (int): The maximum string length the user accepts in the return value
  • Value it should return:
    • A tuple of (int, ctypes.Array of ctypes.c_char) or (int, midas.event.Event). In the first version you can return arbitrary binary data. Best to create the relevant object using ctypes.create_string_buffer(). In the second version we will automatically convert the event into the relevant binary representation. The maximum data length (in bytes) that will be returned to the user is given by the max_len parameter.
Note
  • The javascript RPC call must take the format mjsonrpc_call("brpc", params, "arraybuffer")
  • midas.event.Events may be decoded in javascript with the bkToObj function defined in midas.js

register_deferred_transition_callback(transition, callback)

Register a function that can defer a run transition. No client will receive the actual transition until your callback function says that it is okay.

Note that there is no way to de-register a deferred transition callback, though you can change which function gets called by registering a new one.

Parameters:

  • transition (int) –

    One of midas.TR_START, midas.TR_STOP, midas.TR_PAUSE, midas.TR_RESUME, midas.TR_STARTABORT.

  • callback (function) –

    See below. This will be called frequently by midas during the deferral period, and tells midas whether the transition can proceed or not yet.

Callback function details
  • Arguments it should accept:
    • client (midas.client.MidasClient)
    • transition_type (int): The transition that has been requested (e.g. midas.TR_STOP)
  • Value it should return:
    • bool - True if the transition can proceed; False if the transition should wait.

register_disconnect_callback(callback)

Register a function that will be called immediately before we disconnect from midas.

To GUARANTEE that this function gets called, you should use a context manager style for the midas client (with syntax).

Example

# This style will work in most cases, but is not guaranteed to call
# the callback when the program exits (depends on your specific 
# python version/implementation and python code):
client = midas.client.MidasClient("pytest")
client.register_disconnect_callback(some_func)
exit(2)

# This style is guaranteed to work:
with midas.client.MidasClient("pytest") as client:
    client.register_disconnect_callback(some_func)
    exit(2)

Parameters:

  • callback (function) –

    See below.

Callback function details
  • Arguments it should accept:
    • client (midas.client.MidasClient)
  • Value it should return:
    • Anything or nothing.

register_event_request(buffer_handle, event_id=-1, trigger_mask=-1, sampling_type=midas.GET_ALL)

Register to be told about events matching certain criteria in a given buffer.

To actually get the events, you must later call receive_event.

See examples/event_receiver.py for a full example.

Parameters:

  • buffer_handle (int) –

    The return value from a previous call to open_event_buffer.

  • event_id (int, default: -1 ) –

    Limit to only events where the event ID matches this. -1 means no filtering.

  • trigger_mask (int, default: -1 ) –

    Limit to only events where the trigger mask matches this. -1 means no filtering.

  • sampling_type (int, default: GET_ALL ) –

    One of : midas.GET_ALL (get all events) midas.GET_NONBLOCKING (get as many as possible without blocking producer) midas.GET_RECENT (like non-blocking, but only get events < 1s old)

Returns:

  • int –

    the "request ID", which can later be used to cancel this event request.

register_jrpc_callback(callback, return_success_even_on_failure=False)

Register a function that can be called from mhttpd, custom webpages, and other clients (using the "javascript RPC" system). You should register a single function, but can do multiple things in that function based on the parameters provided by the javascript call.

For jrpc you return a string, and it will be interpreted as a string by the calling function. See register_brpc_callback (brpc not jrpc) for a version where you can return arbitrary binary data (potentially encoded as a midas event).

Parameters:

  • callback (function) –

    See below.

  • return_success_even_on_failure (bool, default: False ) –

    mjsonrpc (the web interface for calling JRPC functions) does not return any message if the status code isn't "SUCCESS". 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 "normal" 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 "SUCCESS", and the result string will be JSON-encoded text of the form `{"code": 604, "msg": "Some error message"}.

Callback function details
  • Arguments it should accept:
    • client (midas.client.MidasClient)
    • cmd (str): The command user wants to execute
    • args (str): Other arguments the user supplied
    • max_len (int): The maximum string length the user accepts in the return value
  • Value it should return:
    • 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. The maximum string length that will be returned to the user is given by the max_len parameter.

register_message_callback(callback, only_message_types=[midas.MT_INFO, midas.MT_ERROR])

Register a function to be called when a message is sent to the message log. Note you will receive messages from all "facilities" - it is not possible to only request messages from a single facility.

Parameters:

  • callback (function) –

    See below.

  • only_message_types (None, or list of midas.MT_xxx flags, default: [MT_INFO, MT_ERROR] ) –

    which message types to pass to the callback function (e.g. only midas.MT_ERROR, not midas.MT_INFO)

Callback function details
  • Arguments it should accept:
    • client (midas.client.MidasClient) msg (str): the actual message timestamp (datetime.datetime): the timestamp of the message msg_type (int): midas.MT_xxx flag, e.g. midas.MT_ERROR
  • Value it should return:
    • Anything or nothing, we don't do anything with it

register_transition_callback(transition, sequence, callback)

Register a function to be called when a transition happens.

Parameters:

  • transition (int) –

    One of midas.TR_START, midas.TR_STOP, midas.TR_PAUSE, midas.TR_RESUME, midas.TR_STARTABORT.

  • sequence (int) –

    The order in which this function will be called.

  • callback (function) –

    See below.

Callback function details
  • Arguments it should accept:
    • client (midas.client.MidasClient)
    • run_number (int): The current/new run number.
  • Value it should return - choose from:
    • None or 1 - Transition was successful
    • int that isn't 1 - Transition failed
    • 2-tuple of (int, str) - Transition failed and the reason why.

reset_alarm(alarm_name)

Reset an alarm that is currently triggered.

Note that if it as an evaluated alarm, midas may re-trigger the alarm again very quickly if the ODB condition is still met.

Parameters:

  • alarm_name (str) –

    The name of the alarm to reset. If empty (or None), will reset all alarms.

reset_all_alarms()

Reset all alarms that are currently triggered.

resume_run(async_flag=False)

Resume a paused run.

Parameters:

  • async_flag (bool, default: False ) –

    If True, this function will return as soon as the transition has started; if False, it will wait until the transition has completed (or failed).

Raises:

send_event(buffer_handle, event)

Send an event into a midas buffer.

Parameters:

!!! example:

import midas.client
import midas.event

client = midas.client.MidasClient("MyClientName")
buffer_handle = client.open_event_buffer("SYSTEM")

event = midas.event.Event()
event.header.event_id = 123
event.header.serial_number = 1
event.create_bank("MYBK", midas.TID_WORD, [1,2,3,4])

client.send_event(buffer_handle, event)

set_transition_sequence(transition, sequence)

Update the order in which our transition callback functions are called.

Parameters:

  • transition (int) –

    One of midas.TR_START, midas.TR_STOP, midas.TR_PAUSE, midas.TR_RESUME, midas.TR_STARTABORT.

  • sequence (int) –

    The order in which this function will be called.

start_all_required_programs()

Start all the programs listed in /Programs in the ODB that have the "Required" key set to True and that are not already running.

start_client(client_name, check_if_already_exists_before_starting=False)

Start a client/program that has been defined in /Programs in the ODB.

Must have the ODB key /Programs/<client_name>/Start command set, as this is the command we'll execute to try to start the client.

You can later use client_exists to check whether the client started successfully (e.g. wait a few seconds and then call client_exists).

Parameters:

  • client_name (str) –

    The name of a client/program defined in /Programs in the ODB.

  • check_if_already_exists_before_starting (bool, default: False ) –

    If True, we won't do anything if midas thinks the client/program is already running.

Returns:

  • int / None –

    the PID of the spawned process. Most users won't care about this. Returns None if check_if_already_exists_before_starting is True and the client is already running.

Raises:

  • KeyError –

    if the client/program isn't found in /Programs.

  • MidasError –

    if fork() failed or similar.

start_run(run_num=0, async_flag=False)

Start a new run.

Parameters:

  • run_num (int, default: 0 ) –

    The new run number. If 0, the run number will be automatically incremented.

  • async_flag (bool, default: False ) –

    If True, this function will return as soon as the transition has started; if False, it will wait until the transition has completed (or failed).

Raises:

stop_client(client_name, exact_match=True)

Stop a client/program that is running and connected to midas.

Note that the underlying C++ code does not distinguish between: * a client that doesn't exist versus a client that timed-out and had to be forcefully stopped with SIGKILL * a client that was shut down gracefully versus a client we couldn't connect to and had to be forcefully stopped with SIGKILL.

Therefore we don't even bother returning a value from this python wrapper. You can just assume that we tried our best to stop the client, and that it no longer has access to the ODB etc.

Parameters:

  • client_name (str) –

    The name of a midas client that is connected. You can pass the special value "all" to shutdown all clients.

  • exact_match (bool, default: True ) –

    If False, we'll look for all clients that start with this name. E.g. "myfe" would match "myfe_1", "myfetest" etc. If True, we require an exact match of the name.

Raises:

  • KeyError –

    if /System/Clients doesn't exist in the ODB (but you have very big problems if that's the case!)

stop_run(async_flag=False)

Stop a run.

Parameters:

  • async_flag (bool, default: False ) –

    If True, this function will return as soon as the transition has started; if False, it will wait until the transition has completed (or failed).

Raises:

trigger_internal_alarm(alarm_name, message, default_alarm_class='Alarm')

Trigger an "internal" alarm (i.e. an alarm raised directly by code, rather than due to an ODB condition failing or a program not running).

Example

client.trigger_internal_alarm("Example", "Oh no, something went wrong!")
client.get_triggered_alarms()
# Returns {'Example': 'Oh no, something went wrong!'}
client.reset_alarm("Example")
client.get_triggered_alarms()
# Returns {}

Parameters:

  • alarm_name (str) –

    The name of this alarm. An entry will be created in the ODB at /Alarms/Alarms/<alarm_name> if it doesn't already exist. Max 31 characters, and no "/" characters allowed.

  • message (str) –

    The message to be displayed to users. Max 80 characters.

  • default_alarm_class (str, default: 'Alarm' ) –

    The "class" of this alarm. (i.e. what to do when this alarm is triggered). See create_alarm_class for more about alarm classes. If an alarm already exists called alarm_name, the class of that alarm will not be changed.

Raises:

  • ValueError –

    If invalid alarm or class name provided