This section refers to the Midas built-in capabilities. The following sections describe in more details the essential aspect of each feature starting from the frontend to the Electronic Logbook.
Midas defines 3 states of Data acquistion: STOPPED, PAUSED, RUNNING
These 3 states require 4 transitions : TR_START, TR_PAUSE , TR_RESUME, TR_STOP
Any Midas client can request notification for run transition. This notification is done by registering to the system for a given transition ( cm_register_transition() ) by specifying the transition type and the sequencing number (1 to 1000). Multiple registration to a given transition can be requested. This last option permits for example to invoke two callback functions prior and after a given transition such as the start of the logger.
my_application.c // Callback INT before_logger(INT run_number, char *error) { printf("Initialize ... before the logger gets the Start Transition"); ... return CM_SUCCESS; } // Callback INT after_logger(INT run_number, char *error) { printf("Log initial info to file... after logger gets the Start Transition"); ... return CM_SUCCESS; } INT main() { ... cm_register_transition(TR_START, before_logger, 100); cm_register_transition(TR_START, after_logger, 300); ... }
By Default the following sequence numbers are used:
The sequence number appears into the ODBedit under /System/Clients/
[local:midas:S]Clients>ls -lr Key name Type #Val Size Last Opn Mode Value --------------------------------------------------------------------------- Clients DIR 1832 DIR <------------ Frontend 1 Name STRING 1 32 21h 0 R ebfe01 Host STRING 1 256 21h 0 R pierre2 Hardware type INT 1 4 21h 0 R 42 Server Port INT 1 4 21h 0 R 2582 Transition START INT 1 4 21h 0 R 500 Transition STOP INT 1 4 21h 0 R 500 Transition PAUSE INT 1 4 21h 0 R 500 Transition RESUME INT 1 4 21h 0 R 500 RPC DIR 17000 BOOL 1 4 21h 0 R y 3872 DIR <------------ Frontend 2 Name STRING 1 32 21h 0 R ebfe02 Host STRING 1 256 21h 0 R pierre2 Hardware type INT 1 4 21h 0 R 42 Server Port INT 1 4 21h 0 R 2585 Transition START INT 1 4 21h 0 R 500 Transition STOP INT 1 4 21h 0 R 500 Transition PAUSE INT 1 4 21h 0 R 500 Transition RESUME INT 1 4 21h 0 R 500 RPC DIR 17000 BOOL 1 4 21h 0 R y 2220 DIR <------------ ODBedit doesn't need transition Name STRING 1 32 42s 0 R ODBEdit Host STRING 1 256 42s 0 R pierre2 Hardware type INT 1 4 42s 0 R 42 Server Port INT 1 4 42s 0 R 3429 568 DIR <------------ Event Builder Name STRING 1 32 26s 0 R Ebuilder Host STRING 1 256 26s 0 R pierre2 Hardware type INT 1 4 26s 0 R 42 Server Port INT 1 4 26s 0 R 3432 Transition START INT 1 4 26s 0 R 300 Transition STOP INT 1 4 26s 0 R 700 2848 DIR <------------ Logger Name STRING 1 32 5s 0 R Logger Host STRING 1 256 5s 0 R pierre2 Hardware type INT 1 4 5s 0 R 42 Server Port INT 1 4 5s 0 R 3436 Transition START INT 1 4 5s 0 R 200 Transition STOP INT 1 4 5s 0 R 800 Transition PAUSE INT 1 4 5s 0 R 500 Transition RESUME INT 1 4 5s 0 R 500 RPC DIR 14000 BOOL 1 4 5s 0 R y
The /System/Clients/... tree reflects the system at a given time. If a permanent change of a client sequence number is required, the system call cm_set_transition_sequence() can be used.
The frontend program (image) consists of a system framework contained in mfe.c (hidden from the user) and a user part contained in frontend.c . The hardware access is only apparent in the user code.
Several libraries and drivers exist for various bus systems like CAMAC, VME or RS232. They are located in the drivers directory of the MIDAS distribution. Some libraries consist only of a header file, others of a C file plus a header file. The file names usually refer to the manufacturer abbreviation followed by the model number of the device. The libraries are continuously expanding to widen Midas support.
ESONE standard routines for CAMAC are supplied and permit to re-use the frontend code among different platforms as well as different CAMAC hardware interface without the need of modification of the code.
The user frontend code consists of several sections described in order below. Example of frontend code can be found under the ../examples/experiment directory:
// The frontend name (client name) as seen by other MIDAS clients char *frontend_name = "Sample Frontend"; // The frontend file name, don't change it char *frontend_file_name = __FILE__; // frontend_loop is called periodically if this variable is TRUE BOOL frontend_call_loop = FALSE; //a frontend status page is displayed with this frequency in ms INT display_period = 3000; //maximum event size produced by this frontend INT max_event_size = 10000; //buffer size to hold events INT event_buffer_size = 10*10000; // Global user section // number of channels #define N_ADC 8 #define N_TDC 8 #define N_SCLR 8 CAMAC crate and slots #define CRATE 0 #define SLOT_C212 23 #define SLOT_ADC 1 #define SLOT_TDC 2 #define SLOT_SCLR 3
INT frontend_init(); INT frontend_exit(); INT begin_of_run(INT run_number, char *error); INT end_of_run(INT run_number, char *error); INT pause_run(INT run_number, char *error); INT resume_run(INT run_number, char *error); INT frontend_loop(); INT read_trigger_event(char *pevent, INT off); INT read_scaler_event(char *pevent, INT off);
#undef USE_INT EQUIPMENT equipment[] = { { "Trigger", // equipment name {1, 0, // event ID, trigger mask "SYSTEM", // event buffer #ifdef USE_INT EQ_INTERRUPT, // equipment type #else EQ_POLLED, // equipment type #endif LAM_SOURCE(CRATE, LAM_STATION(SLOT_C212)), // event source crate 0 "MIDAS", // format TRUE, // enabled RO_RUNNING | // read only when running RO_ODB, // and update ODB 500, // poll for 500ms 0, // stop run after this event limit 0, // number of sub events 0, // don't log history "", "", ""} , read_trigger_event, // readout routine NULL, NULL, trigger_bank_list, // bank list } , ...
cam_init(); // Init CAMAC access cam_crate_clear(CRATE); // Clear Crate cam_crate_zinit(CRATE); // Z crate cam_inhibit_set(CRATE); // Set I crate return SUCCESS;
// clear units camc(CRATE, SLOT_C212, 0, 9); camc(CRATE, SLOT_2249A, 0, 9); camc(CRATE, SLOT_SC2, 0, 9); camc(CRATE, SLOT_SC3, 0, 9); camc(CRATE, SLOT_C212, 0, 26); // Enable LAM generation cam_inhibit_clear(CRATE); // Remove I cam_lam_enable(CRATE, SLOT_C212); // Declare Station to CC as LAM source // set and clear OR1320 pattern bits camo(CRATE, SLOT_OR1320, 0, 18, 0x0330); camo(CRATE, SLOT_OR1320, 0, 21, 0x0663); // Open run gate, reset latch return SUCCESS;
// Trigger event routines --------------------------------------- INT poll_event(INT source, INT count, BOOL test) // Polling routine for events. Returns TRUE if event // is available. If test equals TRUE, don't return. The test // flag is used to time the polling. { int i; DWORD lam; for (i=0 ; i<count ; i++) { cam_lam_read(LAM_SOURCE_CRATE(source), &lam); if (lam & LAM_SOURCE_STATION(source)) // Any of the equipment LAM // *** or *** if (lam) // Any LAM (independent of the equipment) if (!test) return lam; return 0;
LAM_SOURCE(JW_C, LAM_STATION(GE_N) | LAM_STATION(JW_N)),
INT read_trigger_event(char *pevent, INT off) { DWORD lam; lam = *((DWORD *)pevent); // check LAM versus MCS station // The clear is performed at the end of the readout function if (lam & LAM_STATION(JW_N)) { ... ... }
// Event readout ------------------------------------------------- INT read_trigger_event(char *pevent, INT off) { WORD *pdata, a; // init bank structure bk_init(pevent); // create ADC bank bk_create(pevent, "ADC0", TID_WORD, &pdata); ... }
// set and clear OR1320 pattern bits or close run gate. camo(CRATE, SLOT_OR1320, 0, 18, 0x0CC3); camo(CRATE, SLOT_OR1320, 0, 21, 0x0990); camc(CRATE, SLOT_C212, 0, 26); // Enable LAM generation cam_lam_disable(CRATE, SLOT_C212); // disable LAM in crate controller cam_inhibit_set(CRATE); // set crate inhibit
#undef USE_INT EQUIPMENT equipment[] = { { "Trigger", // equipment name {1, 0, // event ID, trigger mask "SYSTEM", // event buffer #ifdef USE_INT EQ_INTERRUPT, // equipment type #else EQ_POLLED, // equipment type #endif LAM_SOURCE(0,0xFFFFFF),// event source crate 0, all stations "MIDAS", // format TRUE, // enabled RO_RUNNING | // read only when running RO_ODB, // and update ODB 500, // poll for 500ms 0, // stop run after this event limit 0, // number of sub events 0, // don't log history "", "", ""} , read_trigger_event, // readout routine NULL, NULL, trigger_bank_list, // bank list } , ...
INT interrupt_configure(INT cmd, INT source [], PTYPE adr) { switch(cmd) { case CMD_INTERRUPT_ENABLE: cam_interrupt_enable(); break; case CMD_INTERRUPT_DISABLE: cam_interrupt_disable(); break; case CMD_INTERRUPT_ATTACH: cam_interrupt_attach((void (*)())adr); break; case CMD_INTERRUPT_DETACH: cam_interrupt_detach(); break; return CM_SUCCESS;
RO_RUNNING | Read on state "running" |
RO_STOPPED | Read on state "stopped" |
RO_PAUSED | Read on state "paused" |
RO_BOR | Read after begin-of-run |
RO_EOR | Read before end-of-run |
RO_PAUSE | Read when run gets paused |
RO_RESUME | Read when run gets resumed |
RO_TRANSITIONS | Read on all transitions |
RO_ALWAYS | Read independently of the states and force a read for all transitions. |
RO_ODB | Equipment event mirrored into ODB under variables |
These flags can be combined with the logical OR operator. Trigger events in the above example are read out only when running while scaler events is read out when running and additionally on all transitions. A special flag RO_ODB tells the system to copy the event to the /Equipment/<equipment name>/Variables ODB tree once every ten seconds for diagnostic. Later on, the event content can then be displayed with ODBEdit.
... in the equipment declaration ... LAM_SOURCE(JW_C, LAM_STATION(GE_N) | LAM_STATION(JW_N)), // event source ... "", "", "", event_dispatcher, // readout routine ... INT event_dispatcher(char *pevent) { DWORD lam, dword; INT size=0; EQUIPMENT *eq; // the *pevent contains the LAM pattern returned from poll_event // The value can be used to dispatch to the proper LAM function // !!!! ONLY one of the LAM is processed in the loop !!!! lam = *((DWORD *)pevent); // check LAM versus MCS station if (lam & LAM_STATION(JW_N)) { ... // read MCS event size = read_mcs_event(pevent); ... else if (lam & LAM_STATION(GE_N)) { ... // read GE event size = read_ge_event(pevent); ... return size;
Following statements would define a fixed event with two ADC and TDC values:
typedef struct { int adc0; int adc1; int tdc0; int tdc1; TRIGGER_EVENT; char *trigger_event_str[] = { "adc0 = INT : 0", "adc1 = INT : 0", "tdc0 = INT : 0", "tdc1 = INT : 0", ASUM_BANK;
The trigger_event_str has to be defined before the equipment list and a reference to it has to be placed in the equipment list like:
{ ... read_trigger_event, // readout routine poll_trigger_event, // polling routine trigger_event_str, // init string ,
The readout routine could then look like this, where the <...> statements have to be filled with the appropriate code accessing the hardware:
INT read_trigger_event(char *pevent) { TRIGGER_EVENT *ptrg; ptrg = (TRIGGER_EVENT *) pevent; ptrg->adc0 = <...>; ptrg->adc1 = <...>; ptrg->tdc0 = <...>; ptrg->tdc1 = <...>; return sizeof(TRIGGER_EVENT);
MIDAS banks are created in the frontend readout code with calls to the MIDAS library. Following routines exist:
INT read_trigger_event(char *pevent) { INT *pdata; bk_init(pevent); bk_create(pevent, "ADC0", TID_INT, &pdata); *pdata++ = <ADC0> *pdata++ = <ADC1> bk_close(pevent, pdata); bk_create(pevent, "TDC0", TID_INT, &pdata); *pdata++ = <TDC0> *pdata++ = <TDC1> bk_close(pevent, pdata); return bk_size(pevent);
Upon normal completion, the readout routine returns the event size in bytes. If the event is not valid, the routine can return zero. In this case no event is sent to the back-end. This can be used to implement a software event filter (sometimes called "third level trigger").
INT read_trigger_event(char *pevent) { WORD *pdata, a; // init bank structure bk_init(pevent); // create ADC bank bk_create(pevent, "ADC0", TID_WORD, &pdata); // read ADC bank for (a=0 ; a<8 ; a++) cami(1, 1, a, 0, pdata++); bk_close(pevent, pdata); // create TDC bank bk_create(pevent, "TDC0", TID_WORD, &pdata); // read TDC bank for (a=0 ; a<8 ; a++) cami(1, 2, a, 0, pdata++); bk_close(pevent, pdata); return bk_size(pevent);
The scheme of bank creation is exactly the same as for MIDAS events, only the routines are named differently. The YBOS format is double word oriented i.e. all incrementation are done in 4 bytes steps. Following routines exist:
The following code creates an ADC0 bank in YBOS format:
INT read_trigger_event(char *pevent) { DWORD i; DWORD *pbkdat; ybk_init((DWORD *) pevent); // collect user hardware data ybk_create((DWORD *)pevent, "ADC0", I4_BKTYPE, (DWORD *)(&pbkdat)); for (i=0 ; i<8 ; i++) *pbkdat++ = i & 0xFFF; ybk_close((DWORD *)pevent, pbkdat); ybk_create((DWORD *)pevent, "TDC0", I2_BKTYPE, (DWORD *)(&pbkdat)); for (i=0 ; i<8 ; i++) *((WORD *)pbkdat)++ = (WORD)(0x10+i) & 0xFFF; ybk_close((DWORD *) pevent, pbkdat); ybk_create((DWORD *)pevent, "SIMU", I2_BKTYPE, (DWORD *)(&pbkdat)); for (i=0 ; i<9 ; i++) *((WORD *)pbkdat)++ = (WORD) (0x20+i) & 0xFFF; ybk_close((DWORD *) pevent, I2_BKTYPE, pbkdat); return (ybk_size((DWORD *)pevent));
In these examples, any application having access to the state of the hardware can register to be a "transition Deferred" client. It will then catch any transition request and postpone the trigger of such transition until condition is satisfied. The Deferred_Transition requires 3 steps for setup:
//-- Frontend Init INT frontend_init() { INT status, index, size; BOOL found=FALSE; // register for deferred transition cm_register_deferred_transition(TR_STOP, wait_end_cycle); cm_register_deferred_transition(TR_PAUSE, wait_end_cycle); ...
//-- Deferred transition callback BOOL wait_end_cycle(int transition, BOOL first) { if (first) { transition_PS_requested = TRUE; return FALSE; if (end_of_mcs_cycle) { transition_PS_requested = FALSE; end_of_mcs_cycle = FALSE; return TRUE; else return FALSE; ...
... In this case at the end of the readout function... ... INT read_mcs_event(char *pevent, INT offset) { ... if (transition_PS_requested) { // Prevent to get new MCS by skipping re_arm_cycle and GE by GE_DISABLE LAM cam_lam_disable(JW_C,JW_N); cam_lam_disable(GE_C,GE_N); cam_lam_clear(JW_C,JW_N); cam_lam_clear(GE_C,GE_N); camc(GE_C,GE_N,0,GE_DISABLE); end_of_mcs_cycle = TRUE; re_arm_cycle(); return bk_size(pevent);
To demonstrate the use of it, let's see the following example:
{ "GE", // equipment name 2, 0x0002, // event ID, trigger mask "SYSTEM", // event buffer #ifdef USE_INT EQ_INTERRUPT, // equipment type #else EQ_POLLED, // equipment type #endif LAM_SOURCE(GE_C, LAM_STATION(GE_N)), // event source "MIDAS", // format TRUE, // enabled RO_RUNNING, // read only when running 200, // poll for 200ms 0, // stop run after this event limit 1000, // -----> number of sub event <----- enable Super event 0, // don't log history "", "", "", read_ge_event, // readout routine , ...
//-- Event readout // Global and fixed -- Expect NWORDS 16bits data readout per sub-event #define NWORDS 3 INT read_ge_event(char *pevent, INT offset) { static WORD *pdata; // Super event structure if (offset == 0) { // FIRST event of the Super event bk_init(pevent); bk_create(pevent, "GERM", TID_WORD, &pdata); else if (offset == -1) { // close the Super event if offset is -1 bk_close(pevent, pdata); // End of Super Event return bk_size(pevent); // read GE sub event (ADC) cam16i(GE_C, GE_N, 0, GE_READ, pdata++); cam16i(GE_C, GE_N, 1, GE_READ, pdata++); cam16i(GE_C, GE_N, 2, GE_READ, pdata++); // clear hardware re_arm_ge(); if (offset == 0) { // Compute the proper event length on the FIRST event in the Super Event // NWORDS correspond to the !! NWORDS WORD above !! // sizeof(BANK_HEADER) + sizeof(BANK) will make the 16 bytes header // sizeof(WORD) is defined by the TID_WORD in bk_create() return NWORDS * sizeof(WORD) + sizeof(BANK_HEADER) + sizeof(BANK); else // Return the data section size only // sizeof(WORD) is defined by the TID_WORD in bk_create() return NWORDS * sizeof(WORD);
The separation into class and device drivers has the advantage that it is very easy to add new devices, because only the simple device driver needs to be written. All higher functionality is inherited from the class driver. The device driver can implement richer functionality, depending on the hardware. For some high voltage devices there is a current read-back for example. This is usually reflected by additional variables in the ODB, i.e. a Current array. Frontend equipment uses exactly one class driver, but a class driver can use more than one device driver. This makes it possible to control several high voltage devices for example with one frontend in one equipment. The number of channels for each device driver is defined in the slow control frontend. Several equipment with different class drivers can be defined in a single frontend.
Key name Type #Val Size Last Opn Mode Value --------------------------------------------------------------------------- Epics DIR Settings DIR Channels DIR Epics INT 1 4 25h 0 RWD 3 Devices DIR Epics DIR Channel name STRING 10 32 25h 0 RWD [0] GPS:VAR1 [1] GPS:VAR2 [2] GPS:VAR3 Names STRING 10 32 17h 1 RWD [0] Current [1] Voltage [2] Watchdog Update Threshold MeasureFLOAT 10 4 17h 0 RWD [0] 2 [1] 2 [2] 2 Common DIR Event ID WORD 1 2 17h 0 RWD 3 Trigger mask WORD 1 2 17h 0 RWD 0 Buffer STRING 1 32 17h 0 RWD SYSTEM Type INT 1 4 17h 0 RWD 4 Source INT 1 4 17h 0 RWD 0 Format STRING 1 8 17h 0 RWD FIXED Enabled BOOL 1 4 17h 0 RWD y Read on INT 1 4 17h 0 RWD 121 Period INT 1 4 17h 0 RWD 60000 Event limit DOUBLE 1 8 17h 0 RWD 0 Num subevents DWORD 1 4 17h 0 RWD 0 Log history INT 1 4 17h 0 RWD 1 Frontend host STRING 1 32 17h 0 RWD hostname Frontend name STRING 1 32 17h 0 RWD Epics Frontend file name STRING 1 256 17h 0 RWD feepic.c Variables DIR Demand FLOAT 10 4 0s 1 RWD [0] 1.56 [1] 120 [2] 87 Measured FLOAT 10 4 2s 0 RWD [0] 1.56 [1] 120 [2] 87 Statistics DIR Events sent DOUBLE 1 8 17h 0 RWDE 26 Events per sec. DOUBLE 1 8 17h 0 RWDE 0 kBytes per sec. DOUBLE 1 8 17h 0 RWDE 0
midas.log file contains system and user messages generated by any application connected to the given experiment.
The MIDAS Macros definition provides a list of possible type of messages.
Fri Mar 24 10:48:40 2000 [CHAOS] Run 8362 started
Fri Mar 24 10:48:40 2000 [Logger] Run #8362 started
Fri Mar 24 10:55:04 2000 [Lazy_Tape] cni-043[10] (cp:383.6s) /dev/nst0/run08360.ybs 849.896MB file NEW
Fri Mar 24 11:24:03 2000 [MStatus] Program MStatus on host umelba started
Fri Mar 24 11:24:03 2000 [MStatus] Program MStatus on host umelba stopped
Fri Mar 24 11:27:02 2000 [Logger] stopping run after having received 1200000 events
Fri Mar 24 11:27:03 2000 [CHAOS] Run 8362 stopped
Fri Mar 24 11:27:03 2000 [SUSIYBOS] saving info in run log
Fri Mar 24 11:27:03 2000 [Logger] Run #8362 stopped
Fri Mar 24 11:27:13 2000 [Logger] starting new run
Fri Mar 24 11:27:14 2000 [CHAOS] Run 8363 started
Fri Mar 24 11:27:14 2000 [CHAOS] odb_access_file -I- /Equipment/kos_trigger/Dump not found
Fri Mar 24 11:27:14 2000 [Logger] Run #8363 started
Fri Mar 24 11:33:47 2000 [Lazy_Tape] cni-043[11] (cp:391.8s) /dev/nst0/run08361.ybs 850.209MB file NEW
Fri Mar 24 11:42:35 2000 [CHAOS] Run 8363 stopped
Fri Mar 24 11:42:40 2000 [SUSIYBOS] saving info in run log
Fri Mar 24 11:42:41 2000 [ODBEdit] Run #8363 stopped
Fri Mar 24 12:19:57 2000 [MChart] client [umelba.Triumf.CA]MChart failed watchdog test after 10 sec
Fri Mar 24 12:19:57 2000 [MChart] Program MChart on host koslx0 stopped
Quick Start - Top - Utilities