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

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