Frontend user code: Difference between revisions
No edit summary |
No edit summary |
||
Line 381: | Line 381: | ||
===Event Types=== | ===Event Types=== | ||
Each equipment is associated with a unique event. Depending on the [[Equipment Flags|Equipment Flag]] in the [[Equipment List Parameters|Equipment Declaration]], different | Each equipment is associated with a unique event. Depending on the [[Equipment Flags|Equipment Flag]] in the [[Equipment List Parameters|Equipment Declaration]], different | ||
types of events will be produced. Common event types are "polled events" where the Equipment Flag is [[Equipment Flags#EQ_POLLED]], "interrupts events" where the Flag is [[Equipment Flags#EQ_INTERRUPT]], and "periodic events" where the Flag is [[Equipment Flags#EQ_PERIODIC]]. | types of events will be produced. Common event types are "polled events" where the Equipment Flag is [[Equipment Flags#EQ_POLLED|EQ_POLLED]], "interrupts events" where the Flag is [[Equipment Flags#EQ_INTERRUPT|EQ_INTERRUPT]], and "periodic events" where the Flag is [[Equipment Flags#EQ_PERIODIC|EQ_PERIODIC]]. | ||
All events require an [[#Event Readout routine|event readout routine]]. The [[Equipment List Parameters#Readout Routine|readout routine]] is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type. | All events require an [[#Event Readout routine|event readout routine]]. The [[Equipment List Parameters#Readout Routine|readout routine]] is specified in the [[Equipment List Parameters|Equipment Declaration]] for each event type. | ||
Line 389: | Line 389: | ||
===Polled and Interrupt Events=== | ===Polled and Interrupt Events=== | ||
Polled and interrupt events (see [[#Event Types|Event Types]] require several extra functions to handle the hardware that periodic events do not require. These are described below. | Polled and interrupt events (see [[#Event Types|Event Types]]) require several extra functions to handle the hardware that periodic events do not require. These are described below. | ||
Line 400: | Line 400: | ||
If the [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment. | If the [[Equipment List Parameters#Equipment Type|Equipment Type]] is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment. | ||
In this case, the Equipment declaration would have this form: | In this case, the [[Equipment List Parameters|Equipment declaration]] would have this form: | ||
EQUIPMENT equipment[] = { | EQUIPMENT equipment[] = { | ||
Line 480: | Line 480: | ||
===Event Readout routine=== | ===Event Readout routine=== | ||
In the case of [[#Polled and Interrupt Events|POLLED or INTERRUPT events]], the event readout routine is called an [[#Polled or Interrupt | In the case of [[#Polled and Interrupt Events|POLLED or INTERRUPT events]], the event readout routine is called an [[#Polled or Interrupt readout routine|Interrupt readout routine]]. | ||
An event readout routine (called when an event occurs) is usually of the form | An event readout routine (called when an event occurs) is usually of the form | ||
Line 504: | Line 504: | ||
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism. | The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism. | ||
The Equipment declaration is of the form: | The [[Equipment List Parameters|Equipment declaration]] is of the form: | ||
EQUIPMENT equipment[] = { | EQUIPMENT equipment[] = { |
Revision as of 17:56, 6 November 2013
Links
Links
Introduction
This section describes the features of the user-written part of a Frontend, referred to here as frontend.c. To make a custom frontend, users will usually modify one of the templates provided in the MIDAS package for their particular hardware and other requirements. A partial list of the templates provided is shown below:
Hardware | Filename | Directory (MIDAS package) | Purpose | Language |
VME | fevmemodules.c | ../midas/examples/Triumf/c/ | Access to VME modules | C |
VME | fevme.cxx | ../midas/examples/Triumf/c++/ | Access to VME modules | C++ |
CAMAC | frontend.c | ../midas/examples/experiment/ | Access to CAMAC modules | C |
RS485 bus | mscb_fe.c | ../midas/examples/slowcont/ | Slow controls | C |
EPICS | frontend.c | ../midas/examples/epics/ | Slow controls | C |
The features of a typical frontend program are best explained by reference to examples of the user code
provided in the Midas Package.
Frontend code
The following sections refer to the templates for user frontend code found in the MIDAS packages (see Introduction).
Include files
The following example (from template frontend file fevmemodules.c - see Introduction) shows the standard include files needed for VME access with VMIC. The user may add any other include files as needed. In this case, header files for several device drivers have also been added. The optional include file "experim.h" is often included. This is a special include file for ease of communication between the C code and the ODB, and is generated by the user (see experim.h). The example below shows a typical list of include files for a frontend:
#include <stdio.h> // C standard headers #include <stdlib.h> #include "midas.h" // MIDAS include file #include "mvmestd.h" // VME header file #include "vmicvme.h" // VMIC header file #include "vmeio.h" // optional hardware header files #include "v1190B.h" #include "v792.h" #include "vf48.h" #include "v1729.h" #include "experim.h" // user-created with odbedit make command
Global declarations
The following example (from template frontend file fevmemodules.c - see Introduction) shows the global declaration. The declarations are system wide. Some may be changed to suit the user, but none should not be removed.
- frontend_name
- This value can be modified to reflect the purpose of the code
- frontend_call_loop
- If set to TRUE, the function frontend_loop() runs after every equipment loop. If FALSE, frontend_loop() does not run. The user can add suitable code to this routine if desired (e.g. to check for a condition).
- display_period
- The time interval (defined in milliseconds) between the refresh of a frontend status display. The value of zero disables the display. If the frontend is started in the background with the display enabled, the stdout should be redirected to the null device to prevent the process from hanging.
- max_event_size
- specifies the maximum size (in bytes) of the expected event.
- event_buffer_size
- specifies the maximum size (in bytes) of the buffer to be allocated by the system.
See below for an example of global declarations from a frontend.
/* The frontend name (client name) as seen by other MIDAS clients */ char *frontend_name = "fevmemodules"; /* 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 = 000; // /* maximum event size produced by this frontend */ INT max_event_size = 200000; // /* maximum event size for fragmented events (EQ_FRAGMENTED) */ INT max_event_size_frag = 5 * 1024 * 1024; // /* buffer size to hold events */ INT event_buffer_size = 10 * 100000;
Global User Declarations
After the global declarations, the user may add his or her own declarations. The following example (from template frontend file fevmemodules.c - see Introduction) defines various VME hardware parameters. An example of hardware declarations in a frontend are shown below:
/* Hardware */ MVME_INTERFACE *myvme; // /* VME base addresses */ DWORD VMEIO_BASE = 0x780000; DWORD VTDC0_BASE = 0xF10000; ....... /* Globals */ #define N_ADC 100 #define N_TDC 100 #define N_PTS 5000
System Function Prototypes
These prototypes declare the pre-defined system functions which should be present.
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();
Readout Function Prototypes
Following the previous group is a second group of prototypes, which define the readout functions. These depend on the defined equipments, and run when the respective equipment is triggered. In this example, two equipments will be defined, so there are two prototypes. The user functions will be described in detail in the following sections.
INT read_trigger_event(char *pevent, INT off); INT read_scaler_event(char *pevent, INT off);
If using an interrupt, callback function prototypes are also included
extern void interrupt_routine(void); void register_cnaf_callback(int debug);
Bank Definition and Equipment List
See Bank Definitions and Equipment List for detailed information.
However, this may be easier to follow if first the sequence of operations, and examples of the the different types of events (polled/interrupt or periodic etc.) and the functions they require in the frontend are introduced.
Sequence of Operations in the frontend
The following table shows the sequence of operations of the Frontend System functions. These subroutines must be present in frontend.c, but the contents are coded by the user. These functions are called by mfe.c at the appropriate time. The System Transition functions are associated with a particular Run Transition as shown below:
System Function | System Transition Function | Associated Transition | Action |
frontend_init() | Runs once after system initialization, before Equipment registration. | ||
begin_of_run() | TR_START | Runs after system statistics reset at each begin-of-run request. | |
pause_run() | TR_PAUSE | Runs at each pause-run request. | |
resume_run() | TR_RESUME | Runs at each resume-run request. | |
end_of_run() | TR_STOP | Runs at each end-of-run request. | |
frontend_exit() | Runs once before any Slow Control Equipment exit |
Each defined Equipment has the option to force itself to run at individual transition times (see Equipment ReadOn Flag), so that its equipment function will be called on a certain transition (or combination of transitions).
The system transition functions all run prior to the equipment functions. This gives the system the chance to take basic action on the transition request (e.g. enable/disable interrupt) before the equipment runs. All the transition routines run with a Transition Sequence number of 500 (the default). This allows users to add additional functions in the frontend that will run before or after any of the transitions (such as a prestart() or a poststop() function). See Run Transition Priority for more information.
Function frontend_init
No parameters.
This function runs once only at the application startup. Users may perform hardware checking, loading/setting of global variables, hot-link settings to the ODB etc. in frontend_init(), e.g.
INT frontend_init() { set_equipment_status(equipment[idx].name, "Initializing...", "yellow"); .... // Open VME interface status = mvme_open(&myvme, 0); // // Set am to A24 non-privileged Data mvme_set_am(myvme, MVME_AM_A24_ND); // // Set dmode to D32 mvme_set_dmode(myvme, MVME_DMODE_D32) .... // set_equipment_status(equipment[idx].name, "OK", "green"); return SUCCESS; }
Reporting Equipment Status
If running with run control utility mhttpd, a frontend can send an update to the main status page, to report on its progress, using the function set_equipment_status (see above example). This is useful when hardware can take a long time to respond.
Function begin_of_run
Parameters:
- INT run number provides the number of the current run being started
- char * error can be used for returning a message to the system. This message string will be logged into the midas.log file (see Message System.
This function is called every time a run start transition occurs, i.e. at begin-of-run. It allows the updating of user parameters, and the loading/setup/clearing of hardware. At the exit of this function, the acquisition should be armed and ready to test the interrupt (if used), e.g.
INT begin_of_run (INT runnumber, char * error) { ...... // Set am to A24 non-privileged Data mvme_set_am(myvme, MVME_AM_A24_ND); // Set dmode to D32 mvme_set_dmode(myvme, MVME_DMODE_D32); // //-------- ADCs ------------------- v792_Setup(myvme, VADC0_BASE, 2); v792_ThresholdWrite(myvme, VADC0_BASE, (WORD *)&(ts.v792.threshold1)); v792_DataClear(myvme, VADC0_BASE); csr = v792_CSR1Read(myvme, VADC0_BASE); printf("Data Ready ADC0: 0x%x\n", csr); ........ // Disable interrupt mvme_write_value(myvme, VLAM_BASE+4, 0x0); // Reset Latch mvme_write_value(myvme, VLAM_BASE, 0x1); // Clear pending interrupts mvme_write_value(myvme, VLAM_BASE+8, 0x0); // Enable interrupt inRun = 1; mvme_write_value(myvme, VLAM_BASE+4, inRun); return SUCCESS; }
Functions pause/resume_run
Parameters:
- INT run number provides the number of the current run being paused/resumed.
- char * error can be used for returning a message to the system. This message string will be logged into the midas.log file (see Message System.
These two functions are called upon "Pause" and "Resume" command respectively. Any code relevant to the upcoming run state can be included,e.g.
INT pause_run (INT run_number, char * error) { disable_trigger(); // Disable interrupt inRun = 0; mvme_write_value(myvme, VLAM_BASE+4, inRun); return SUCCESS; } // INT resume_run (INT run_number, char * error) { enable_trigger(); inRun = 1; mvme_write_value(myvme, VLAM_BASE+4, inRun); return SUCCESS; }
Function end_run
Parameters:
- INT run number provides the number of the current run being ended.
- char * error can be used for returning a message to the system. This message string will be logged into the midas.log file (see Message System.
This function is called at every "stop run" transition. It provides the opportunity to disable the hardware, e.g.
INT end_of_run(INT run_number, char *error) { // Stop DAQ for seting up the parameters vf48_AcqStop(myvme, VF48_BASE); // done = 0; stop_req = 0; inRun = 0; // Disable interrupt mvme_write_value(myvme, VLAM_BASE+4, inRun); trig_level = 0; // Close run gate vmeio_AsyncWrite(myvme, VMEIO_BASE, 0x0); return SUCCESS; }
Function frontend_exit
Parameters: none
The function runs When the frontend program is shut down. Can be used to release any locked resources like memory, communications ports etc. e.g.
function frontend_exit() { mvme_close(gVme); return; }
Function frontend_loop
Parameters: none
If the flag frontend_call_loop is set TRUE, this routine is called when the frontend is idle or once between every event. In the following example, it is being used to check for a timeout:
... BOOL frontend_call_loop = TRUE; ... // INT frontend_loop() { char str[128]; // if (stop_req && done==0) { db_set_value(hDB,0,"/logger/channels/0/Settings/Event limit", &evlimit, sizeof(evlimit), 1, TID_DWORD); if (cm_transition(TR_STOP, 0, str, sizeof(str), ASYNC, FALSE) != CM_SUCCESS) { cm_msg(MERROR, "VF48 Timeout", "cannot stop run: %s", str); } inRun = 0; // Disable interrupt mvme_write_value(myvme, VLAM_BASE+4, inRun); done = 1; cm_msg(MERROR, "VF48 Timeout","VF48 Stop requested"); } return SUCCESS; }
Event Types
Each equipment is associated with a unique event. Depending on the Equipment Flag in the Equipment Declaration, different types of events will be produced. Common event types are "polled events" where the Equipment Flag is EQ_POLLED, "interrupts events" where the Flag is EQ_INTERRUPT, and "periodic events" where the Flag is EQ_PERIODIC.
All events require an event readout routine. The readout routine is specified in the Equipment Declaration for each event type.
Polled and Interrupt Events
Polled and interrupt events (see Event Types) require several extra functions to handle the hardware that periodic events do not require. These are described below.
Function poll_event
Parameters:
- INT source
- INT count
- BOOL test
If the Equipment Type is EQ_POLLED, the poll_event() routine will be called as often as possible over the corresponding poll time (e.g. 500ms) given by each polling equipment.
In this case, the Equipment declaration would have this form:
EQUIPMENT equipment[] = {
{ "Trigger", // equipment name
{
...
EQ_POLLED, // equipment type
...
500, // poll for 500ms
...
"", "", "",},
read_trigger_event, // readout routine
...
The user must provide suitable code in the routine poll_event(), e.g.
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; int lam = 0; // for (i = 0; i < count; i++, lam++) { lam = vmeio_CsrRead(myvme, VMEIO_BASE); if (lam) if (!test) return lam; } return 0; }
An event readout routine must also be provided by the user.
Function interrupt_configure
- INT cmd
- INT source
- PTYPE adr
If the Equipment Type is EQ_INTERRUPT, an interrupt configuration routine called interrupt_configure() must be provided by the user. The interrupt configuration routine has the following declaration:
/*-- Interrupt configuration --------------------------*/ INT interrupt_configure(INT cmd, INT source, PTYPE adr) { int vec = 0; switch (cmd) { case CMD_INTERRUPT_ENABLE: if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x1); break; // case CMD_INTERRUPT_DISABLE: if (inRun) mvme_write_value(myvme, VLAM_BASE+4, 0x0); break; // case CMD_INTERRUPT_ATTACH: mvme_set_dmode(myvme, MVME_DMODE_D32); mvme_interrupt_attach(myvme, INT_LEVEL, INT_VECTOR, (void *)adr, &myinfo); mvme_write_value(myvme, VLAM_BASE+0x10, INT_VECTOR); vec = mvme_read_value(myvme, VLAM_BASE+0x10); printf("Interrupt Attached to 0x%x for vector:0x%x\n", adr, vec&0xFF); break; // case CMD_INTERRUPT_DETACH: printf("Interrupt Detach\n"); break; } return SUCCESS; }
Under the four commands listed above, the user must implement the hardware operation needed to perform the requested action. In the Midas drivers directory examples can be found of such an interrupt code for CAMAC. See source code such as hyt1331.c,ces8210.c
An event readout routine must also be provided by the user in the frontend.
Event Readout routine
In the case of POLLED or INTERRUPT events, the event readout routine is called an Interrupt readout routine.
An event readout routine (called when an event occurs) is usually of the form
INT function_name ( char *pevent ... ) { INT event_size; ........ // read data from hardware ........ // pack into banks depending on format ........ return (event_size); }
where the first argument of the readout function (pevent) provides the pointer to the newly constructed event, and points to the first valid location for storing the data.
- NOTE
- The return value is the event size, and must be the number of bytes collected in this function.
- The event serial number will be incremented by one for every call to the readout routine, as long as the returned size is non-zero.
- If the returned value is set to zero, the event will be dismissed and the serial number to that event will be decremented by one.
Polled or Interrupt readout routine
In the case of a Polled or Interrupt event, the content of the memory location pointed to by pevent (see Event Readout routine) prior to its use in the readout function, contains the interrupt source bitwise register. This feature can be exploited in order to identify which hardware module has triggered the readout when multiple interrupts have been assigned to the same readout function.
The examples below show a VME interrupt source for a given equipment. Depending whether USE_INT is defined, the Equipment will either use a Polled or an Interrupt mechanism. The Equipment declaration is of the form:
EQUIPMENT equipment[] = { // {"Trigger", /* equipment name */ ... #ifdef USE_INT EQ_INTERRUPT, /* equipment type */ #else EQ_POLLED, /* equipment type */ #endif /* interrupt source: crate 0, all stations */ LAM_SOURCE(0, 0x0), .... "", "", "", }, read_trigger_event, /* readout routine */ NULL, NULL, trigger_bank_list, }
Note that the LAM_SOURCE macro simply codes the parameters into a bitwise register.
The readout routine would contains code such as
INT read_trigger_event(char *pevent, INT off) { #if defined VADC0_CODE DWORD *pdata; #endif // #if defined VADC0_CODE /* read ADC0 data */ v792_EvtCntRead(myvme, VADC0_BASE, &evtcnt); ........ /* Read Event */ v792_EventRead(myvme, VADC0_BASE, pdata, &nentry); ........ v792_DataClear(myvme, VADC0_BASE); #endif // ........ return (size); }
The examples fevmemodules.c (VME) and frontend.c (CAMAC) (see Introduction contain a complete example of read_trigger_event().
General readout function
If the Equipment type is not EQ_INTERRUPT or EQ_POLLED (see Polled or Interrupt event) the readout routine is the only event routine that the user needs to provide in frontend.c .
In this case (e.g. a periodic event) the Equipment declaration may have this form:
EQUIPMENT equipment[] = { { "Scaler", // equipment name { ... EQ_PERIODIC // equipment type 0, // interrupt source (ignored) ... 10000, // period (read every 10s) ... "", "", "",}, read_scaler_event, // readout routine ...
An example of a scaler readout routine read_scaler_event() where the data is read out into data banks is shown below.
INT read_scaler_event(char *pevent, INT off) { DWORD *pdata, a; // /* init bank structure */ bk_init(pevent); // /* create SCLR bank */ bk_create(pevent, "SCLR", TID_DWORD, &pdata); // /* read scaler bank (CAMAC) */ for (a = 0; a < N_SCLR; a++) cam24i(CRATE, SLOT_SCLR, a, 0, pdata++); // /* close SCLR bank */ bk_close(pevent, pdata); // /* return event size in bytes */ return bk_size(pevent); }