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:
-
have_numpy(bool) –Whether numpy is available.
-
logger(Logger) –Python logger for this module. See
midas.MidasLogHandlerfor more.
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.handsrc/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 themidas.MidasLibclass 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_eventis True, whether to use numpy arrays or regular python tuples for bank data in the receivedEvent. -
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_eventis False. -
Event–the data populated by the other client if
as_eventis 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:
-
int–handle that you should pass to
disconnect_from_other_client,jrpc_client_callandbrpc_client_call.
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_classfor 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:
-
buffer_handle(int) –The return value from a previous call to
open_event_buffer. -
request_id(int) –The return value from a previous call to
register_event_request.
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
withstatement:with midas.client.MidasClient('name') as c: .......
disconnect_from_other_client(connection, shutdown_other_client=False)
exec(command)
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:
-
list of str–names that can be passed to
start_client,stop_client,connect_to_other_clientetc.
get_experiment_dir()
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_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_eventsto find valid event names. -
tag_name(str) –Name of the midas history "tag" to query. Use
hist_get_tagsto 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.datetimeobjects, 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
timestampsandvaluesarrays 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:
For all other parameters, see hist_get_data
Returns:
-
list of dicts–see
hist_get_data
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)
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:
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:
odb_exists(path)
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.MidasErrorexception will be raised (with codemidas.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
pathpoints to a directory, whether to recruse down and get all entries beneath that directory, or just the entries immediately below the specified path.recurse_dirbeing False acts more like anlsof 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
pathpoints to a directory and/orinclude_key_metadatais True. Dicts are keyed by the ODB key name. Ifrecurse_diris False and one of the entries inpathis also a directory, it's value will be an empty dict. Ifinclude_key_metadatais 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
pathpoints to a single ODB key andinclude_key_metadatais False. -
list of strings–If
just_key_listis True, andpathpoints to a directory.
Raises:
-
KeyError–if
pathdoesn't exist in the ODB -
MidasError–for other midas-related issues
-
ValueError–if
just_key_listis True andpathisn't a directory.
odb_get_link_destination(link_path)
odb_last_update_time(path)
odb_link(link_path, destination_path)
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:
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
pathpoints to a directory andcontentsis a dict, remove any ODB keys withinpaththat aren't present incontents. 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 toodb_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_neededis False and the ODB entry does not already exist. -
TypeError–if there is a problem converting
contentsto 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. ifresize_arraysis 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_linkto fix the link first).
odb_stop_watching(path)
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:
-
TransitionFailedError–if async_flag is False and the transition fails.
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:
-
Event–
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 usingctypes.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 themax_lenparameter.
- A tuple of (int,
Note
- The javascript RPC call must take the format
mjsonrpc_call("brpc", params, "arraybuffer") midas.event.Eventsmay be decoded in javascript with thebkToObjfunction 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_lenparameter.
- 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
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:
-
TransitionFailedError–if async_flag is False and the transition fails.
send_event(buffer_handle, event)
Send an event into a midas buffer.
Parameters:
-
buffer_handle(int) –The return value from a previous call to
open_event_buffer. -
event(Event) –The event to write.
!!! 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)
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_startingis 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:
-
TransitionFailedError–if async_flag is False and the transition fails.
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/Clientsdoesn'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:
-
TransitionFailedError–if async_flag is False and the transition fails.
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_classfor more about alarm classes. If an alarm already exists calledalarm_name, the class of that alarm will not be changed.
Raises:
-
ValueError–If invalid alarm or class name provided