Advanced Usage¶
Construct or modify the Clip object mannualy¶
You may want to make a Clip object without using eta.clip_file
or other higher-level APIs built upon this. For example, you may want to build a Clip from a memory buffer recived form a remote computer over the Internet. You may also want to read a header-less file of any supported format, by skipping the default header-reading behaviors.
Read a header-less file¶
Here is a sample to create a empty Clip object in the script panel, which could be fed into eta.clips
as modify_clip
and so that eta.clips
will not read the file header at all.
import etabackend.clip as clip
first_clip = clip.Clip()
first_clip.TTRes_pspr = int(TTRes) # you will need to mannualy configure TTRes (integer in picoseconds). It was usually read from the header
first_clip.DTRes_pspr = int(DTRes) # same for DTRes
first_clip.SYNCRate_pspr = int(SYNCRate) # same for SYNCRate
first_clip.BytesofRecords = 4 # for PicoHarp, HydraHarp, and TimeHarp
first_clip.RecordType = 0x00010203 # 0x00010203 for PicoHarp T2 mode, 0x00010303 for T3 mode.
# By using ret_clip = eta.clip_file(filename) to read a complete file (with header) generated by the program from the instrument vendor, you can find the correct value of ret_clip.RecordType and ret_clip.BytesofRecords.
my_clip = eta.clips(filename,... , modify_clip=first_clip)
eta.run(... my_clip ...)
Build a Clip without any file¶
Here is an example to avoid eta.clip_file
or other higher-level APIs that reads a file on disk, by creating the Clip directly from the memory buffer.
import etabackend.clip as clip
clip_from_remote_pc = clip.Clip()
clip_from_remote_pc.TTRes_pspr = ...
clip_from_remote_pc.DTRes_pspr = ...
clip_from_remote_pc.SYNCRate_pspr = ...
clip_from_remote_pc.BytesofRecords = ...
clip_from_remote_pc.RecordType = ...
your_buffer, read_length = your_network_lib.recv(1000)
clip_from_remote_pc.buffer = your_buffer
clip_from_remote_pc.batch_actualread_length = read_length
clip_from_remote_pc.next_RecID_in_batch = 0
Internally, ETA performs zero-copy analysis with the given buffer. This example could help if you have throughput issues with the hard-drive and want to direclty perfrom the analysis with the memory buffer obtained by the instrument driver program over USB, PCIE, or the Internet.
Noet that this exmaple is for only one device and it’s over-simplified. It’s suggested to use the implementation of eta.clips
as a reference to properly warp your devices as generators, so that ETA could properly handel the synchronization between multiple devices.
Applying global time shift¶
You may also want to modify the Clip returned by eta.clip_file
or other higher-level APIs to apply a global time shift to all channels within a certain timetag file. This is handy if you use multiple timetag as sources (RFILES) in one experiment. Refer to clock.infer_start_from_stop
for more ideas.
ret_clip = eta.clip_file(filename)
ret_clip.GlobalTimeShift = -1,000,000 # picoseconds
new_clip = eta.clips(filename, seek_event=0, modify_clip=ret_clip, reuse_clips=True)
# use seek_event=0 to resume to the first event after the header
# make sure reuse_clips=True, so that your modification will be preserved when the generator is sliding windows
eta.run(... new_clip ...)
Run ETA as a Python Library¶
There are two ways to run ETA as a Python Library, one with the BACKEND
Class and the other with ETA Class.
Use the BACKEND
Class if you want full ETA Backend features, without Websocket and GUI. This is ideal for using ETA in monitor-less (headless) enviroments like supercomputers, or embedded devices.
Use the ETA
Class, if you would like to ignore all Script Panels in the exsiting recipe and simply obtian a eta
object for later use, as described in Customizing Script Panel. This is ideal for performing automated testing, using ETA with a notebook environment like Jupyter, or integrating ETA into your own Remote Procedure Calling system.
backend.process_eta(recipe, id, group=”main”)¶
Run a Script Panel, as if it is being run from the GUI. You will usually need to hook a send
function to obtian results, as the Script Panel code might use logger or other methods to stream the results to the caller.
recipe
- The recipe object parsed from the
.eta
JSON file.
id
- The identifier of the Script Panel to be started.
group
- The gruop name of this Script Panel
import json
from etabackend.backend import BACKEND
backend = BACKEND(run_forever=False)
def send(self, text, endpoint="log"):
print(text)
backend.send = send
with open("./Realtime.eta", 'r') as filehandle:
backend.process_eta(json.load(filehandle), id="dpp_template_code", group="main")
Talking to ETA backend via WebSocket¶
ETA backend implements a Remote Procedure Call mechanism using JSON format, with which you can upload an existing recipe, modifying parameters like filename
, run the analysis, and even get the real-time streaming of the result.
Before invoking a remote procedure, connect your program (client) to ETA backend (server) using the Websocket protocal.
(Examples in LabVIEW and Javascript are provided. [TODO:link to .vi] )
Sending a JSON string in a format of {"method": "<name of method>", "args": [<arg1>,<arg2>,...] }
to the Websocket will invoke the corresponding procedure immediately. When invoked procedure is running, new requests will be queued until the current one finishes.
The procedure might send JSON strings as responses in a format of ["<type>","<content>"]
. Please note that the client might get multiple responses (even in different types) after invoking a single procedure.
Remote procedures provided by ETA Backend¶
There are three special functions provided for remote controlling ETA Backend.
All these methods bundle a set of internal functions that first update the recipe on ETA Backend to the uploaded one, and then perform the requested actions. Optionaly they will also send the updated table for GUI as responses. There might be some extra response, for errors in the recipe or user-defined frontend logger in the Script Panel code.
It is not recommended to remotely call the undocumented procedures provided by the backend object, because they are not designed for remote calling and the returned value will not be streamed back to caller’s side.
VI Checking
JSON:
{ 'method': "compile_eta", 'args': [eta_file_content] }
Arg: eta_file_content is a string of the content of the .eta recipe.
Browse file and set it as the parameter.
JSON:
{ 'method': "recipe_set_filename", 'args': [eta_file_content, id, name] }
Arg: eta_file_content is a string of the content of the .eta recipe. For specifying the parameter that you want to modify, the id and name should also be provided.
Run a Display Panel
JSON:
{ 'method': "process_eta", 'args': [eta_file_content, id, group] }
Arg: eta_file_content is a string of the content of the .eta recipe. For specifying the Display Panel that you want to run, the id and group should also be provided.
Extra Responses: Other responses are sent in code of Display Panel in the recipe, using eta.send().
Type of responses from ETA Backend¶
In order to interact with the Backend properly, your client needs to handel these types of responses, and display them to the user.
Errors
Type:
err
JSON:
["err","<text>"]
Args:
<text>
is a string of the error message.Generic Information
Type:
log
JSON:
["log","<text>"]
Args:
<text>
is a string of the message.Update Main Table
Type:
table
JSON:
["table","<json>"]
Args:
<json>
is a JSON string of the main table.Switch state to running
Type:
running
JSON:
["running"]
Switch state to stopped
Type:
stopped
JSON:
["stopped"]
Switch state to discarded
Type:
discard
JSON:
["discard"]
URL of dashboard
Type:
dash
JSON:
["dash",<url>]
Args:
<url>
is a string of URL to the dashboard.User-defined message (eg. streamming histogram or GUI updates)
Type: defined using eta.send(message,type)
JSON:
["<type>","<message>"]
Args:
<message>
is a string of a user-defined message.