mfe.c

Go to the documentation of this file.
00001 /********************************************************************\
00002 
00003   Name:         mfe.c
00004   Created by:   Stefan Ritt
00005 
00006   Contents:     The system part of the MIDAS frontend. Has to be
00007                 linked with user code to form a complete frontend
00008 
00009   $Log: mfe.c,v $
00010   Revision 1.72  2004/12/14 22:29:24  olchansk
00011   Include record name in error messages about "Cannot find statistics record"
00012 
00013   Revision 1.71  2004/10/01 23:35:53  midas
00014   Removed PRE/POST transitions and implemented sequence order of transitions
00015 
00016   Revision 1.70  2004/09/29 19:38:20  midas
00017   Decreased run stop polling time from 500ms to 100ms
00018 
00019   Revision 1.69  2004/09/29 00:16:21  midas
00020   Fixed shutdown problem with EB frontends
00021 
00022   Revision 1.68  2004/09/28 23:56:53  midas
00023   Added -i flag and EQ_EB flag for event building
00024 
00025   Revision 1.67  2004/09/24 21:42:17  midas
00026   Send manually triggered events in scheduler
00027 
00028   Revision 1.66  2004/09/21 21:09:15  midas
00029   Updated event statistics to ODB properly at EOR
00030 
00031   Revision 1.65  2004/05/07 19:40:11  midas
00032   Replaced min/max by MIN/MAX macros
00033 
00034   Revision 1.64  2004/03/26 09:31:56  midas
00035   Converted pritnf() to cm_msg() for statistics record error
00036 
00037   Revision 1.63  2004/03/19 09:31:43  midas
00038   Re-formatted comments
00039 
00040   Revision 1.62  2004/01/08 08:40:10  midas
00041   Implemented standard indentation
00042 
00043   Revision 1.61  2003/11/24 08:22:46  midas
00044   Changed timeouts from INT to DWORD, added ignore_timeout to cm_cleanup, adde '-f' flag to ODBEdit 'cleanup'
00045 
00046   Revision 1.60  2003/11/20 11:29:44  midas
00047   Implemented db_check_record and use it in most places instead of db_create_record
00048 
00049   Revision 1.59  2003/11/01 00:48:14  olchansk
00050   abort if cannot read /runinfo/run number
00051   abort if run number resets to zero
00052 
00053   Revision 1.58  2003/10/29 15:33:48  midas
00054   Properly display message about stopped previous frontend
00055 
00056   Revision 1.57  2003/09/30 19:47:57  midas
00057   Removed tabs
00058 
00059   Revision 1.56  2003/09/30 18:30:07  midas
00060   Put CNAF functionality under #ifdef HAVE_CAMAC
00061 
00062   Revision 1.55  2003/05/09 07:40:04  midas
00063   Added extra parameter to cm_get_environment
00064 
00065   Revision 1.54  2003/05/07 10:57:10  midas
00066   Removed tabs
00067 
00068   Revision 1.53  2003/04/25 10:41:42  midas
00069   Removed FTCP mode in update_odb()
00070 
00071   Revision 1.52  2003/04/25 07:45:03  midas
00072   Write first event to ODB only if logger uses ROOT format
00073 
00074   Revision 1.51  2003/04/14 12:40:23  midas
00075   Create ODB entries for structured banks derived from BANK_LIST in frontend.c
00076 
00077   Revision 1.50  2003/04/14 05:12:54  pierre
00078   fix send_event for handle=0
00079 
00080   Revision 1.49  2003/04/01 19:22:44  pierre
00081   fix update_odb for hKeyl copy
00082 
00083   Revision 1.48  2003/03/31 08:27:34  midas
00084   Fixed alignment bug for strings
00085 
00086   Revision 1.47  2003/03/28 09:13:05  midas
00087   Added warning for uncreated structured banks
00088 
00089   Revision 1.46  2003/03/28 08:55:11  midas
00090   Added code for structured banks
00091 
00092   Revision 1.45  2002/10/15 18:44:23  olchansk
00093   fix printf() and cm_msg() format mismatches
00094 
00095   Revision 1.44  2002/10/15 18:15:25  olchansk
00096   disable dtsout, stderr buffering,
00097   catch-ignore SIGPIPE
00098 
00099   Revision 1.43  2002/10/15 18:13:59  olchansk
00100   always recreate the statistics record.
00101 
00102   Revision 1.42  2002/10/15 18:09:59  olchansk
00103   catch bm_xxx() errors and bail out from schedule(). This fixes infinite looping after rpc timeouts.
00104 
00105   Revision 1.41  2002/09/13 07:32:47  midas
00106   Added client name to cm_cleanup()
00107 
00108   Revision 1.40  2002/06/03 06:07:15  midas
00109   Added extra parameter to ss_daemon_init to keep stdout
00110 
00111   Revision 1.39  2002/05/15 23:43:56  midas
00112   Added event fragmentation
00113 
00114   Revision 1.38  2002/05/10 01:41:19  midas
00115   Added optional debug output to cm_transition
00116 
00117   Revision 1.37  2002/05/08 19:54:40  midas
00118   Added extra parameter to function db_get_value()
00119 
00120   Revision 1.36  2001/11/21 08:37:32  midas
00121   Took out db_delete_key for statistics again (not needed)
00122 
00123   Revision 1.35  2001/11/20 19:22:07  pierre
00124   - Add rpc_flush_event in case no periodic eqp
00125   - Force rpc_flush_event for low trigger rate
00126   - Patch /Statistics in case it exists but size=0
00127 
00128   Revision 1.34  2001/06/27 12:34:49  midas
00129   Added -D flag to become a daemon
00130 
00131   Revision 1.33  2001/04/06 04:13:40  midas
00132   Put cm_cleanup() in init code
00133 
00134   Revision 1.32  2001/01/29 09:50:53  midas
00135   Changed send_event() parameter from event_id to equipment index
00136 
00137   Revision 1.31  2000/11/14 08:30:03  midas
00138   SLOW events are not any more sent to the ODB when the history is on
00139 
00140   Revision 1.30  2000/11/06 10:10:23  midas
00141   Flush events for set-ups with only non-periodic events
00142 
00143   Revision 1.29  2000/10/23 14:19:06  midas
00144   Added idle period for slow control equipment
00145 
00146   Revision 1.28  2000/09/28 13:16:02  midas
00147   Fixed bug that MANUAL_TRIG only events are not read out during transitions
00148 
00149   Revision 1.27  2000/09/28 13:02:03  midas
00150   Added manual triggered events
00151 
00152   Revision 1.26  2000/08/21 11:01:11  midas
00153   Changed comments
00154 
00155   Revision 1.25  2000/08/21 10:44:58  midas
00156   Removed reconnect functionality, it's better to restart the frontend via
00157   /programs/frontend/auto restart.
00158 
00159   Revision 1.24  2000/08/21 10:36:41  midas
00160   Reworked event and event buffer sizes:
00161   - Both max_event_size and event_buffer_size must be defined in user code
00162   - While max_event_size is used for frontend buffers, MAX_EVENT_SIZE is the
00163     system limit which must be larger than max_event_size
00164   - Event size returned from user readout routine is now checked against event
00165     size limits
00166   - Buffer settings are now always displayed, not only under VxWorks
00167 
00168   Revision 1.23  2000/08/09 12:02:43  midas
00169   Evaluate return status in tr_xxx functions properly
00170 
00171   Revision 1.22  2000/08/09 11:37:56  midas
00172   Rearranged serial number increments
00173 
00174   Revision 1.21  2000/07/11 16:43:40  pierre
00175   - Fix serial number for POLL super event.
00176 
00177   Revision 1.20  2000/04/03 12:27:43  midas
00178   Changed auto restart to 20 seconds in main loop
00179 
00180   Revision 1.19  2000/03/22 14:42:48  midas
00181   Fixed bug with invalid pointer
00182 
00183   Revision 1.18  2000/03/17 13:00:05  midas
00184   Frontends use default timeout fo 60 sec.
00185 
00186   Revision 1.17  2000/03/01 23:29:20  midas
00187   Fixed bug with wrong event header data size in super eventsvents mfe.c
00188 
00189   Revision 1.16  2000/02/29 21:59:45  midas
00190   Added auto restart
00191 
00192   Revision 1.15  2000/02/26 01:25:54  midas
00193   Fixed bug that number of sent events was not cleared at start
00194 
00195   Revision 1.14  2000/02/25 20:22:49  midas
00196   Added super-event scheme
00197 
00198   Revision 1.13  2000/02/24 22:38:19  midas
00199   Outcommented USE_EVENT_CHANNEL
00200 
00201   Revision 1.12  2000/02/24 22:29:24  midas
00202   Added deferred transitions
00203 
00204   Revision 1.11  1999/12/08 00:50:29  pierre
00205   - fix EVID (ybos: strncpy to strncmp)
00206 
00207   Revision 1.10  1999/11/24 00:49:59  pierre
00208   - YBOS reject EVID bank in udpate_odb with eb_functions
00209 
00210   Revision 1.9  1999/11/23 18:25:25  pierre
00211   - YBOS reject EVID bank in udpate_odb
00212 
00213   Revision 1.8  1999/10/18 14:41:51  midas
00214   Use /programs/<name>/Watchdog timeout in all programs as timeout value. The
00215   default value can be submitted by calling cm_connect_experiment1(..., timeout)
00216 
00217   Revision 1.7  1999/10/15 12:17:52  midas
00218   Increased timeout
00219 
00220   Revision 1.6  1999/09/27 13:49:04  midas
00221   Added bUnique parameter to cm_shutdown
00222 
00223   Revision 1.5  1999/09/23 12:45:48  midas
00224   Added 32 bit banks
00225 
00226   Revision 1.4  1999/06/23 09:38:51  midas
00227   - Added D8_BKTYPE
00228   - Fixed CAMAC server F24 bug
00229   - cm_synchronize only called for VxWorks
00230 
00231   Revision 1.3  1998/12/10 12:50:47  midas
00232   Program abort with "!" now works without a return under UNIX
00233 
00234   Revision 1.2  1998/10/12 12:19:01  midas
00235   Added Log tag in header
00236 
00237 
00238 \********************************************************************/
00239 
00240 #include <stdio.h>
00241 #include <assert.h>
00242 #include "midas.h"
00243 #include "msystem.h"
00244 #include "mcstd.h"
00245 
00246 #ifdef YBOS_SUPPORT
00247 #include "ybos.h"
00248 #endif
00249 
00250 /*------------------------------------------------------------------*/
00251 
00252 /* items defined in frontend.c */
00253 
00254 extern char *frontend_name;
00255 extern char *frontend_file_name;
00256 extern BOOL frontend_call_loop;
00257 extern INT max_event_size;
00258 extern INT max_event_size_frag;
00259 extern INT event_buffer_size;
00260 extern INT display_period;
00261 extern INT frontend_init(void);
00262 extern INT frontend_exit(void);
00263 extern INT frontend_loop(void);
00264 extern INT begin_of_run(INT run_number, char *error);
00265 extern INT end_of_run(INT run_number, char *error);
00266 extern INT pause_run(INT run_number, char *error);
00267 extern INT resume_run(INT run_number, char *error);
00268 extern INT poll_event(INT source, INT count, BOOL test);
00269 extern INT interrupt_configure(INT cmd, INT source, PTYPE adr);
00270 
00271 /*------------------------------------------------------------------*/
00272 
00273 /* globals */
00274 
00275 #undef USE_EVENT_CHANNEL
00276 
00277 #define SERVER_CACHE_SIZE  100000       /* event cache before buffer */
00278 
00279 #define ODB_UPDATE_TIME      1000       /* 1 seconds for ODB update */
00280 
00281 #define DEFAULT_FE_TIMEOUT  60000       /* 60 seconds for watchdog timeout */
00282 
00283 INT run_state;                     /* STATE_RUNNING, STATE_STOPPED, STATE_PAUSED */
00284 INT run_number;
00285 DWORD actual_time;                 /* current time in seconds since 1970 */
00286 DWORD actual_millitime;            /* current time in milliseconds */
00287 
00288 char host_name[HOST_NAME_LENGTH];
00289 char exp_name[NAME_LENGTH];
00290 char full_frontend_name[256];
00291 
00292 INT max_bytes_per_sec;
00293 INT optimize = 0;                  /* set this to one to opimize TCP buffer size */
00294 INT fe_stop = 0;                   /* stop switch for VxWorks */
00295 BOOL debug;                        /* disable watchdog messages from server */
00296 DWORD auto_restart = 0;            /* restart run after event limit reached stop */
00297 INT manual_trigger_event_id = 0;   /* set from the manual_trigger callback */
00298 INT frontend_index = -1;           /* frontend index for event building */
00299 
00300 HNDLE hDB;
00301 
00302 #ifdef YBOS_SUPPORT
00303 struct {
00304    DWORD ybos_type;
00305    DWORD odb_type;
00306    INT tsize;
00307 } id_map[] = {
00308    {
00309    A1_BKTYPE, TID_CHAR, 1}, {
00310    I1_BKTYPE, TID_BYTE, 1}, {
00311    I2_BKTYPE, TID_WORD, 2}, {
00312    I4_BKTYPE, TID_DWORD, 4}, {
00313    F4_BKTYPE, TID_FLOAT, 4}, {
00314    D8_BKTYPE, TID_DOUBLE, 8}, {
00315    0, 0}
00316 };
00317 #endif
00318 
00319 extern EQUIPMENT equipment[];
00320 
00321 EQUIPMENT *interrupt_eq = NULL;
00322 EVENT_HEADER *interrupt_odb_buffer;
00323 BOOL interrupt_odb_buffer_valid;
00324 
00325 int send_event(INT index);
00326 void send_all_periodic_events(INT transition);
00327 void interrupt_routine(void);
00328 void interrupt_enable(BOOL flag);
00329 void display(BOOL bInit);
00330 
00331 /*---- ODB records -------------------------------------------------*/
00332 
00333 #define EQUIPMENT_COMMON_STR "\
00334 Event ID = WORD : 0\n\
00335 Trigger mask = WORD : 0\n\
00336 Buffer = STRING : [32] SYSTEM\n\
00337 Type = INT : 0\n\
00338 Source = INT : 0\n\
00339 Format = STRING : [8] FIXED\n\
00340 Enabled = BOOL : 0\n\
00341 Read on = INT : 0\n\
00342 Period = INT : 0\n\
00343 Event limit = DOUBLE : 0\n\
00344 Num subevents = DWORD : 0\n\
00345 Log history = INT : 0\n\
00346 Frontend host = STRING : [32] \n\
00347 Frontend name = STRING : [32] \n\
00348 Frontend file name = STRING : [256] \n\
00349 "
00350 
00351 #define EQUIPMENT_STATISTICS_STR "\
00352 Events sent = DOUBLE : 0\n\
00353 Events per sec. = DOUBLE : 0\n\
00354 kBytes per sec. = DOUBLE : 0\n\
00355 "
00356 
00357 /*-- transition callbacks ------------------------------------------*/
00358 
00359 /*-- start ---------------------------------------------------------*/
00360 
00361 INT tr_start(INT rn, char *error)
00362 {
00363    INT i, status;
00364 
00365    /* reset serial numbers */
00366    for (i = 0; equipment[i].name[0]; i++) {
00367       equipment[i].serial_number = 1;
00368       equipment[i].subevent_number = 0;
00369       equipment[i].stats.events_sent = 0;
00370       equipment[i].odb_in = equipment[i].odb_out = 0;
00371    }
00372 
00373    status = begin_of_run(rn, error);
00374 
00375    if (status == CM_SUCCESS) {
00376       run_state = STATE_RUNNING;
00377       run_number = rn;
00378 
00379       send_all_periodic_events(TR_START);
00380 
00381       if (display_period) {
00382          ss_printf(14, 2, "Running ");
00383          ss_printf(36, 2, "%d", rn);
00384       }
00385 
00386       /* enable interrupts */
00387       interrupt_enable(TRUE);
00388    }
00389 
00390    return status;
00391 }
00392 
00393 /*-- prestop -------------------------------------------------------*/
00394 
00395 INT tr_stop(INT rn, char *error)
00396 {
00397    INT status, i;
00398    EQUIPMENT *eq;
00399 
00400    /* disable interrupts */
00401    interrupt_enable(FALSE);
00402 
00403    status = end_of_run(rn, error);
00404 
00405    if (status == CM_SUCCESS) {
00406       /* don't send events if already stopped */
00407       if (run_state != STATE_STOPPED)
00408          send_all_periodic_events(TR_STOP);
00409 
00410       run_state = STATE_STOPPED;
00411       run_number = rn;
00412 
00413       if (display_period)
00414          ss_printf(14, 2, "Stopped ");
00415    } else
00416       interrupt_enable(TRUE);
00417 
00418    /* flush remaining buffered events */
00419    rpc_flush_event();
00420    for (i = 0; equipment[i].name[0]; i++)
00421       if (equipment[i].buffer_handle) {
00422          INT err = bm_flush_cache(equipment[i].buffer_handle, SYNC);
00423          if (err != BM_SUCCESS) {
00424             cm_msg(MERROR, "tr_prestop", "bm_flush_cache(SYNC) error %d", err);
00425             return err;
00426          }
00427       }
00428 
00429    /* update final statistics record in ODB */
00430    for (i = 0; equipment[i].name[0]; i++) {
00431       eq = &equipment[i];
00432       eq->stats.events_sent += eq->events_sent;
00433       eq->bytes_sent = 0;
00434       eq->events_sent = 0;
00435    }
00436 
00437    db_send_changed_records();
00438 
00439    return status;
00440 }
00441 
00442 /*-- pause ---------------------------------------------------------*/
00443 
00444 INT tr_pause(INT rn, char *error)
00445 {
00446    INT status;
00447 
00448    /* disable interrupts */
00449    interrupt_enable(FALSE);
00450 
00451    status = pause_run(rn, error);
00452 
00453    if (status == CM_SUCCESS) {
00454       run_state = STATE_PAUSED;
00455       run_number = rn;
00456 
00457       send_all_periodic_events(TR_PAUSE);
00458 
00459       if (display_period)
00460          ss_printf(14, 2, "Paused  ");
00461    } else
00462       interrupt_enable(TRUE);
00463 
00464    return status;
00465 }
00466 
00467 /*-- resume --------------------------------------------------------*/
00468 
00469 INT tr_resume(INT rn, char *error)
00470 {
00471    INT status;
00472 
00473    status = resume_run(rn, error);
00474 
00475    if (status == CM_SUCCESS) {
00476       run_state = STATE_RUNNING;
00477       run_number = rn;
00478 
00479       send_all_periodic_events(TR_RESUME);
00480 
00481       if (display_period)
00482          ss_printf(14, 2, "Running ");
00483 
00484       /* enable interrupts */
00485       interrupt_enable(TRUE);
00486    }
00487 
00488    return status;
00489 }
00490 
00491 /*------------------------------------------------------------------*/
00492 
00493 INT manual_trigger(INT index, void *prpc_param[])
00494 {
00495    manual_trigger_event_id = CWORD(0);
00496    return SUCCESS;
00497 }
00498 
00499 /*------------------------------------------------------------------*/
00500 
00501 INT register_equipment(void)
00502 {
00503    INT index, count, size, status, i, j, k, n;
00504    char str[256];
00505    EQUIPMENT_INFO *eq_info;
00506    EQUIPMENT_STATS *eq_stats;
00507    DWORD start_time, delta_time;
00508    HNDLE hKey;
00509    BOOL manual_trig_flag = FALSE;
00510    BANK_LIST *bank_list;
00511    DWORD dummy;
00512 
00513    /* get current ODB run state */
00514    size = sizeof(run_state);
00515    run_state = STATE_STOPPED;
00516    db_get_value(hDB, 0, "/Runinfo/State", &run_state, &size, TID_INT, TRUE);
00517    size = sizeof(run_number);
00518    run_number = 1;
00519    status =
00520        db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
00521    assert(status == SUCCESS);
00522 
00523    /* scan EQUIPMENT table from FRONTEND.C */
00524    for (index = 0; equipment[index].name[0]; index++) {
00525       eq_info = &equipment[index].info;
00526       eq_stats = &equipment[index].stats;
00527 
00528       if (eq_info->event_id == 0) {
00529          printf("\nEvent ID 0 for %s not allowed\n", equipment[index].name);
00530          cm_disconnect_experiment();
00531          ss_sleep(5000);
00532          exit(0);
00533       }
00534 
00535       /* init status */
00536       equipment[index].status = FE_SUCCESS;
00537 
00538       /* check for event builder event */
00539       if (eq_info->eq_type & EQ_EB) {
00540 
00541          if (frontend_index == -1) {
00542             printf("\nEquipment \"%s\" has EQ_EB set, but no", equipment[index].name); 
00543             printf(" index specified via \"-i\" flag.\nExiting.");
00544             cm_disconnect_experiment();
00545             ss_sleep(5000);
00546             exit(0);
00547          }
00548 
00549          /* modify equipment name to <name>xx where xx is the frontend index*/
00550          sprintf(equipment[index].name+strlen(equipment[index].name), "%02d", frontend_index);
00551 
00552          /* modify event buffer name to <name>xx where xx is the frontend index*/
00553          sprintf(eq_info->buffer+strlen(eq_info->buffer), "%02d", frontend_index);
00554       }
00555 
00556       sprintf(str, "/Equipment/%s/Common", equipment[index].name);
00557 
00558       /* get last event limit from ODB */
00559       if (eq_info->eq_type != EQ_SLOW) {
00560          db_find_key(hDB, 0, str, &hKey);
00561          size = sizeof(double);
00562          if (hKey)
00563             db_get_value(hDB, hKey, "Event limit", &eq_info->event_limit, &size,
00564                          TID_DOUBLE, TRUE);
00565       }
00566 
00567       /* Create common subtree */
00568       status = db_check_record(hDB, 0, str, EQUIPMENT_COMMON_STR, TRUE);
00569       if (status != DB_SUCCESS) {
00570          printf("Cannot check equipment record, status = %d\n", status);
00571          ss_sleep(3000);
00572       }
00573       db_find_key(hDB, 0, str, &hKey);
00574 
00575       if (equal_ustring(eq_info->format, "YBOS"))
00576          equipment[index].format = FORMAT_YBOS;
00577       else if (equal_ustring(eq_info->format, "FIXED"))
00578          equipment[index].format = FORMAT_FIXED;
00579       else                      /* default format is MIDAS */
00580          equipment[index].format = FORMAT_MIDAS;
00581 
00582       gethostname(eq_info->frontend_host, sizeof(eq_info->frontend_host));
00583       strcpy(eq_info->frontend_name, full_frontend_name);
00584       strcpy(eq_info->frontend_file_name, frontend_file_name);
00585 
00586       /* set record from equipment[] table in frontend.c */
00587       db_set_record(hDB, hKey, eq_info, sizeof(EQUIPMENT_INFO), 0);
00588 
00589       /* open hot link to equipment info */
00590       db_open_record(hDB, hKey, eq_info, sizeof(EQUIPMENT_INFO), MODE_READ, NULL, NULL);
00591 
00592     /*---- Create variables record ---------------------------------*/
00593       sprintf(str, "/Equipment/%s/Variables", equipment[index].name);
00594       if (equipment[index].event_descrip) {
00595          if (equipment[index].format == FORMAT_FIXED)
00596             db_check_record(hDB, 0, str, (char *) equipment[index].event_descrip, TRUE);
00597          else {
00598             /* create bank descriptions */
00599             bank_list = (BANK_LIST *) equipment[index].event_descrip;
00600 
00601             for (; bank_list->name[0]; bank_list++) {
00602                /* mabye needed later...
00603                   if (bank_list->output_flag == 0)
00604                   continue;
00605                 */
00606 
00607                if (bank_list->type == TID_STRUCT) {
00608                   sprintf(str, "/Equipment/%s/Variables/%s", equipment[index].name,
00609                           bank_list->name);
00610                   status =
00611                       db_check_record(hDB, 0, str, strcomb(bank_list->init_str), TRUE);
00612                   if (status != DB_SUCCESS) {
00613                      printf("Cannot check/create record \"%s\", status = %d\n", str,
00614                             status);
00615                      ss_sleep(3000);
00616                   }
00617                } else {
00618                   sprintf(str, "/Equipment/%s/Variables/%s", equipment[index].name,
00619                           bank_list->name);
00620                   dummy = 0;
00621                   db_set_value(hDB, 0, str, &dummy, rpc_tid_size(bank_list->type), 1,
00622                                bank_list->type);
00623                }
00624             }
00625          }
00626       } else
00627          db_create_key(hDB, 0, str, TID_KEY);
00628 
00629       sprintf(str, "/Equipment/%s/Variables", equipment[index].name);
00630       db_find_key(hDB, 0, str, &hKey);
00631       equipment[index].hkey_variables = hKey;
00632 
00633       /*---- Create and initialize statistics tree -------------------*/
00634       
00635       sprintf(str, "/Equipment/%s/Statistics", equipment[index].name);
00636 
00637       status = db_check_record(hDB, 0, str, EQUIPMENT_STATISTICS_STR, TRUE);
00638       if (status != DB_SUCCESS) {
00639          printf("Cannot create/check statistics record \'%s\', error %d\n", str, status);
00640          ss_sleep(3000);
00641       }
00642 
00643       status = db_find_key(hDB, 0, str, &hKey);
00644       if (status != DB_SUCCESS) {
00645          printf("Cannot find statistics record \'%s\', error %d\n", str, status);
00646          ss_sleep(3000);
00647       }
00648 
00649       eq_stats->events_sent = 0;
00650       eq_stats->events_per_sec = 0;
00651       eq_stats->kbytes_per_sec = 0;
00652 
00653       /* open hot link to statistics tree */
00654       status =
00655           db_open_record(hDB, hKey, eq_stats, sizeof(EQUIPMENT_STATS), MODE_WRITE, NULL,
00656                          NULL);
00657       if (status != DB_SUCCESS) {
00658          cm_msg(MERROR, "register_equipment", 
00659              "Cannot open statistics record \'%s\', error %d. Probably other FE is using it",
00660               str, status);
00661          ss_sleep(3000);
00662       }
00663 
00664       /*---- open event buffer ---------------------------------------*/
00665 
00666       if (eq_info->buffer[0]) {
00667          status =
00668              bm_open_buffer(eq_info->buffer, EVENT_BUFFER_SIZE,
00669                             &equipment[index].buffer_handle);
00670          if (status != BM_SUCCESS && status != BM_CREATED) {
00671             cm_msg(MERROR, "register_equipment",
00672                    "Cannot open event buffer. Try to reduce EVENT_BUFFER_SIZE in midas.h \
00673 and rebuild the system.");
00674             return 0;
00675          }
00676 
00677          /* set the default buffer cache size */
00678          bm_set_cache_size(equipment[index].buffer_handle, 0, SERVER_CACHE_SIZE);
00679       } else
00680          equipment[index].buffer_handle = 0;
00681 
00682     /*---- evaluate polling count ----------------------------------*/
00683       if (eq_info->eq_type & EQ_POLLED) {
00684          if (display_period)
00685             printf("\nCalibrating");
00686 
00687          count = 1;
00688          do {
00689             if (display_period)
00690                printf(".");
00691 
00692             start_time = ss_millitime();
00693 
00694             poll_event(equipment[index].info.source, count, TRUE);
00695 
00696             delta_time = ss_millitime() - start_time;
00697 
00698             if (delta_time > 0)
00699                count = (INT) ((double) count * 100 / delta_time);
00700             else
00701                count *= 100;
00702          } while (delta_time > 120 || delta_time < 80);
00703 
00704          equipment[index].poll_count = (INT) ((double) eq_info->period / 100 * count);
00705 
00706          if (display_period)
00707             printf("OK\n");
00708       }
00709 
00710     /*---- initialize interrupt events -----------------------------*/
00711       if (eq_info->eq_type & EQ_INTERRUPT) {
00712          /* install interrupt for interrupt events */
00713 
00714          for (i = 0; equipment[i].name[0]; i++)
00715             if (equipment[i].info.eq_type & EQ_POLLED) {
00716                equipment[index].status = FE_ERR_DISABLED;
00717                cm_msg(MINFO, "register_equipment",
00718                       "Interrupt readout cannot be combined with polled readout");
00719             }
00720 
00721          if (equipment[index].status != FE_ERR_DISABLED) {
00722             if (eq_info->enabled) {
00723                if (interrupt_eq) {
00724                   equipment[index].status = FE_ERR_DISABLED;
00725                   cm_msg(MINFO, "register_equipment",
00726                          "Defined more than one equipment with interrupt readout");
00727                } else {
00728                   interrupt_configure(CMD_INTERRUPT_ATTACH, eq_info->source,
00729                                       (PTYPE) interrupt_routine);
00730                   interrupt_eq = &equipment[index];
00731                   interrupt_odb_buffer = malloc(MAX_EVENT_SIZE + sizeof(EVENT_HEADER));
00732                }
00733             } else {
00734                equipment[index].status = FE_ERR_DISABLED;
00735                cm_msg(MINFO, "register_equipment",
00736                       "Equipment %s disabled in file \"frontend.c\"",
00737                       equipment[index].name);
00738             }
00739          }
00740       }
00741 
00742     /*---- initialize slow control equipment -----------------------*/
00743       if (eq_info->eq_type & EQ_SLOW) {
00744          /* resolve duplicate device names */
00745          for (i = 0; equipment[index].driver[i].name[0]; i++)
00746             for (j = i + 1; equipment[index].driver[j].name[0]; j++)
00747                if (equal_ustring(equipment[index].driver[i].name,
00748                                  equipment[index].driver[j].name)) {
00749                   strcpy(str, equipment[index].driver[i].name);
00750                   for (k = 0, n = 0; equipment[index].driver[k].name[0]; k++)
00751                      if (equal_ustring(str, equipment[index].driver[k].name))
00752                         sprintf(equipment[index].driver[k].name, "%s_%d", str, n++);
00753 
00754                   break;
00755                }
00756 
00757          /* loop over equipment list and call class driver's init method */
00758          if (eq_info->enabled)
00759             equipment[index].status = equipment[index].cd(CMD_INIT, &equipment[index]);
00760          else {
00761             equipment[index].status = FE_ERR_DISABLED;
00762             cm_msg(MINFO, "register_equipment",
00763                    "Equipment %s disabled in file \"frontend.c\"", equipment[index].name);
00764          }
00765 
00766          /* let user read error messages */
00767          if (equipment[index].status != FE_SUCCESS)
00768             ss_sleep(3000);
00769       }
00770 
00771       /*---- register callback for manual triggered events -----------*/
00772       if (eq_info->eq_type & EQ_MANUAL_TRIG) {
00773          if (!manual_trig_flag)
00774             cm_register_function(RPC_MANUAL_TRIG, manual_trigger);
00775 
00776          manual_trig_flag = TRUE;
00777       }
00778    }
00779 
00780    return SUCCESS;
00781 }
00782 
00783 /*------------------------------------------------------------------*/
00784 
00785 void update_odb(EVENT_HEADER * pevent, HNDLE hKey, INT format)
00786 {
00787    INT size, i, ni4, tsize, status, n_data;
00788    void *pdata;
00789    char name[5];
00790    BANK_HEADER *pbh;
00791    BANK *pbk;
00792    BANK32 *pbk32;
00793    void *pydata;
00794    DWORD odb_type;
00795    DWORD *pyevt, bkname;
00796    WORD bktype;
00797    HNDLE hKeyRoot, hKeyl;
00798    KEY key;
00799 
00800    /* outcommented sind db_find_key does not work in FTCP mode, SR 25.4.03
00801       rpc_set_option(-1, RPC_OTRANSPORT, RPC_FTCP); */
00802 
00803    if (format == FORMAT_FIXED) {
00804       if (db_set_record(hDB, hKey, (char *) (pevent + 1),
00805                         pevent->data_size, 0) != DB_SUCCESS)
00806          cm_msg(MERROR, "update_odb", "event #%d size mismatch", pevent->event_id);
00807    } else if (format == FORMAT_MIDAS) {
00808       pbh = (BANK_HEADER *) (pevent + 1);
00809       pbk = NULL;
00810       pbk32 = NULL;
00811       do {
00812          /* scan all banks */
00813          if (bk_is32(pbh)) {
00814             size = bk_iterate32(pbh, &pbk32, &pdata);
00815             if (pbk32 == NULL)
00816                break;
00817             bkname = *((DWORD *) pbk32->name);
00818             bktype = (WORD) pbk32->type;
00819          } else {
00820             size = bk_iterate(pbh, &pbk, &pdata);
00821             if (pbk == NULL)
00822                break;
00823             bkname = *((DWORD *) pbk->name);
00824             bktype = (WORD) pbk->type;
00825          }
00826 
00827          n_data = size;
00828          if (rpc_tid_size(bktype & 0xFF))
00829             n_data /= rpc_tid_size(bktype & 0xFF);
00830 
00831          /* get bank key */
00832          *((DWORD *) name) = bkname;
00833          name[4] = 0;
00834 
00835          if (bktype == TID_STRUCT) {
00836             status = db_find_key(hDB, hKey, name, &hKeyRoot);
00837             if (status != DB_SUCCESS) {
00838                cm_msg(MERROR, "update_odb",
00839                       "please define bank %s in BANK_LIST in frontend.c", name);
00840                continue;
00841             }
00842 
00843             /* write structured bank */
00844             for (i = 0;; i++) {
00845                status = db_enum_key(hDB, hKeyRoot, i, &hKeyl);
00846                if (status == DB_NO_MORE_SUBKEYS)
00847                   break;
00848 
00849                db_get_key(hDB, hKeyl, &key);
00850 
00851                /* adjust for alignment */
00852                if (key.type != TID_STRING && key.type != TID_LINK)
00853                   pdata =
00854                       (void *) VALIGN(pdata, MIN(ss_get_struct_align(), key.item_size));
00855 
00856                status = db_set_data(hDB, hKeyl, pdata, key.item_size * key.num_values,
00857                                     key.num_values, key.type);
00858                if (status != DB_SUCCESS) {
00859                   cm_msg(MERROR, "update_odb", "cannot write %s to ODB", name);
00860                   continue;
00861                }
00862 
00863                /* shift data pointer to next item */
00864                (char *) pdata += key.item_size * key.num_values;
00865             }
00866          } else {
00867             /* write variable length bank  */
00868             if (n_data > 0)
00869                db_set_value(hDB, hKey, name, pdata, size, n_data, bktype & 0xFF);
00870          }
00871 
00872       } while (1);
00873    } else if (format == FORMAT_YBOS) {
00874 #ifdef YBOS_SUPPORT
00875       YBOS_BANK_HEADER *pybkh;
00876 
00877       /* skip the lrl (4 bytes per event) */
00878       pyevt = (DWORD *) (pevent + 1);
00879       pybkh = NULL;
00880       do {
00881          /* scan all banks */
00882          ni4 = ybk_iterate(pyevt, &pybkh, &pydata);
00883          if (pybkh == NULL || ni4 == 0)
00884             break;
00885 
00886          /* find the corresponding odb type */
00887          tsize = odb_type = 0;
00888          for (i = 0; id_map[0].ybos_type > 0; i++) {
00889             if (pybkh->type == id_map[i].ybos_type) {
00890                odb_type = id_map[i].odb_type;
00891                tsize = id_map[i].tsize;
00892                break;
00893             }
00894          }
00895 
00896          /* extract bank name (key name) */
00897          *((DWORD *) name) = pybkh->name;
00898          name[4] = 0;
00899 
00900          /* reject EVID bank */
00901          if (strncmp(name, "EVID", 4) == 0)
00902             continue;
00903 
00904          /* correct YBS number of entries */
00905          if (pybkh->type == D8_BKTYPE)
00906             ni4 /= 2;
00907          if (pybkh->type == I2_BKTYPE)
00908             ni4 *= 2;
00909          if (pybkh->type == I1_BKTYPE || pybkh->type == A1_BKTYPE)
00910             ni4 *= 4;
00911 
00912          /* write bank to ODB, ni4 always in I*4 */
00913          size = ni4 * tsize;
00914          if ((status =
00915               db_set_value(hDB, hKey, name, pydata, size, ni4,
00916                            odb_type & 0xFF)) != DB_SUCCESS) {
00917             printf("status:%i odb_type:%li name:%s ni4:%i size:%i tsize:%i\n", status,
00918                    odb_type, name, ni4, size, tsize);
00919             for (i = 0; i < 6; i++)
00920                printf("data: %f\n", *((float *) (pydata))++);
00921          }
00922       } while (1);
00923 #endif                          /* YBOS_SUPPORT */
00924    }
00925 
00926    rpc_set_option(-1, RPC_OTRANSPORT, RPC_TCP);
00927 }
00928 
00929 /*------------------------------------------------------------------*/
00930 
00931 int send_event(INT index)
00932 {
00933    EQUIPMENT_INFO *eq_info;
00934    EVENT_HEADER *pevent, *pfragment;
00935    char *pdata;
00936    unsigned char *pd;
00937    INT i, status;
00938    DWORD sent, size;
00939    static void *frag_buffer = NULL;
00940 
00941    eq_info = &equipment[index].info;
00942 
00943    /* check for fragmented event */
00944    if (eq_info->eq_type & EQ_FRAGMENTED) {
00945       if (frag_buffer == NULL)
00946          frag_buffer = malloc(max_event_size_frag);
00947 
00948       if (frag_buffer == NULL) {
00949          cm_msg(MERROR, "send_event",
00950                 "Not enough memory to allocate buffer for fragmented events");
00951          return SS_NO_MEMORY;
00952       }
00953 
00954       pevent = frag_buffer;
00955    } else {
00956       /* return value should be valid pointer. if NULL BIG error ==> abort  */
00957       pevent = dm_pointer_get();
00958       if (pevent == NULL) {
00959          cm_msg(MERROR, "send_event", "dm_pointer_get not returning valid pointer");
00960          return SS_NO_MEMORY;
00961       }
00962    }
00963 
00964    /* compose MIDAS event header */
00965    pevent->event_id = eq_info->event_id;
00966    pevent->trigger_mask = eq_info->trigger_mask;
00967    pevent->data_size = 0;
00968    pevent->time_stamp = ss_time();
00969    pevent->serial_number = equipment[index].serial_number++;
00970 
00971    equipment[index].last_called = ss_millitime();
00972 
00973    /* call user readout routine */
00974    *((EQUIPMENT **) (pevent + 1)) = &equipment[index];
00975    pevent->data_size = equipment[index].readout((char *) (pevent + 1), 0);
00976 
00977    /* send event */
00978    if (pevent->data_size) {
00979       if (eq_info->eq_type & EQ_FRAGMENTED) {
00980          /* fragment event */
00981          if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size_frag) {
00982             cm_msg(MERROR, "send_event",
00983                    "Event size %ld larger than maximum size %d for frag. ev.",
00984                    (long) (pevent->data_size + sizeof(EVENT_HEADER)),
00985                    max_event_size_frag);
00986             return SS_NO_MEMORY;
00987          }
00988 
00989          /* compose fragments */
00990          pfragment = dm_pointer_get();
00991          if (pfragment == NULL) {
00992             cm_msg(MERROR, "send_event", "dm_pointer_get not returning valid pointer");
00993             return SS_NO_MEMORY;
00994          }
00995 
00996          /* compose MIDAS event header */
00997          memcpy(pfragment, pevent, sizeof(EVENT_HEADER));
00998          pfragment->event_id |= EVENTID_FRAG1;
00999 
01000          /* store total event size */
01001          pd = (char *) (pfragment + 1);
01002          size = pevent->data_size;
01003          for (i = 0; i < 4; i++) {
01004             pd[i] = (unsigned char) (size & 0xFF);      /* little endian, please! */
01005             size >>= 8;
01006          }
01007 
01008          pfragment->data_size = sizeof(DWORD);
01009 
01010          pdata = (char *) (pevent + 1);
01011 
01012          for (i = 0, sent = 0; sent < pevent->data_size; i++) {
01013             if (i > 0) {
01014                pfragment = dm_pointer_get();
01015 
01016                /* compose MIDAS event header */
01017                memcpy(pfragment, pevent, sizeof(EVENT_HEADER));
01018                pfragment->event_id |= EVENTID_FRAG;
01019 
01020                /* copy portion of event */
01021                size = pevent->data_size - sent;
01022                if (size > max_event_size - sizeof(EVENT_HEADER))
01023                   size = max_event_size - sizeof(EVENT_HEADER);
01024 
01025                memcpy(pfragment + 1, pdata, size);
01026                pfragment->data_size = size;
01027                sent += size;
01028                pdata += size;
01029             }
01030 
01031             /* send event to buffer */
01032             if (equipment[index].buffer_handle) {
01033 #ifdef USE_EVENT_CHANNEL
01034                dm_pointer_increment(equipment[index].buffer_handle,
01035                                     pfragment->data_size + sizeof(EVENT_HEADER));
01036 #else
01037                rpc_flush_event();
01038                status = bm_send_event(equipment[index].buffer_handle, pfragment,
01039                                       pfragment->data_size + sizeof(EVENT_HEADER), SYNC);
01040                if (status != BM_SUCCESS) {
01041                   cm_msg(MERROR, "send_event", "bm_send_event(SYNC) error %d", status);
01042                   return status;
01043                }
01044 #endif
01045             }
01046          }
01047 
01048          if (equipment[index].buffer_handle) {
01049 #ifndef USE_EVENT_CHANNEL
01050             status = bm_flush_cache(equipment[index].buffer_handle, SYNC);
01051             if (status != BM_SUCCESS) {
01052                cm_msg(MERROR, "send_event", "bm_flush_cache(SYNC) error %d", status);
01053                return status;
01054             }
01055 #endif
01056          }
01057       } else {
01058          /* send unfragmented event */
01059 
01060          if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size) {
01061             cm_msg(MERROR, "send_event", "Event size %ld larger than maximum size %d",
01062                    (long) (pevent->data_size + sizeof(EVENT_HEADER)), max_event_size);
01063             return SS_NO_MEMORY;
01064          }
01065 
01066          /* send event to buffer */
01067          if (equipment[index].buffer_handle) {
01068 #ifdef USE_EVENT_CHANNEL
01069             dm_pointer_increment(equipment[index].buffer_handle,
01070                                  pevent->data_size + sizeof(EVENT_HEADER));
01071 #else
01072             rpc_flush_event();
01073             status = bm_send_event(equipment[index].buffer_handle, pevent,
01074                                    pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01075             if (status != BM_SUCCESS) {
01076                cm_msg(MERROR, "send_event", "bm_send_event(SYNC) error %d", status);
01077                return status;
01078             }
01079             status = bm_flush_cache(equipment[index].buffer_handle, SYNC);
01080             if (status != BM_SUCCESS) {
01081                cm_msg(MERROR, "send_event", "bm_flush_cache(SYNC) error %d", status);
01082                return status;
01083             }
01084 #endif
01085          }
01086 
01087          /* send event to ODB if RO_ODB flag is set or history is on. Do not
01088             send SLOW events since the class driver does that */
01089          if ((eq_info->read_on & RO_ODB) ||
01090              (eq_info->history > 0 && (eq_info->eq_type & ~EQ_SLOW))) {
01091             update_odb(pevent, equipment[index].hkey_variables, equipment[index].format);
01092             equipment[index].odb_out++;
01093          }
01094       }
01095 
01096       equipment[index].bytes_sent += pevent->data_size + sizeof(EVENT_HEADER);
01097       equipment[index].events_sent++;
01098 
01099       equipment[index].stats.events_sent += equipment[index].events_sent;
01100       equipment[index].events_sent = 0;
01101    } else
01102       equipment[index].serial_number--;
01103 
01104    /* emtpy event buffer */
01105 #ifdef USE_EVENT_CHANNEL
01106    if ((status = dm_area_flush()) != CM_SUCCESS)
01107       cm_msg(MERROR, "send_event", "dm_area_flush: %i", status);
01108 #endif
01109 
01110    for (i = 0; equipment[i].name[0]; i++)
01111       if (equipment[i].buffer_handle) {
01112          status = bm_flush_cache(equipment[i].buffer_handle, SYNC);
01113          if (status != BM_SUCCESS) {
01114             cm_msg(MERROR, "send_event", "bm_flush_cache(SYNC) error %d", status);
01115             return status;
01116          }
01117       }
01118 
01119    return CM_SUCCESS;
01120 }
01121 
01122 /*------------------------------------------------------------------*/
01123 
01124 void send_all_periodic_events(INT transition)
01125 {
01126    EQUIPMENT_INFO *eq_info;
01127    INT i;
01128 
01129    for (i = 0; equipment[i].name[0]; i++) {
01130       eq_info = &equipment[i].info;
01131 
01132       if (!eq_info->enabled || equipment[i].status != FE_SUCCESS)
01133          continue;
01134 
01135       if (transition == TR_START && (eq_info->read_on & RO_BOR) == 0)
01136          continue;
01137       if (transition == TR_STOP && (eq_info->read_on & RO_EOR) == 0)
01138          continue;
01139       if (transition == TR_PAUSE && (eq_info->read_on & RO_PAUSE) == 0)
01140          continue;
01141       if (transition == TR_RESUME && (eq_info->read_on & RO_RESUME) == 0)
01142          continue;
01143 
01144       send_event(i);
01145    }
01146 }
01147 
01148 /*------------------------------------------------------------------*/
01149 
01150 BOOL interrupt_enabled;
01151 
01152 void interrupt_enable(BOOL flag)
01153 {
01154    interrupt_enabled = flag;
01155 
01156    if (interrupt_eq) {
01157       if (interrupt_enabled)
01158          interrupt_configure(CMD_INTERRUPT_ENABLE, 0, 0);
01159       else
01160          interrupt_configure(CMD_INTERRUPT_DISABLE, 0, 0);
01161    }
01162 }
01163 
01164 /*------------------------------------------------------------------*/
01165 
01166 void interrupt_routine(void)
01167 {
01168    EVENT_HEADER *pevent;
01169 
01170    /* get pointer for upcoming event.
01171       This is a blocking call if no space available */
01172    if ((pevent = dm_pointer_get()) == NULL)
01173       cm_msg(MERROR, "interrupt_routine", "interrupt, dm_pointer_get returned NULL");
01174 
01175    /* compose MIDAS event header */
01176    pevent->event_id = interrupt_eq->info.event_id;
01177    pevent->trigger_mask = interrupt_eq->info.trigger_mask;
01178    pevent->data_size = 0;
01179    pevent->time_stamp = actual_time;
01180    pevent->serial_number = interrupt_eq->serial_number++;
01181 
01182    /* call user readout routine */
01183    pevent->data_size = interrupt_eq->readout((char *) (pevent + 1), 0);
01184 
01185    /* send event */
01186    if (pevent->data_size) {
01187       interrupt_eq->bytes_sent += pevent->data_size + sizeof(EVENT_HEADER);
01188       interrupt_eq->events_sent++;
01189 
01190       if (interrupt_eq->buffer_handle) {
01191 #ifdef USE_EVENT_CHANNEL
01192          dm_pointer_increment(interrupt_eq->buffer_handle,
01193                               pevent->data_size + sizeof(EVENT_HEADER));
01194 #else
01195          rpc_send_event(interrupt_eq->buffer_handle, pevent,
01196                         pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01197 #endif
01198       }
01199 
01200       /* send event to ODB */
01201       if (interrupt_eq->info.read_on & RO_ODB || interrupt_eq->info.history) {
01202          if (actual_millitime - interrupt_eq->last_called > ODB_UPDATE_TIME) {
01203             interrupt_eq->last_called = actual_millitime;
01204             memcpy(interrupt_odb_buffer, pevent,
01205                    pevent->data_size + sizeof(EVENT_HEADER));
01206             interrupt_odb_buffer_valid = TRUE;
01207             interrupt_eq->odb_out++;
01208          }
01209       }
01210    } else
01211       interrupt_eq->serial_number--;
01212 
01213 }
01214 
01215 /*------------------------------------------------------------------*/
01216 
01217 int message_print(const char *msg)
01218 {
01219    char str[160];
01220 
01221    memset(str, ' ', 159);
01222    str[159] = 0;
01223 
01224    if (msg[0] == '[')
01225       msg = strchr(msg, ']') + 2;
01226 
01227    memcpy(str, msg, strlen(msg));
01228    ss_printf(0, 20, str);
01229 
01230    return 0;
01231 }
01232 
01233 /*------------------------------------------------------------------*/
01234 
01235 void display(BOOL bInit)
01236 {
01237    INT i, status;
01238    time_t full_time;
01239    char str[30];
01240 
01241    if (bInit) {
01242       ss_clear_screen();
01243 
01244       if (host_name[0])
01245          strcpy(str, host_name);
01246       else
01247          strcpy(str, "<local>");
01248 
01249       ss_printf(0, 0, "%s connected to %s. Press \"!\" to exit", full_frontend_name, str);
01250       ss_printf(0, 1,
01251                 "================================================================================");
01252       ss_printf(0, 2, "Run status:   %s",
01253                 run_state == STATE_STOPPED ? "Stopped" : run_state ==
01254                 STATE_RUNNING ? "Running" : "Paused");
01255       ss_printf(25, 2, "Run number %d   ", run_number);
01256       ss_printf(0, 3,
01257                 "================================================================================");
01258       ss_printf(0, 4,
01259                 "Equipment     Status     Events     Events/sec Rate[kB/s] ODB->FE    FE->ODB");
01260       ss_printf(0, 5,
01261                 "--------------------------------------------------------------------------------");
01262       for (i = 0; equipment[i].name[0]; i++)
01263          ss_printf(0, i + 6, "%s", equipment[i].name);
01264    }
01265 
01266    /* display time */
01267    time(&full_time);
01268    strcpy(str, ctime(&full_time) + 11);
01269    str[8] = 0;
01270    ss_printf(72, 0, "%s", str);
01271 
01272    for (i = 0; equipment[i].name[0]; i++) {
01273       status = equipment[i].status;
01274 
01275       if ((status == 0 || status == FE_SUCCESS) && equipment[i].info.enabled)
01276          ss_printf(14, i + 6, "OK       ");
01277       else if (!equipment[i].info.enabled)
01278          ss_printf(14, i + 6, "Disabled ");
01279       else if (status == FE_ERR_ODB)
01280          ss_printf(14, i + 6, "ODB Error");
01281       else if (status == FE_ERR_HW)
01282          ss_printf(14, i + 6, "HW Error ");
01283       else if (status == FE_ERR_DISABLED)
01284          ss_printf(14, i + 6, "Disabled ");
01285       else
01286          ss_printf(14, i + 6, "Unknown  ");
01287 
01288       if (equipment[i].stats.events_sent > 1E9)
01289          ss_printf(25, i + 6, "%1.3lfG     ", equipment[i].stats.events_sent / 1E9);
01290       else if (equipment[i].stats.events_sent > 1E6)
01291          ss_printf(25, i + 6, "%1.3lfM     ", equipment[i].stats.events_sent / 1E6);
01292       else
01293          ss_printf(25, i + 6, "%1.0lf      ", equipment[i].stats.events_sent);
01294       ss_printf(36, i + 6, "%1.1lf      ", equipment[i].stats.events_per_sec);
01295       ss_printf(47, i + 6, "%1.1lf      ", equipment[i].stats.kbytes_per_sec);
01296       ss_printf(58, i + 6, "%ld       ", equipment[i].odb_in);
01297       ss_printf(69, i + 6, "%ld       ", equipment[i].odb_out);
01298    }
01299 
01300    /* go to next line */
01301    ss_printf(0, i + 6, "");
01302 }
01303 
01304 /*------------------------------------------------------------------*/
01305 
01306 BOOL logger_root()
01307 /* check if logger uses ROOT format */
01308 {
01309    int size, i, status;
01310    char str[80];
01311    HNDLE hKeyRoot, hKey;
01312 
01313    if (db_find_key(hDB, 0, "/Logger/Channels", &hKeyRoot) == DB_SUCCESS) {
01314       for (i = 0;; i++) {
01315          status = db_enum_key(hDB, hKeyRoot, i, &hKey);
01316          if (status == DB_NO_MORE_SUBKEYS)
01317             break;
01318 
01319          strcpy(str, "MIDAS");
01320          size = sizeof(str);
01321          db_get_value(hDB, hKey, "Settings/Format", str, &size, TID_STRING, TRUE);
01322 
01323          if (equal_ustring(str, "ROOT"))
01324             return TRUE;
01325       }
01326    }
01327 
01328    return FALSE;
01329 }
01330 
01331 /*------------------------------------------------------------------*/
01332 
01333 INT scheduler(void)
01334 {
01335    EQUIPMENT_INFO *eq_info;
01336    EQUIPMENT *eq;
01337    EVENT_HEADER *pevent;
01338    DWORD last_time_network = 0, last_time_display = 0, last_time_flush = 0, readout_start;
01339    INT i, j, index, status, ch, source, size, state;
01340    char str[80];
01341    BOOL buffer_done, flag, force_update = FALSE;
01342 
01343    INT opt_max = 0, opt_index = 0, opt_tcp_size = 128, opt_cnt = 0;
01344    INT err;
01345 
01346 #ifdef OS_VXWORKS
01347    rpc_set_opt_tcp_size(1024);
01348 #ifdef PPCxxx
01349    rpc_set_opt_tcp_size(NET_TCP_SIZE);
01350 #endif
01351 #endif
01352 
01353    /*----------------- MAIN equipment loop ------------------------------*/
01354 
01355    do {
01356       actual_millitime = ss_millitime();
01357       actual_time = ss_time();
01358 
01359       /*---- loop over equipment table -------------------------------*/
01360       for (index = 0;; index++) {
01361          eq = &equipment[index];
01362          eq_info = &eq->info;
01363 
01364          /* check if end of equipment list */
01365          if (!eq->name[0])
01366             break;
01367 
01368          if (!eq_info->enabled)
01369             continue;
01370 
01371          if (eq->status != FE_SUCCESS)
01372             continue;
01373 
01374          /*---- call idle routine for slow control equipment ----*/
01375          if ((eq_info->eq_type & EQ_SLOW) && eq->status == FE_SUCCESS) {
01376             if (eq_info->event_limit > 0 && run_state == STATE_RUNNING) {
01377                if (actual_time - eq->last_idle >= (DWORD) eq_info->event_limit) {
01378                   eq->cd(CMD_IDLE, eq);
01379                   eq->last_idle = actual_time;
01380                }
01381             } else
01382                eq->cd(CMD_IDLE, eq);
01383          }
01384 
01385          if (run_state == STATE_STOPPED && (eq_info->read_on & RO_STOPPED) == 0)
01386             continue;
01387          if (run_state == STATE_PAUSED && (eq_info->read_on & RO_PAUSED) == 0)
01388             continue;
01389          if (run_state == STATE_RUNNING && (eq_info->read_on & RO_RUNNING) == 0)
01390             continue;
01391 
01392          /*---- check periodic events ----*/
01393          if ((eq_info->eq_type & EQ_PERIODIC) || (eq_info->eq_type & EQ_SLOW)) {
01394             if (eq_info->period == 0)
01395                continue;
01396 
01397             /* check if period over */
01398             if (actual_millitime - eq->last_called >= (DWORD) eq_info->period) {
01399                /* disable interrupts during readout */
01400                interrupt_enable(FALSE);
01401 
01402                /* readout and send event */
01403                status = send_event(index);
01404 
01405                if (status != CM_SUCCESS) {
01406                   cm_msg(MERROR, "scheduler", "send_event error %d", status);
01407                   goto net_error;
01408                }
01409 
01410                /* re-enable the interrupt after periodic */
01411                interrupt_enable(TRUE);
01412             }
01413          }
01414 
01415          /* end of periodic equipments */
01416          /*---- check polled events ----*/
01417          if (eq_info->eq_type & EQ_POLLED) {
01418             readout_start = actual_millitime;
01419             pevent = NULL;
01420 
01421             while ((source = poll_event(eq_info->source, eq->poll_count, FALSE)) > 0) {
01422                pevent = dm_pointer_get();
01423                if (pevent == NULL) {
01424                   cm_msg(MERROR, "scheduler",
01425                          "polled, dm_pointer_get not returning valid pointer");
01426                   status = SS_NO_MEMORY;
01427                   goto net_error;
01428                }
01429 
01430                /* compose MIDAS event header */
01431                pevent->event_id = eq_info->event_id;
01432                pevent->trigger_mask = eq_info->trigger_mask;
01433                pevent->data_size = 0;
01434                pevent->time_stamp = actual_time;
01435                pevent->serial_number = eq->serial_number;
01436 
01437                /* put source at beginning of event, will be overwritten by 
01438                   user readout code, just a special feature used by some 
01439                   multi-source applications */
01440                *(INT *) (pevent + 1) = source;
01441 
01442                if (eq->info.num_subevents) {
01443                   eq->subevent_number = 0;
01444                   do {
01445                      *(INT *) ((char *) (pevent + 1) + pevent->data_size) = source;
01446 
01447                      /* call user readout routine for subevent indicating offset */
01448                      size = eq->readout((char *) (pevent + 1), pevent->data_size);
01449                      pevent->data_size += size;
01450                      if (size > 0) {
01451                         if (pevent->data_size + sizeof(EVENT_HEADER) >
01452                             (DWORD) max_event_size) {
01453                            cm_msg(MERROR, "scheduler",
01454                                   "Event size %ld larger than maximum size %d",
01455                                   (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01456                                   max_event_size);
01457                         }
01458 
01459                         eq->subevent_number++;
01460                         eq->serial_number++;
01461                      }
01462 
01463                      /* wait for next event */
01464                      do {
01465                         source = poll_event(eq_info->source, eq->poll_count, FALSE);
01466 
01467                         if (source == FALSE) {
01468                            actual_millitime = ss_millitime();
01469 
01470                            /* repeat no more than period */
01471                            if (actual_millitime - readout_start > (DWORD) eq_info->period)
01472                               break;
01473                         }
01474                      } while (source == FALSE);
01475 
01476                   } while (eq->subevent_number < eq->info.num_subevents && source);
01477 
01478                   /* notify readout routine about end of super-event */
01479                   pevent->data_size = eq->readout((char *) (pevent + 1), -1);
01480                } else {
01481                   /* call user readout routine indicating event source */
01482                   pevent->data_size = eq->readout((char *) (pevent + 1), 0);
01483 
01484                   /* check event size */
01485                   if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size) {
01486                      cm_msg(MERROR, "scheduler",
01487                             "Event size %ld larger than maximum size %d",
01488                             (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01489                             max_event_size);
01490                   }
01491 
01492                   /* increment serial number if event read out sucessfully */
01493                   if (pevent->data_size)
01494                      eq->serial_number++;
01495                }
01496 
01497                /* send event */
01498                if (pevent->data_size) {
01499                   if (eq->buffer_handle) {
01500                      /* send first event to ODB if logger writes in root format */
01501                      if (pevent->serial_number == 1)
01502                         if (logger_root())
01503                            update_odb(pevent, eq->hkey_variables, eq->format);
01504 
01505 #ifdef USE_EVENT_CHANNEL
01506                      dm_pointer_increment(eq->buffer_handle,
01507                                           pevent->data_size + sizeof(EVENT_HEADER));
01508 #else
01509                      status = rpc_send_event(eq->buffer_handle, pevent,
01510                                              pevent->data_size + sizeof(EVENT_HEADER),
01511                                              SYNC);
01512 
01513                      if (status != SUCCESS) {
01514                         cm_msg(MERROR, "scheduler", "rpc_send_event error %d", status);
01515                         goto net_error;
01516                      }
01517 #endif
01518 
01519                      eq->bytes_sent += pevent->data_size + sizeof(EVENT_HEADER);
01520 
01521                      if (eq->info.num_subevents)
01522                         eq->events_sent += eq->subevent_number;
01523                      else
01524                         eq->events_sent++;
01525                   }
01526                }
01527 
01528                actual_millitime = ss_millitime();
01529 
01530                /* repeat no more than period */
01531                if (actual_millitime - readout_start > (DWORD) eq_info->period)
01532                   break;
01533 
01534                /* quit if event limit is reached */
01535                if (eq_info->event_limit > 0 &&
01536                    eq->stats.events_sent + eq->events_sent >= eq_info->event_limit)
01537                   break;
01538             }
01539 
01540             /* send event to ODB */
01541             if (pevent && (eq_info->read_on & RO_ODB || eq_info->history)) {
01542                if (actual_millitime - eq->last_called > ODB_UPDATE_TIME && pevent != NULL) {
01543                   eq->last_called = actual_millitime;
01544                   update_odb(pevent, eq->hkey_variables, eq->format);
01545                   eq->odb_out++;
01546                }
01547             }
01548          }
01549 
01550          /*---- send interrupt events ----*/
01551          if (eq_info->eq_type & EQ_INTERRUPT) {
01552             /* not much to do as work being done independently in interrupt_routine() */
01553 
01554             /* update ODB */
01555             if (interrupt_odb_buffer_valid) {
01556                update_odb(interrupt_odb_buffer, interrupt_eq->hkey_variables,
01557                           interrupt_eq->format);
01558                interrupt_odb_buffer_valid = FALSE;
01559             }
01560 
01561          }
01562 
01563          /*---- check if event limit is reached ----*/
01564          if (eq_info->eq_type != EQ_SLOW &&
01565              eq_info->event_limit > 0 &&
01566              eq->stats.events_sent + eq->events_sent >= eq_info->event_limit &&
01567              run_state == STATE_RUNNING) {
01568             /* stop run */
01569             if (cm_transition(TR_STOP, 0, str, sizeof(str), SYNC, FALSE) != CM_SUCCESS)
01570                cm_msg(MERROR, "scheduler", "cannot stop run: %s", str);
01571 
01572             /* check if autorestart, main loop will take care of it */
01573             size = sizeof(BOOL);
01574             flag = FALSE;
01575             db_get_value(hDB, 0, "/Logger/Auto restart", &flag, &size, TID_BOOL, TRUE);
01576 
01577             if (flag)
01578                auto_restart = ss_time() + 20;   /* restart in 20 sec. */
01579 
01580             /* update event display correctly */
01581             force_update = TRUE;
01582          }
01583       }
01584 
01585       /*---- call frontend_loop periodically -------------------------*/
01586       if (frontend_call_loop) {
01587          status = frontend_loop();
01588          if (status != CM_SUCCESS)
01589             status = RPC_SHUTDOWN;
01590       }
01591 
01592       /*---- check for deferred transitions --------------------------*/
01593       cm_check_deferred_transition();
01594 
01595       /*---- check for manual triggered events -----------------------*/
01596       if (manual_trigger_event_id) {
01597          interrupt_enable(FALSE);
01598 
01599          /* readout and send event */
01600          status = BM_INVALID_PARAM; 
01601          for (i = 0; equipment[i].name[0]; i++)
01602             if (equipment[i].info.event_id == manual_trigger_event_id) {
01603                status = send_event(i);
01604                break;
01605             }
01606 
01607          manual_trigger_event_id = 0;
01608 
01609          if (status != CM_SUCCESS) {
01610             cm_msg(MERROR, "scheduler", "send_event error %d", status);
01611             goto net_error;
01612          }
01613 
01614          /* re-enable the interrupt after periodic */
01615          interrupt_enable(TRUE);
01616       }
01617 
01618       /*---- calculate rates and update status page periodically -----*/
01619       if (force_update ||
01620           (display_period
01621            && actual_millitime - last_time_display > (DWORD) display_period)
01622           || (!display_period && actual_millitime - last_time_display > 3000)) {
01623          force_update = FALSE;
01624 
01625          /* calculate rates */
01626          if (actual_millitime != last_time_display) {
01627             max_bytes_per_sec = 0;
01628             for (i = 0; equipment[i].name[0]; i++) {
01629                eq = &equipment[i];
01630                eq->stats.events_sent += eq->events_sent;
01631                eq->stats.events_per_sec =
01632                    eq->events_sent / ((actual_millitime - last_time_display) / 1000.0);
01633                eq->stats.kbytes_per_sec =
01634                    eq->bytes_sent / 1024.0 / ((actual_millitime - last_time_display) /
01635                                               1000.0);
01636 
01637                if ((INT) eq->bytes_sent > max_bytes_per_sec)
01638                   max_bytes_per_sec = eq->bytes_sent;
01639 
01640                eq->bytes_sent = 0;
01641                eq->events_sent = 0;
01642             }
01643 
01644             max_bytes_per_sec = (DWORD)
01645                 ((double) max_bytes_per_sec /
01646                  ((actual_millitime - last_time_display) / 1000.0));
01647 
01648             /* tcp buffer size evaluation */
01649             if (optimize) {
01650                opt_max = MAX(opt_max, (INT) max_bytes_per_sec);
01651                ss_printf(0, opt_index, "%6d : %5.1lf %5.1lf", opt_tcp_size,
01652                          opt_max / 1024.0, max_bytes_per_sec / 1024.0);
01653                if (++opt_cnt == 10) {
01654                   opt_cnt = 0;
01655                   opt_max = 0;
01656                   opt_index++;
01657                   opt_tcp_size = 1 << (opt_index + 7);
01658                   rpc_set_opt_tcp_size(opt_tcp_size);
01659                   if (1 << (opt_index + 7) > 0x8000) {
01660                      opt_index = 0;
01661                      opt_tcp_size = 1 << 7;
01662                      rpc_set_opt_tcp_size(opt_tcp_size);
01663                   }
01664                }
01665             }
01666 
01667          }
01668 
01669          /* propagate changes in equipment to ODB */
01670          rpc_set_option(-1, RPC_OTRANSPORT, RPC_FTCP);
01671          db_send_changed_records();
01672          rpc_set_option(-1, RPC_OTRANSPORT, RPC_TCP);
01673 
01674          if (display_period) {
01675             display(FALSE);
01676 
01677             /* check keyboard */
01678             ch = 0;
01679             status = 0;
01680             while (ss_kbhit()) {
01681                ch = ss_getchar(0);
01682                if (ch == -1)
01683                   ch = getchar();
01684 
01685                if (ch == '!')
01686                   status = RPC_SHUTDOWN;
01687             }
01688 
01689             if (ch > 0)
01690                display(TRUE);
01691             if (status == RPC_SHUTDOWN)
01692                break;
01693          }
01694 
01695          last_time_display = actual_millitime;
01696       }
01697 
01698       /*---- check to flush cache ------------------------------------*/
01699       if (actual_millitime - last_time_flush > 1000) {
01700          last_time_flush = actual_millitime;
01701 
01702          /* if cache on server is not filled in one second at current
01703             data rate, flush it now to make events available to consumers */
01704 
01705          if (max_bytes_per_sec < SERVER_CACHE_SIZE) {
01706             interrupt_enable(FALSE);
01707 
01708 #ifdef USE_EVENT_CHANNEL
01709             if ((status = dm_area_flush()) != CM_SUCCESS)
01710                cm_msg(MERROR, "scheduler", "dm_area_flush: %i", status);
01711 #endif
01712 
01713             for (i = 0; equipment[i].name[0]; i++) {
01714                if (equipment[i].buffer_handle) {
01715                   /* check if buffer already flushed */
01716                   buffer_done = FALSE;
01717                   for (j = 0; j < i; j++)
01718                      if (equipment[i].buffer_handle == equipment[j].buffer_handle) {
01719                         buffer_done = TRUE;
01720                         break;
01721                      }
01722 
01723                   if (!buffer_done) {
01724                      rpc_set_option(-1, RPC_OTRANSPORT, RPC_FTCP);
01725                      rpc_flush_event();
01726                      err = bm_flush_cache(equipment[i].buffer_handle, ASYNC);
01727                      if (err != BM_SUCCESS) {
01728                         cm_msg(MERROR, "scheduler", "bm_flush_cache(ASYNC) error %d",
01729                                err);
01730                         return err;
01731                      }
01732                      rpc_set_option(-1, RPC_OTRANSPORT, RPC_TCP);
01733                   }
01734                }
01735             }
01736             interrupt_enable(TRUE);
01737          }
01738       }
01739 
01740       /*---- check for auto restart --------------------------------*/
01741       if (auto_restart > 0 && ss_time() > auto_restart) {
01742          /* check if really stopped */
01743          size = sizeof(state);
01744          status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
01745          if (status != DB_SUCCESS)
01746             cm_msg(MERROR, "scheduler", "cannot get Runinfo/State in database");
01747 
01748          if (state == STATE_STOPPED) {
01749             auto_restart = 0;
01750             size = sizeof(run_number);
01751             status =
01752                 db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT,
01753                              TRUE);
01754             assert(status == SUCCESS);
01755 
01756             if (run_number <= 0) {
01757                cm_msg(MERROR, "main", "aborting on attempt to use invalid run number %d",
01758                       run_number);
01759                abort();
01760             }
01761 
01762             cm_msg(MTALK, "main", "starting new run");
01763             status = cm_transition(TR_START, run_number + 1, NULL, 0, SYNC, FALSE);
01764             if (status != CM_SUCCESS)
01765                cm_msg(MERROR, "main", "cannot restart run");
01766          }
01767       }
01768 
01769       /*---- check network messages ----------------------------------*/
01770       if (run_state == STATE_RUNNING && interrupt_eq == NULL) {
01771          /* only call yield once every 100ms when running */
01772          if (actual_millitime - last_time_network > 100) {
01773             status = cm_yield(0);
01774             last_time_network = actual_millitime;
01775          } else
01776             status = RPC_SUCCESS;
01777       } else
01778          /* when run is stopped or interrupts used, 
01779             call yield with 100ms timeout */
01780          status = cm_yield(100);
01781 
01782       /* exit for VxWorks */
01783       if (fe_stop)
01784          status = RPC_SHUTDOWN;
01785 
01786    } while (status != RPC_SHUTDOWN && status != SS_ABORT);
01787 
01788  net_error:
01789 
01790    return status;
01791 }
01792 
01793 /*------------------------------------------------------------------*/
01794 
01795 #ifdef HAVE_CAMAC
01796 
01797 INT cnaf_callback(INT index, void *prpc_param[])
01798 {
01799    DWORD cmd, b, c, n, a, f, *pdword, *size, *x, *q, dtemp;
01800    WORD *pword, *pdata, temp;
01801    INT i, count;
01802 
01803    /* Decode parameters */
01804    cmd = CDWORD(0);
01805    b = CDWORD(1);
01806    c = CDWORD(2);
01807    n = CDWORD(3);
01808    a = CDWORD(4);
01809    f = CDWORD(5);
01810    pdword = CPDWORD(6);
01811    pword = CPWORD(6);
01812    pdata = CPWORD(6);
01813    size = CPDWORD(7);
01814    x = CPDWORD(8);
01815    q = CPDWORD(9);
01816 
01817    /* determine repeat count */
01818    if (index == RPC_CNAF16)
01819       count = *size / sizeof(WORD);     /* 16 bit */
01820    else
01821       count = *size / sizeof(DWORD);    /* 24 bit */
01822 
01823    switch (cmd) {
01824     /*---- special commands ----*/
01825 
01826    case CNAF_INHIBIT_SET:
01827       cam_inhibit_set(c);
01828       break;
01829    case CNAF_INHIBIT_CLEAR:
01830       cam_inhibit_clear(c);
01831       break;
01832    case CNAF_CRATE_CLEAR:
01833       cam_crate_clear(c);
01834       break;
01835    case CNAF_CRATE_ZINIT:
01836       cam_crate_zinit(c);
01837       break;
01838 
01839    case CNAF_TEST:
01840       break;
01841 
01842    case CNAF:
01843       if (index == RPC_CNAF16) {
01844          for (i = 0; i < count; i++)
01845             if (f < 16)
01846                cam16i_q(c, n, a, f, pword++, (int *) x, (int *) q);
01847             else if (f < 24)
01848                cam16o_q(c, n, a, f, pword[i], (int *) x, (int *) q);
01849             else
01850                cam16i_q(c, n, a, f, &temp, (int *) x, (int *) q);
01851       } else {
01852          for (i = 0; i < count; i++)
01853             if (f < 16)
01854                cam24i_q(c, n, a, f, pdword++, (int *) x, (int *) q);
01855             else if (f < 24)
01856                cam24o_q(c, n, a, f, pdword[i], (int *) x, (int *) q);
01857             else
01858                cam24i_q(c, n, a, f, &dtemp, (int *) x, (int *) q);
01859       }
01860 
01861       break;
01862 
01863    case CNAF_nQ:
01864       if (index == RPC_CNAF16) {
01865          if (f < 16)
01866             cam16i_rq(c, n, a, f, (WORD **) & pdword, count);
01867       } else {
01868          if (f < 16)
01869             cam24i_rq(c, n, a, f, &pdword, count);
01870       }
01871 
01872       /* return reduced return size */
01873       *size = (int) pdword - (int) pdata;
01874       break;
01875 
01876    default:
01877       printf("cnaf: Unknown command 0x%X\n", (unsigned int) cmd);
01878    }
01879 
01880    if (debug) {
01881       if (index == RPC_CNAF16)
01882          printf("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n",
01883                 (int) cmd, (int) count, (int) c, (int) n, (int) a, (int) f,
01884                 (int) pword[0], (int) *x, (int) *q);
01885       else if (index == RPC_CNAF24)
01886          printf("cmd=%d r=%d c=%d n=%d a=%d f=%d d=%X x=%d q=%d\n",
01887                 (int) cmd, (int) count, (int) c, (int) n, (int) a, (int) f,
01888                 (int) pdword[0], (int) *x, (int) *q);
01889    }
01890 
01891    return RPC_SUCCESS;
01892 }
01893 
01894 #endif                          /* HAVE_CAMAC */
01895 
01896 
01897 /*------------------------------------------------------------------*/
01898 
01899 INT get_frontend_index()
01900 {
01901    return frontend_index;
01902 }
01903 
01904 /*------------------------------------------------------------------*/
01905 
01906 #ifdef OS_VXWORKS
01907 int mfe(char *ahost_name, char *aexp_name, BOOL adebug)
01908 #else
01909 int main(int argc, char *argv[])
01910 #endif
01911 {
01912    INT status, i, dm_size;
01913    INT daemon;
01914 
01915    host_name[0] = 0;
01916    exp_name[0] = 0;
01917    debug = FALSE;
01918    daemon = 0;
01919 
01920    setbuf(stdout, 0);
01921    setbuf(stderr, 0);
01922 
01923 #ifdef SIGPIPE
01924    signal(SIGPIPE, SIG_IGN);
01925 #endif
01926 
01927 #ifdef OS_VXWORKS
01928    if (ahost_name)
01929       strcpy(host_name, ahost_name);
01930    if (aexp_name)
01931       strcpy(exp_name, aexp_name);
01932    debug = adebug;
01933 #else
01934 
01935    /* get default from environment */
01936    cm_get_environment(host_name, sizeof(host_name), exp_name, sizeof(exp_name));
01937 
01938    /* parse command line parameters */
01939    for (i = 1; i < argc; i++) {
01940       if (argv[i][0] == '-' && argv[i][1] == 'd')
01941          debug = TRUE;
01942       else if (argv[i][0] == '-' && argv[i][1] == 'D')
01943          daemon = 1;
01944       else if (argv[i][0] == '-' && argv[i][1] == 'O')
01945          daemon = 2;
01946       else if (argv[i][0] == '-') {
01947          if (i + 1 >= argc || argv[i + 1][0] == '-')
01948             goto usage;
01949          if (argv[i][1] == 'e')
01950             strcpy(exp_name, argv[++i]);
01951          else if (argv[i][1] == 'h')
01952             strcpy(host_name, argv[++i]);
01953          else if (argv[i][1] == 'i')
01954             frontend_index = atoi(argv[++i]);
01955          else {
01956           usage:
01957             printf("usage: frontend [-h Hostname] [-e Experiment] [-d] [-D] [-O] [-i n]\n");
01958             printf("         [-d]     Used to debug the frontend\n");
01959             printf("         [-D]     Become a daemon\n");
01960             printf("         [-O]     Become a daemon but keep stdout\n");
01961             printf("         [-i n]   Set frontend index (used for event building)\n");
01962             return 0;
01963          }
01964       }
01965    }
01966 #endif
01967 
01968    /* check event and buffer sizes */
01969    if (event_buffer_size < 2 * max_event_size) {
01970       printf("event_buffer_size too small for max. event size\n");
01971       ss_sleep(5000);
01972       return 1;
01973    }
01974 
01975    if (max_event_size > MAX_EVENT_SIZE) {
01976       printf("Requested max_event_size (%d) exceeds max. system event size (%d)",
01977              max_event_size, MAX_EVENT_SIZE);
01978       ss_sleep(5000);
01979       return 1;
01980    }
01981 
01982    dm_size = event_buffer_size;
01983 
01984 #ifdef OS_VXWORKS
01985    /* override dm_size in case of VxWorks
01986       take remaining free memory and use 20% of it for dm_ */
01987    dm_size = 2 * 10 * (max_event_size + sizeof(EVENT_HEADER) + sizeof(INT));
01988    if (dm_size > memFindMax()) {
01989       cm_msg(MERROR, "mainFE", "Not enough mem space for event size");
01990       return 0;
01991    }
01992    /* takes overall 20% of the available memory resource for dm_() */
01993    dm_size = 0.2 * memFindMax();
01994 
01995    /* there are two buffers */
01996    dm_size /= 2;
01997 #endif
01998 
01999    /* reduce memory size for MS-DOS */
02000 #ifdef OS_MSDOS
02001    if (dm_size > 0x4000)
02002       dm_size = 0x4000;         /* 16k */
02003 #endif
02004 
02005    /* add frontend index to frontend name if present */
02006    strcpy(full_frontend_name, frontend_name);
02007    if (frontend_index >= 0)
02008       sprintf(full_frontend_name+strlen(full_frontend_name), "%02d", frontend_index);
02009 
02010    /* inform user of settings */
02011    printf("Frontend name          :     %s\n", full_frontend_name);
02012    printf("Event buffer size      :     %d\n", event_buffer_size);
02013    printf("Buffer allocation      : 2 x %d\n", dm_size);
02014    printf("System max event size  :     %d\n", MAX_EVENT_SIZE);
02015    printf("User max event size    :     %d\n", max_event_size);
02016    if (max_event_size_frag > 0)
02017       printf("User max frag. size    :     %d\n", max_event_size_frag);
02018    printf("# of events per buffer :     %d\n\n", dm_size / max_event_size);
02019 
02020    if (daemon) {
02021       printf("\nBecoming a daemon...\n");
02022       ss_daemon_init(daemon == 2);
02023    }
02024 
02025    /* now connect to server */
02026    if (display_period) {
02027       if (host_name[0])
02028          printf("Connect to experiment %s on host %s...", exp_name, host_name);
02029       else
02030          printf("Connect to experiment %s...", exp_name);
02031    }
02032 
02033    status = cm_connect_experiment1(host_name, exp_name, full_frontend_name,
02034                                    NULL, DEFAULT_ODB_SIZE, DEFAULT_FE_TIMEOUT);
02035    if (status != CM_SUCCESS) {
02036       /* let user read message before window might close */
02037       ss_sleep(5000);
02038       return 1;
02039    }
02040 
02041    if (display_period)
02042       printf("OK\n");
02043 
02044    /* book buffer space */
02045    status = dm_buffer_create(dm_size, max_event_size);
02046    if (status != CM_SUCCESS) {
02047       printf("dm_buffer_create: Not enough memory or event too big\n");
02048       return 1;
02049    }
02050 
02051    /* remomve any dead frontend */
02052    cm_cleanup(full_frontend_name, FALSE);
02053 
02054    /* shutdown previous frontend */
02055    status = cm_shutdown(full_frontend_name, FALSE);
02056    if (status == CM_SUCCESS && display_period) {
02057       printf("Previous frontend stopped\n");
02058 
02059       /* let user read message */
02060       ss_sleep(3000);
02061    }
02062 
02063    /* register transition callbacks */
02064    if (cm_register_transition(TR_START, tr_start, 500) != CM_SUCCESS ||
02065        cm_register_transition(TR_STOP, tr_stop, 500) != CM_SUCCESS ||
02066        cm_register_transition(TR_PAUSE, tr_pause, 500) != CM_SUCCESS ||
02067        cm_register_transition(TR_RESUME, tr_resume, 500) != CM_SUCCESS) {
02068       printf("Failed to start local RPC server");
02069       cm_disconnect_experiment();
02070       dm_buffer_release();
02071 
02072       /* let user read message before window might close */
02073       ss_sleep(5000);
02074       return 1;
02075    }
02076 #ifdef HAVE_CAMAC
02077 
02078    /* register CNAF callback */
02079    cm_register_function(RPC_CNAF16, cnaf_callback);
02080    cm_register_function(RPC_CNAF24, cnaf_callback);
02081 
02082 #endif
02083 
02084    cm_get_experiment_database(&hDB, &status);
02085 
02086    /* set time from server */
02087 #ifdef OS_VXWORKS
02088    cm_synchronize(NULL);
02089 #endif
02090 
02091    /* turn off watchdog if in debug mode */
02092    if (debug)
02093       cm_set_watchdog_params(TRUE, 0);
02094 
02095    /* increase RPC timeout to 2min for logger with exabyte or blocked disk */
02096    rpc_set_option(-1, RPC_OTIMEOUT, 120000);
02097 
02098    /* set own message print function */
02099    if (display_period)
02100       cm_set_msg_print(MT_ALL, MT_ALL, message_print);
02101 
02102    /* call user init function */
02103    if (display_period)
02104       printf("Init hardware...");
02105    if (frontend_init() != SUCCESS) {
02106       if (display_period)
02107          printf("\n");
02108       cm_disconnect_experiment();
02109       dm_buffer_release();
02110 
02111       /* let user read message before window might close */
02112       ss_sleep(5000);
02113       return 1;
02114    }
02115 
02116    /* reqister equipment in ODB */
02117    if (register_equipment() != SUCCESS) {
02118       if (display_period)
02119          printf("\n");
02120       cm_disconnect_experiment();
02121       dm_buffer_release();
02122 
02123       /* let user read message before window might close */
02124       ss_sleep(5000);
02125       return 1;
02126    }
02127 
02128    if (display_period)
02129       printf("OK\n");
02130 
02131    /* initialize screen display */
02132    if (display_period) {
02133       ss_sleep(1000);
02134       display(TRUE);
02135    }
02136 
02137    /* switch on interrupts if running */
02138    if (interrupt_eq && run_state == STATE_RUNNING)
02139       interrupt_enable(TRUE);
02140 
02141    /* initialize ss_getchar */
02142    ss_getchar(0);
02143 
02144    /* call main scheduler loop */
02145    status = scheduler();
02146 
02147    /* reset terminal */
02148    ss_getchar(TRUE);
02149 
02150    /* switch off interrupts */
02151    if (interrupt_eq) {
02152       interrupt_configure(CMD_INTERRUPT_DISABLE, 0, 0);
02153       interrupt_configure(CMD_INTERRUPT_DETACH, 0, 0);
02154       if (interrupt_odb_buffer)
02155          free(interrupt_odb_buffer);
02156    }
02157 
02158    /* detach interrupts */
02159    if (interrupt_eq != NULL)
02160       interrupt_configure(CMD_INTERRUPT_DETACH, interrupt_eq->info.source, 0);
02161 
02162    /* call user exit function */
02163    frontend_exit();
02164 
02165    /* close slow control drivers */
02166    for (i = 0; equipment[i].name[0]; i++)
02167       if ((equipment[i].info.eq_type & EQ_SLOW) && equipment[i].status == FE_SUCCESS)
02168          equipment[i].cd(CMD_EXIT, &equipment[i]);
02169 
02170    /* close network connection to server */
02171    cm_disconnect_experiment();
02172 
02173    if (display_period) {
02174       if (status == RPC_SHUTDOWN) {
02175          ss_clear_screen();
02176          ss_printf(0, 0, "Frontend shut down.");
02177          ss_printf(0, 1, "");
02178       }
02179    }
02180 
02181    if (status != RPC_SHUTDOWN)
02182       printf("Network connection aborted.\n");
02183 
02184    dm_buffer_release();
02185 
02186    return 0;
02187 }

Midas DOC Version 1.9.3 ---- PSI Stefan Ritt ----
Contributions: Pierre-Andre Amaudruz - Suzannah Daviel - Doxygen - Peter Green - Greg Hackman - Gertjan Hofman - Paul Knowles - Rudi Meier - Glenn Moloney - Dave Morris - John M O'Donnell - Konstantin Olchanski - Renee Poutissou - Andreas Suter - Jan M.Wouters - Piotr Adam Zolnierczuk