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   $Id: mfe.c 4225 2008-06-16 12:43:26Z ritt@PSI.CH $
00010 
00011 \********************************************************************/
00012 
00013 #include <stdio.h>
00014 #include <assert.h>
00015 #include "midas.h"
00016 #include "msystem.h"
00017 #include "mcstd.h"
00018 
00019 #ifdef YBOS_SUPPORT
00020 #include "ybos.h"
00021 #endif
00022 
00023 /*------------------------------------------------------------------*/
00024 
00025 /* items defined in frontend.c */
00026 
00027 extern char *frontend_name;
00028 extern char *frontend_file_name;
00029 extern BOOL frontend_call_loop;
00030 extern INT max_event_size;
00031 extern INT max_event_size_frag;
00032 extern INT event_buffer_size;
00033 extern INT display_period;
00034 extern INT frontend_init(void);
00035 extern INT frontend_exit(void);
00036 extern INT frontend_loop(void);
00037 extern INT begin_of_run(INT run_number, char *error);
00038 extern INT end_of_run(INT run_number, char *error);
00039 extern INT pause_run(INT run_number, char *error);
00040 extern INT resume_run(INT run_number, char *error);
00041 extern INT poll_event(INT source, INT count, BOOL test);
00042 extern INT interrupt_configure(INT cmd, INT source, POINTER_T adr);
00043 
00044 /*------------------------------------------------------------------*/
00045 
00046 /* globals */
00047 
00048 INT rpc_mode = 1; // 0 for RPC socket, 1 for event socket
00049 
00050 #define SERVER_CACHE_SIZE  100000       /* event cache before buffer */
00051 
00052 #define ODB_UPDATE_TIME      1000       /* 1 seconds for ODB update */
00053 
00054 #define DEFAULT_FE_TIMEOUT  60000       /* 60 seconds for watchdog timeout */
00055 
00056 INT run_state;                  /* STATE_RUNNING, STATE_STOPPED, STATE_PAUSED */
00057 INT run_number;
00058 DWORD actual_time;              /* current time in seconds since 1970 */
00059 DWORD actual_millitime;         /* current time in milliseconds */
00060 
00061 char host_name[HOST_NAME_LENGTH];
00062 char exp_name[NAME_LENGTH];
00063 char full_frontend_name[256];
00064 
00065 INT max_bytes_per_sec;
00066 INT optimize = 0;               /* set this to one to opimize TCP buffer size */
00067 INT fe_stop = 0;                /* stop switch for VxWorks */
00068 BOOL debug;                     /* disable watchdog messages from server */
00069 DWORD auto_restart = 0;         /* restart run after event limit reached stop */
00070 INT manual_trigger_event_id = 0;        /* set from the manual_trigger callback */
00071 INT frontend_index = -1;        /* frontend index for event building */
00072 
00073 HNDLE hDB;
00074 
00075 #ifdef YBOS_SUPPORT
00076 struct {
00077    DWORD ybos_type;
00078    DWORD odb_type;
00079    INT tsize;
00080 } id_map[] = {
00081    {
00082    A1_BKTYPE, TID_CHAR, 1}, {
00083    I1_BKTYPE, TID_BYTE, 1}, {
00084    I2_BKTYPE, TID_WORD, 2}, {
00085    I4_BKTYPE, TID_DWORD, 4}, {
00086    F4_BKTYPE, TID_FLOAT, 4}, {
00087    D8_BKTYPE, TID_DOUBLE, 8}, {
00088    0, 0, 0}
00089 };
00090 #endif
00091 
00092 extern EQUIPMENT equipment[];
00093 
00094 EQUIPMENT *interrupt_eq = NULL;
00095 EQUIPMENT *multithread_eq = NULL;
00096 BOOL slowcont_eq = FALSE;
00097 void *event_buffer;
00098 void *frag_buffer = NULL;
00099 
00100 /* inter-thread communication */
00101 int rbh1=0, rbh2=0, rbh1_next=0, rbh2_next=0;
00102 volatile int stop_all_threads = 0;
00103 int readout_thread(void *param);
00104 volatile int readout_thread_active = 0;
00105 
00106 int send_event(INT idx);
00107 int receive_trigger_event(EQUIPMENT *eq);
00108 void send_all_periodic_events(INT transition);
00109 void interrupt_routine(void);
00110 void readout_enable(BOOL flag);
00111 int readout_enabled(void);
00112 void display(BOOL bInit);
00113 BOOL logger_root();
00114 INT check_polled_events(void);
00115 
00116 /*---- ODB records -------------------------------------------------*/
00117 
00118 #define EQUIPMENT_COMMON_STR "\
00119 Event ID = WORD : 0\n\
00120 Trigger mask = WORD : 0\n\
00121 Buffer = STRING : [32] SYSTEM\n\
00122 Type = INT : 0\n\
00123 Source = INT : 0\n\
00124 Format = STRING : [8] FIXED\n\
00125 Enabled = BOOL : 0\n\
00126 Read on = INT : 0\n\
00127 Period = INT : 0\n\
00128 Event limit = DOUBLE : 0\n\
00129 Num subevents = DWORD : 0\n\
00130 Log history = INT : 0\n\
00131 Frontend host = STRING : [32] \n\
00132 Frontend name = STRING : [32] \n\
00133 Frontend file name = STRING : [256] \n\
00134 "
00135 
00136 #define EQUIPMENT_STATISTICS_STR "\
00137 Events sent = DOUBLE : 0\n\
00138 Events per sec. = DOUBLE : 0\n\
00139 kBytes per sec. = DOUBLE : 0\n\
00140 "
00141 
00142 /*-- transition callbacks ------------------------------------------*/
00143 
00144 /*-- start ---------------------------------------------------------*/
00145 
00146 INT tr_start(INT rn, char *error)
00147 {
00148    INT i, status;
00149 
00150    /* reset serial numbers */
00151    for (i = 0; equipment[i].name[0]; i++) {
00152       equipment[i].serial_number = 0;
00153       equipment[i].subevent_number = 0;
00154       equipment[i].stats.events_sent = 0;
00155       equipment[i].odb_in = equipment[i].odb_out = 0;
00156    }
00157 
00158    status = begin_of_run(rn, error);
00159 
00160    if (status == CM_SUCCESS) {
00161       run_state = STATE_RUNNING;
00162       run_number = rn;
00163 
00164       send_all_periodic_events(TR_START);
00165 
00166       if (display_period) {
00167          ss_printf(14, 2, "Running ");
00168          ss_printf(36, 2, "%d", rn);
00169       }
00170 
00171       /* enable interrupts or readout thread */
00172       readout_enable(TRUE);
00173    }
00174 
00175    return status;
00176 }
00177 
00178 /*-- prestop -------------------------------------------------------*/
00179 
00180 INT tr_stop(INT rn, char *error)
00181 {
00182    INT status, i;
00183    EQUIPMENT *eq;
00184 
00185    /* disable interrupts or readout thread */
00186    readout_enable(FALSE);
00187 
00188    status = end_of_run(rn, error);
00189 
00190    /* check if event(s) happened just before disabling the trigger */
00191    if ((i = check_polled_events()) > 0) {
00192       // cm_msg(MINFO, "tr_stop", "sent remaining %d polled events", i);
00193    }
00194 
00195    if (status == CM_SUCCESS) {
00196       /* don't send events if already stopped */
00197       if (run_state != STATE_STOPPED)
00198          send_all_periodic_events(TR_STOP);
00199 
00200       run_state = STATE_STOPPED;
00201       run_number = rn;
00202 
00203       if (display_period)
00204          ss_printf(14, 2, "Stopped ");
00205    } else
00206       readout_enable(TRUE);
00207 
00208    for (i = 0; equipment[i].name[0]; i++)
00209       /* read remaining events from ring buffers */
00210       if (equipment[i].info.eq_type & (EQ_MULTITHREAD | EQ_INTERRUPT)) {
00211          while (receive_trigger_event(equipment+i) > 0);
00212       }
00213 
00214       /* flush remaining buffered events */
00215       rpc_flush_event();
00216       if (equipment[i].buffer_handle) {
00217          INT err = bm_flush_cache(equipment[i].buffer_handle, SYNC);
00218          if (err != BM_SUCCESS) {
00219             cm_msg(MERROR, "tr_stop", "bm_flush_cache(SYNC) error %d", err);
00220             return err;
00221          }
00222       }
00223 
00224    /* update final statistics record in ODB */
00225    for (i = 0; equipment[i].name[0]; i++) {
00226       eq = &equipment[i];
00227       eq->stats.events_sent += eq->events_sent;
00228       eq->bytes_sent = 0;
00229       eq->events_sent = 0;
00230    }
00231 
00232    db_send_changed_records();
00233 
00234    return status;
00235 }
00236 
00237 /*-- pause ---------------------------------------------------------*/
00238 
00239 INT tr_pause(INT rn, char *error)
00240 {
00241    INT status;
00242 
00243    /* disable interrupts or readout thread */
00244    readout_enable(FALSE);
00245 
00246    status = pause_run(rn, error);
00247 
00248    if (status == CM_SUCCESS) {
00249       run_state = STATE_PAUSED;
00250       run_number = rn;
00251 
00252       send_all_periodic_events(TR_PAUSE);
00253 
00254       if (display_period)
00255          ss_printf(14, 2, "Paused  ");
00256    } else
00257       readout_enable(TRUE);
00258 
00259    return status;
00260 }
00261 
00262 /*-- resume --------------------------------------------------------*/
00263 
00264 INT tr_resume(INT rn, char *error)
00265 {
00266    INT status;
00267 
00268    status = resume_run(rn, error);
00269 
00270    if (status == CM_SUCCESS) {
00271       run_state = STATE_RUNNING;
00272       run_number = rn;
00273 
00274       send_all_periodic_events(TR_RESUME);
00275 
00276       if (display_period)
00277          ss_printf(14, 2, "Running ");
00278 
00279       /* enable interrupts or readout thread */
00280       readout_enable(TRUE);
00281    }
00282 
00283    return status;
00284 }
00285 
00286 /*------------------------------------------------------------------*/
00287 
00288 INT manual_trigger(INT idx, void *prpc_param[])
00289 {
00290    int i;
00291 
00292    i = idx; /* avoid compiler warning */
00293    manual_trigger_event_id = CWORD(0);
00294    return SUCCESS;
00295 }
00296 
00297 /*------------------------------------------------------------------*/
00298 
00299 int sc_thread(void *info)
00300 {
00301    DEVICE_DRIVER *device_drv = info;
00302    int i, status, cmd;
00303    int current_channel = 0;
00304    int current_priority_channel = 0;
00305    float value;
00306    int *last_update;
00307    unsigned int current_time;
00308 
00309    last_update = calloc(device_drv->channels, sizeof(int));
00310 
00311    do {
00312       /* read one channel from device */
00313       for (cmd = CMD_GET_FIRST; cmd <= CMD_GET_LAST; cmd++) {
00314          status = device_drv->dd(cmd, device_drv->dd_info, current_channel, &value);
00315 
00316          ss_mutex_wait_for(device_drv->mutex, 1000);
00317          device_drv->mt_buffer->channel[current_channel].variable[cmd] = value;
00318          device_drv->mt_buffer->status = status;
00319          ss_mutex_release(device_drv->mutex);
00320 
00321          //printf("TID %d: channel %d, value %f\n", ss_gettid(), current_channel, value);
00322       }
00323 
00324       /* switch to next channel in next loop */
00325       current_channel = (current_channel + 1) % device_drv->channels;
00326 
00327       /* check for priority channel */
00328       current_time = ss_millitime();
00329       i = (current_priority_channel + 1) % device_drv->channels;
00330       while (!(current_time - last_update[i] < 10000)) {
00331          i = (i + 1) % device_drv->channels;
00332          if (i == current_priority_channel) {
00333             /* non found, so finish */
00334             break;
00335          }
00336       }
00337 
00338       /* updated channel found, so read it additionally */
00339       if (current_time - last_update[i] < 10000) {
00340          current_priority_channel = i;
00341 
00342          for (cmd = CMD_GET_FIRST; cmd <= CMD_GET_LAST; cmd++) {
00343             status = device_drv->dd(cmd, device_drv->dd_info, i, &value);
00344 
00345             ss_mutex_wait_for(device_drv->mutex, 1000);
00346             device_drv->mt_buffer->channel[i].variable[cmd] = value;
00347             device_drv->mt_buffer->status = status;
00348             ss_mutex_release(device_drv->mutex);
00349          }
00350       }
00351 
00352       /* check if anything to write to device */
00353       for (i = 0; i < device_drv->channels; i++) {
00354 
00355          for (cmd = CMD_SET_FIRST; cmd <= CMD_SET_LAST; cmd++) {
00356             if (!ss_isnan(device_drv->mt_buffer->channel[i].variable[cmd])) {
00357                ss_mutex_wait_for(device_drv->mutex, 1000);
00358                value = device_drv->mt_buffer->channel[i].variable[cmd];
00359                device_drv->mt_buffer->channel[i].variable[cmd] = (float) ss_nan();
00360                device_drv->mt_buffer->status = status;
00361                ss_mutex_release(device_drv->mutex);
00362 
00363                status = device_drv->dd(cmd, device_drv->dd_info, i, value);
00364                last_update[i] = ss_millitime();
00365             }
00366          }
00367       }
00368 
00369    } while (device_drv->stop_thread == 0);
00370 
00371    free(last_update);
00372 
00373    /* signal stopped thread */
00374    device_drv->stop_thread = 2;
00375 
00376    return SUCCESS;
00377 }
00378 
00379 /*------------------------------------------------------------------*/
00380 
00381 INT device_driver(DEVICE_DRIVER * device_drv, INT cmd, ...)
00382 {
00383    va_list argptr;
00384    HNDLE hKey;
00385    INT channel, status, i, j;
00386    float value, *pvalue;
00387    char *name, *label, str[256];
00388 
00389    va_start(argptr, cmd);
00390    status = FE_SUCCESS;
00391 
00392    switch (cmd) {
00393    case CMD_INIT:
00394       hKey = va_arg(argptr, HNDLE);
00395 
00396       if (device_drv->flags & DF_MULTITHREAD) {
00397          status = device_drv->dd(CMD_INIT, hKey, &device_drv->dd_info,
00398                                     device_drv->channels, device_drv->flags,
00399                                     device_drv->bd);
00400 
00401          if (status == FE_SUCCESS && (device_drv->flags & DF_MULTITHREAD)) {
00402             /* create inter-thread data exchange buffers */
00403             device_drv->mt_buffer = (DD_MT_BUFFER *) calloc(1, sizeof(DD_MT_BUFFER));
00404             device_drv->mt_buffer->n_channels = device_drv->channels;
00405             device_drv->mt_buffer->channel = (DD_MT_CHANNEL *) calloc(device_drv->channels, sizeof(DD_MT_CHANNEL));
00406             assert(device_drv->mt_buffer->channel);
00407 
00408             /* set all set values to NaN */
00409             for (i=0 ; i<device_drv->channels ; i++)
00410                for (j=CMD_SET_FIRST ; j<=CMD_SET_LAST ; j++)
00411                   device_drv->mt_buffer->channel[i].variable[j] = (float)ss_nan();
00412 
00413             /* get default names for this driver already now */
00414             for (i = 0; i < device_drv->channels; i++)
00415                device_drv->dd(CMD_GET_LABEL, device_drv->dd_info, i,
00416                                  device_drv->mt_buffer->channel[i].label);
00417 
00418             /* create mutex */
00419             sprintf(str, "DD_%s", device_drv->name);
00420             status = ss_mutex_create(str, &device_drv->mutex);
00421             if (status != SS_CREATED && status != SS_SUCCESS)
00422                return FE_ERR_DRIVER;
00423             status = FE_SUCCESS;
00424          }
00425       } else {
00426          status = device_drv->dd(CMD_INIT, hKey, &device_drv->dd_info,
00427                                     device_drv->channels, device_drv->flags,
00428                                     device_drv->bd);
00429       }
00430       break;
00431 
00432    case CMD_START:
00433       if (device_drv->flags & DF_MULTITHREAD && device_drv->mt_buffer != NULL) {
00434          /* create dedicated thread for this device */
00435          device_drv->mt_buffer->thread_id = ss_thread_create(sc_thread, device_drv);
00436       }
00437       break;
00438 
00439    case CMD_STOP:
00440       if (device_drv->flags & DF_MULTITHREAD && device_drv->mt_buffer != NULL) {
00441          device_drv->stop_thread = 1;
00442          /* wait for max. 10 seconds until thread has gracefully stopped */
00443          for (i = 0; i < 1000; i++) {
00444             if (device_drv->stop_thread == 2)
00445                break;
00446             ss_sleep(10);
00447          }
00448 
00449          /* if timeout expired, kill thread */
00450          if (i == 1000)
00451             ss_thread_kill(device_drv->mt_buffer->thread_id);
00452 
00453          ss_mutex_delete(device_drv->mutex, TRUE);
00454          free(device_drv->mt_buffer->channel);
00455          free(device_drv->mt_buffer);
00456       }
00457       break;
00458 
00459    case CMD_EXIT:
00460       status = device_drv->dd(CMD_EXIT, device_drv->dd_info);
00461       break;
00462 
00463    case CMD_SET_LABEL:
00464       channel = va_arg(argptr, INT);
00465       label = va_arg(argptr, char *);
00466       status = device_drv->dd(CMD_SET_LABEL, device_drv->dd_info, channel, label);
00467       break;
00468 
00469    case CMD_GET_LABEL:
00470       channel = va_arg(argptr, INT);
00471       name = va_arg(argptr, char *);
00472       status = device_drv->dd(CMD_GET_LABEL, device_drv->dd_info, channel, name);
00473       break;
00474 
00475    default:
00476 
00477       if (cmd >= CMD_SET_FIRST && cmd <= CMD_SET_LAST) {
00478 
00479          /* transfer data to sc_thread for SET commands */
00480          channel = va_arg(argptr, INT);
00481          value = (float) va_arg(argptr, double);        // floats are passed as double
00482          if (device_drv->flags & DF_MULTITHREAD) {
00483             ss_mutex_wait_for(device_drv->mutex, 1000);
00484             device_drv->mt_buffer->channel[channel].variable[cmd] = value;
00485             status = device_drv->mt_buffer->status;
00486             ss_mutex_release(device_drv->mutex);
00487          } else {
00488             status = device_drv->dd(cmd, device_drv->dd_info, channel, value);
00489          }
00490 
00491       } else if (cmd >= CMD_GET_FIRST && cmd <= CMD_GET_LAST) {
00492 
00493          /* transfer data from sc_thread for GET commands */
00494          channel = va_arg(argptr, INT);
00495          pvalue = va_arg(argptr, float *);
00496          if (device_drv->flags & DF_MULTITHREAD) {
00497             ss_mutex_wait_for(device_drv->mutex, 1000);
00498             *pvalue = device_drv->mt_buffer->channel[channel].variable[cmd];
00499             status = device_drv->mt_buffer->status;
00500             ss_mutex_release(device_drv->mutex);
00501          } else
00502             status = device_drv->dd(cmd, device_drv->dd_info, channel, pvalue);
00503 
00504       } else {
00505 
00506          /* all remaining commands which are passed directly to the device driver */
00507          channel = va_arg(argptr, INT);
00508          pvalue = va_arg(argptr, float *);
00509          status = device_drv->dd(cmd, device_drv->dd_info, channel, pvalue);
00510       }
00511 
00512       break;
00513    }
00514 
00515    va_end(argptr);
00516    return status;
00517 }
00518 
00519 /*------------------------------------------------------------------*/
00520 
00521 INT register_equipment(void)
00522 {
00523    INT idx, count, size, status, i, j, k, n;
00524    char str[256];
00525    EQUIPMENT_INFO *eq_info;
00526    EQUIPMENT_STATS *eq_stats;
00527    DWORD start_time, delta_time;
00528    HNDLE hKey;
00529    BOOL manual_trig_flag = FALSE;
00530    BANK_LIST *bank_list;
00531    DWORD dummy;
00532 
00533    /* get current ODB run state */
00534    size = sizeof(run_state);
00535    run_state = STATE_STOPPED;
00536    db_get_value(hDB, 0, "/Runinfo/State", &run_state, &size, TID_INT, TRUE);
00537    size = sizeof(run_number);
00538    run_number = 1;
00539    status =
00540        db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
00541    assert(status == SUCCESS);
00542 
00543    /* scan EQUIPMENT table from FRONTEND.C */
00544    for (idx = 0; equipment[idx].name[0]; idx++) {
00545       eq_info = &equipment[idx].info;
00546       eq_stats = &equipment[idx].stats;
00547 
00548       if (eq_info->event_id == 0) {
00549          printf("\nEvent ID 0 for %s not allowed\n", equipment[idx].name);
00550          cm_disconnect_experiment();
00551          ss_sleep(5000);
00552          exit(0);
00553       }
00554 
00555       /* init status */
00556       equipment[idx].status = FE_SUCCESS;
00557 
00558       /* check for % in equipment (needed for event building) */
00559       if (frontend_index != -1) {
00560          /* modify equipment name to <name>xx where xx is the frontend index */
00561          if (strchr(equipment[idx].name, '%')) {
00562             strcpy(str, equipment[idx].name);
00563             sprintf(equipment[idx].name, str, frontend_index);
00564          }
00565 
00566          /* modify event buffer name to <name>xx where xx is the frontend index */
00567          if (strchr(eq_info->buffer, '%')) {
00568             strcpy(str, eq_info->buffer);
00569             sprintf(eq_info->buffer, str, frontend_index);
00570          }
00571       } else {
00572          /* stip %.. */
00573          if (strchr(equipment[idx].name, '%'))
00574             *strchr(equipment[idx].name, '%') = 0;
00575          if (strchr(eq_info->buffer, '%'))
00576             *strchr(eq_info->buffer, '%') = 0;
00577       }
00578 
00579       sprintf(str, "/Equipment/%s/Common", equipment[idx].name);
00580 
00581       /* get last event limit from ODB */
00582       if (eq_info->eq_type != EQ_SLOW) {
00583          db_find_key(hDB, 0, str, &hKey);
00584          size = sizeof(double);
00585          if (hKey)
00586             db_get_value(hDB, hKey, "Event limit", &eq_info->event_limit, &size,
00587                          TID_DOUBLE, TRUE);
00588       }
00589 
00590       /* Create common subtree */
00591       status = db_check_record(hDB, 0, str, EQUIPMENT_COMMON_STR, FALSE);
00592       if (status == DB_NO_KEY || status == DB_STRUCT_MISMATCH) {
00593          db_create_record(hDB, 0, str, EQUIPMENT_COMMON_STR);
00594          db_find_key(hDB, 0, str, &hKey);
00595          db_set_record(hDB, hKey, eq_info, sizeof(EQUIPMENT_INFO), 0);
00596       } else if (status != DB_SUCCESS) {
00597          printf("Cannot check equipment record, status = %d\n", status);
00598          ss_sleep(3000);
00599       }
00600       db_find_key(hDB, 0, str, &hKey);
00601       assert(hKey);
00602 
00603       /* set fixed parameters from user structure */
00604       db_set_value(hDB, hKey, "Event ID", &eq_info->event_id, sizeof(WORD), 1, TID_WORD);
00605       db_set_value(hDB, hKey, "Type", &eq_info->eq_type, sizeof(INT), 1, TID_INT);
00606       db_set_value(hDB, hKey, "Source", &eq_info->source, sizeof(INT), 1, TID_INT);
00607 
00608       /* open hot link to equipment info */
00609       db_open_record(hDB, hKey, eq_info, sizeof(EQUIPMENT_INFO), MODE_READ, NULL, NULL);
00610 
00611       if (equal_ustring(eq_info->format, "YBOS"))
00612          equipment[idx].format = FORMAT_YBOS;
00613       else if (equal_ustring(eq_info->format, "FIXED"))
00614          equipment[idx].format = FORMAT_FIXED;
00615       else                      /* default format is MIDAS */
00616          equipment[idx].format = FORMAT_MIDAS;
00617 
00618       gethostname(eq_info->frontend_host, sizeof(eq_info->frontend_host));
00619       strcpy(eq_info->frontend_name, full_frontend_name);
00620       strcpy(eq_info->frontend_file_name, frontend_file_name);
00621 
00622       /* update variables in ODB */
00623       db_set_record(hDB, hKey, eq_info, sizeof(EQUIPMENT_INFO), 0);
00624 
00625       /*---- Create variables record ---------------------------------*/
00626 
00627       sprintf(str, "/Equipment/%s/Variables", equipment[idx].name);
00628       if (equipment[idx].event_descrip) {
00629          if (equipment[idx].format == FORMAT_FIXED)
00630             db_check_record(hDB, 0, str, (char *) equipment[idx].event_descrip, TRUE);
00631          else {
00632             /* create bank descriptions */
00633             bank_list = (BANK_LIST *) equipment[idx].event_descrip;
00634 
00635             for (; bank_list->name[0]; bank_list++) {
00636                /* mabye needed later...
00637                   if (bank_list->output_flag == 0)
00638                   continue;
00639                 */
00640 
00641                if (bank_list->type == TID_STRUCT) {
00642                   sprintf(str, "/Equipment/%s/Variables/%s", equipment[idx].name,
00643                           bank_list->name);
00644                   status =
00645                       db_check_record(hDB, 0, str, strcomb(bank_list->init_str), TRUE);
00646                   if (status != DB_SUCCESS) {
00647                      printf("Cannot check/create record \"%s\", status = %d\n", str,
00648                             status);
00649                      ss_sleep(3000);
00650                   }
00651                } else {
00652                   sprintf(str, "/Equipment/%s/Variables/%s", equipment[idx].name,
00653                           bank_list->name);
00654                   dummy = 0;
00655                   db_set_value(hDB, 0, str, &dummy, rpc_tid_size(bank_list->type), 1,
00656                                bank_list->type);
00657                }
00658             }
00659          }
00660       } else
00661          db_create_key(hDB, 0, str, TID_KEY);
00662 
00663       sprintf(str, "/Equipment/%s/Variables", equipment[idx].name);
00664       db_find_key(hDB, 0, str, &hKey);
00665       equipment[idx].hkey_variables = hKey;
00666 
00667       /*---- Create and initialize statistics tree -------------------*/
00668 
00669       sprintf(str, "/Equipment/%s/Statistics", equipment[idx].name);
00670 
00671       status = db_check_record(hDB, 0, str, EQUIPMENT_STATISTICS_STR, TRUE);
00672       if (status != DB_SUCCESS) {
00673          printf("Cannot create/check statistics record \'%s\', error %d\n", str, status);
00674          ss_sleep(3000);
00675       }
00676 
00677       status = db_find_key(hDB, 0, str, &hKey);
00678       if (status != DB_SUCCESS) {
00679          printf("Cannot find statistics record \'%s\', error %d\n", str, status);
00680          ss_sleep(3000);
00681       }
00682 
00683       eq_stats->events_sent = 0;
00684       eq_stats->events_per_sec = 0;
00685       eq_stats->kbytes_per_sec = 0;
00686 
00687       /* open hot link to statistics tree */
00688       status =
00689           db_open_record(hDB, hKey, eq_stats, sizeof(EQUIPMENT_STATS), MODE_WRITE, NULL,
00690                          NULL);
00691       if (status != DB_SUCCESS) {
00692          cm_msg(MERROR, "register_equipment",
00693                 "Cannot open statistics record \'%s\', error %d. Probably other FE is using it",
00694                 str, status);
00695          ss_sleep(3000);
00696       }
00697 
00698       /*---- open event buffer ---------------------------------------*/
00699 
00700       /* check for fragmented event */
00701       if (eq_info->eq_type & EQ_FRAGMENTED) {
00702          if (frag_buffer == NULL)
00703             frag_buffer = malloc(max_event_size_frag);
00704 
00705          if (frag_buffer == NULL) {
00706             cm_msg(MERROR, "register_equipment",
00707                   "Not enough memory to allocate buffer for fragmented events");
00708             return SS_NO_MEMORY;
00709          }
00710       }
00711 
00712       if (eq_info->buffer[0]) {
00713          status =
00714              bm_open_buffer(eq_info->buffer, 2*MAX_EVENT_SIZE,
00715                             &equipment[idx].buffer_handle);
00716          if (status != BM_SUCCESS && status != BM_CREATED) {
00717             cm_msg(MERROR, "register_equipment",
00718                    "Cannot open event buffer \"%s\" size %d, bm_open_buffer() status %d", eq_info->buffer, 2*MAX_EVENT_SIZE, status);
00719             return 0;
00720          }
00721 
00722          /* set the default buffer cache size */
00723          bm_set_cache_size(equipment[idx].buffer_handle, 0, SERVER_CACHE_SIZE);
00724       } else
00725          equipment[idx].buffer_handle = 0;
00726 
00727       /*---- evaluate polling count ----------------------------------*/
00728 
00729       if (eq_info->eq_type & (EQ_POLLED | EQ_MULTITHREAD)) {
00730          if (eq_info->eq_type & EQ_INTERRUPT) {
00731             if (eq_info->eq_type & EQ_POLLED)
00732                cm_msg(MERROR, "register_equipment",
00733                   "Equipment \"%s\" cannot be of type EQ_INTERRUPT and EQ_POLLED at the same time", 
00734                   equipment[idx].name);
00735             else
00736                cm_msg(MERROR, "register_equipment",
00737                   "Equipment \"%s\" cannot be of type EQ_INTERRUPT and EQ_MULTITHREAD at the same time", 
00738                   equipment[idx].name);
00739             return 0;
00740          }
00741 
00742          if (display_period)
00743             printf("\nCalibrating");
00744 
00745          count = 1;
00746          do {
00747             if (display_period)
00748                printf(".");
00749 
00750             start_time = ss_millitime();
00751 
00752             poll_event(equipment[idx].info.source, count, TRUE);
00753 
00754             delta_time = ss_millitime() - start_time;
00755 
00756             if (count == 1 && delta_time > eq_info->period * 1.2) {
00757                cm_msg(MERROR, "register_equipment", "Polling routine with count=1 takes %d ms", delta_time);
00758                ss_sleep(3000);
00759                break;
00760             }
00761 
00762             if (delta_time > 0)
00763                count = (INT) ((double) count * eq_info->period / delta_time);
00764             else
00765                count *= 100;
00766 
00767          } while (delta_time > eq_info->period * 1.2 || delta_time < eq_info->period * 0.8);
00768 
00769          equipment[idx].poll_count = count;
00770 
00771          if (display_period)
00772             printf("OK\n");
00773       }
00774 
00775       /*---- initialize interrupt events -----------------------------*/
00776 
00777       if (eq_info->eq_type & EQ_INTERRUPT) {
00778          /* install interrupt for interrupt events */
00779 
00780          for (i = 0; equipment[i].name[0]; i++)
00781             if (equipment[i].info.eq_type & EQ_POLLED) {
00782                equipment[idx].status = FE_ERR_DISABLED;
00783                cm_msg(MINFO, "register_equipment",
00784                       "Interrupt readout cannot be combined with polled readout");
00785             }
00786 
00787          if (equipment[idx].status != FE_ERR_DISABLED) {
00788             if (eq_info->enabled) {
00789                if (interrupt_eq) {
00790                   equipment[idx].status = FE_ERR_DISABLED;
00791                   cm_msg(MINFO, "register_equipment",
00792                          "Defined more than one equipment with interrupt readout");
00793                } else {
00794                   interrupt_eq = &equipment[idx];
00795 
00796                   /* create ring buffer for inter-thread data transfer */
00797                   if (!rbh1) {
00798                      rb_create(event_buffer_size, max_event_size, &rbh1);
00799                      rbh2 = rbh1;
00800                   }
00801 
00802                   /* establish interrupt handler */
00803                   interrupt_configure(CMD_INTERRUPT_ATTACH, eq_info->source,
00804                                       (POINTER_T) interrupt_routine);
00805                }
00806             } else {
00807                equipment[idx].status = FE_ERR_DISABLED;
00808                cm_msg(MINFO, "register_equipment",
00809                       "Equipment %s disabled in file \"frontend.c\"",
00810                       equipment[idx].name);
00811             }
00812          }
00813       }
00814 
00815       /*---- initialize multithread events -------------------------*/
00816 
00817       if (eq_info->eq_type & EQ_MULTITHREAD) {
00818          /* install interrupt for interrupt events */
00819 
00820          for (i = 0; equipment[i].name[0]; i++)
00821             if (equipment[i].info.eq_type & EQ_POLLED) {
00822                equipment[idx].status = FE_ERR_DISABLED;
00823                cm_msg(MINFO, "register_equipment",
00824                       "Multi-threaded readout cannot be combined with polled readout for equipment \'%s\'", equipment[i].name);
00825             }
00826 
00827          if (equipment[idx].status != FE_ERR_DISABLED) {
00828             if (eq_info->enabled) {
00829                if (multithread_eq) {
00830                   equipment[idx].status = FE_ERR_DISABLED;
00831                   cm_msg(MINFO, "register_equipment",
00832                          "Defined more than one equipment with multi-threaded readout for equipment \'%s\'", equipment[i].name);
00833                } else {
00834                   multithread_eq = &equipment[idx];
00835 
00836                   /* create ring buffer for inter-thread data transfer */
00837                   if (!rbh1) {
00838                      rb_create(event_buffer_size, max_event_size, &rbh1);
00839                      rbh2 = rbh1;
00840                   }
00841 
00842                   /* create hardware reading thread */
00843                   readout_enable(FALSE);
00844                   ss_thread_create(readout_thread, multithread_eq);
00845                }
00846             } else {
00847                equipment[idx].status = FE_ERR_DISABLED;
00848                cm_msg(MINFO, "register_equipment",
00849                       "Equipment %s disabled in file \"frontend.c\"",
00850                       equipment[idx].name);
00851             }
00852          }
00853       }
00854 
00855       /*---- initialize slow control equipment ---------------------*/
00856 
00857       if (eq_info->eq_type & EQ_SLOW) {
00858          /* resolve duplicate device names */
00859          for (i = 0; equipment[idx].driver[i].name[0]; i++)
00860             for (j = i + 1; equipment[idx].driver[j].name[0]; j++)
00861                if (equal_ustring(equipment[idx].driver[i].name,
00862                                  equipment[idx].driver[j].name)) {
00863                   strcpy(str, equipment[idx].driver[i].name);
00864                   for (k = 0, n = 0; equipment[idx].driver[k].name[0]; k++)
00865                      if (equal_ustring(str, equipment[idx].driver[k].name))
00866                         sprintf(equipment[idx].driver[k].name, "%s_%d", str, n++);
00867 
00868                   break;
00869                }
00870 
00871          /* loop over equipment list and call class driver's init method */
00872          if (eq_info->enabled) {
00873             printf("%s:\n", equipment[idx].name);
00874             equipment[idx].status = equipment[idx].cd(CMD_INIT, &equipment[idx]);
00875          } else {
00876             equipment[idx].status = FE_ERR_DISABLED;
00877             cm_msg(MINFO, "register_equipment",
00878                    "Equipment %s disabled in ODB", equipment[idx].name);
00879          }
00880 
00881          /* now start threads if requested */
00882          if (equipment[idx].status == FE_SUCCESS)
00883             equipment[idx].cd(CMD_START, &equipment[idx]);   /* start threads for this equipment */
00884 
00885          /* remember that we have slowcontrol equipment (needed later for scheduler) */
00886          slowcont_eq = TRUE;
00887 
00888          /* let user read error messages */
00889          if (equipment[idx].status != FE_SUCCESS && 
00890              equipment[idx].status != FE_ERR_DISABLED)
00891             ss_sleep(3000);
00892       }
00893 
00894       /*---- register callback for manual triggered events -----------*/
00895       if (eq_info->eq_type & EQ_MANUAL_TRIG) {
00896          if (!manual_trig_flag)
00897             cm_register_function(RPC_MANUAL_TRIG, manual_trigger);
00898 
00899          manual_trig_flag = TRUE;
00900       }
00901    }
00902 
00903    if (slowcont_eq)
00904       cm_msg(MINFO, "register_equipment", "Slow control equipment initialized");
00905 
00906    return SUCCESS;
00907 }
00908 
00909 /*------------------------------------------------------------------*/
00910 
00911 void update_odb(EVENT_HEADER * pevent, HNDLE hKey, INT format)
00912 {
00913    INT size, i, ni4, tsize, status, n_data;
00914    char *pdata;
00915    char name[5];
00916    BANK_HEADER *pbh;
00917    BANK *pbk;
00918    BANK32 *pbk32;
00919    char *pydata;
00920    DWORD odb_type;
00921    DWORD *pyevt, bkname;
00922    WORD bktype;
00923    HNDLE hKeyRoot, hKeyl;
00924    KEY key;
00925 
00926    /* outcommented since db_find_key does not work in FTCP mode, SR 25.4.03
00927       rpc_set_option(-1, RPC_OTRANSPORT, RPC_FTCP); */
00928 
00929    if (format == FORMAT_FIXED) {
00930       if (db_set_record(hDB, hKey, (char *) (pevent + 1),
00931                         pevent->data_size, 0) != DB_SUCCESS)
00932          cm_msg(MERROR, "update_odb", "event #%d size mismatch", pevent->event_id);
00933    } else if (format == FORMAT_MIDAS) {
00934       pbh = (BANK_HEADER *) (pevent + 1);
00935       pbk = NULL;
00936       pbk32 = NULL;
00937       do {
00938          /* scan all banks */
00939          if (bk_is32(pbh)) {
00940             size = bk_iterate32(pbh, &pbk32, &pdata);
00941             if (pbk32 == NULL)
00942                break;
00943             bkname = *((DWORD *) pbk32->name);
00944             bktype = (WORD) pbk32->type;
00945          } else {
00946             size = bk_iterate(pbh, &pbk, &pdata);
00947             if (pbk == NULL)
00948                break;
00949             bkname = *((DWORD *) pbk->name);
00950             bktype = (WORD) pbk->type;
00951          }
00952 
00953          n_data = size;
00954          if (rpc_tid_size(bktype & 0xFF))
00955             n_data /= rpc_tid_size(bktype & 0xFF);
00956 
00957          /* get bank key */
00958          *((DWORD *) name) = bkname;
00959          name[4] = 0;
00960 
00961          if (bktype == TID_STRUCT) {
00962             status = db_find_key(hDB, hKey, name, &hKeyRoot);
00963             if (status != DB_SUCCESS) {
00964                cm_msg(MERROR, "update_odb",
00965                       "please define bank %s in BANK_LIST in frontend.c", name);
00966                continue;
00967             }
00968 
00969             /* write structured bank */
00970             for (i = 0;; i++) {
00971                status = db_enum_key(hDB, hKeyRoot, i, &hKeyl);
00972                if (status == DB_NO_MORE_SUBKEYS)
00973                   break;
00974 
00975                db_get_key(hDB, hKeyl, &key);
00976 
00977                /* adjust for alignment */
00978                if (key.type != TID_STRING && key.type != TID_LINK)
00979                   pdata =
00980                       (void *) VALIGN(pdata, MIN(ss_get_struct_align(), key.item_size));
00981 
00982                status = db_set_data(hDB, hKeyl, pdata, key.item_size * key.num_values,
00983                                     key.num_values, key.type);
00984                if (status != DB_SUCCESS) {
00985                   cm_msg(MERROR, "update_odb", "cannot write %s to ODB", name);
00986                   continue;
00987                }
00988 
00989                /* shift data pointer to next item */
00990                pdata += key.item_size * key.num_values;
00991             }
00992          } else {
00993             /* write variable length bank  */
00994             if (n_data > 0)
00995                db_set_value(hDB, hKey, name, pdata, size, n_data, bktype & 0xFF);
00996          }
00997 
00998       } while (1);
00999    } else if (format == FORMAT_YBOS) {
01000 #ifdef YBOS_SUPPORT
01001       YBOS_BANK_HEADER *pybkh;
01002 
01003       /* skip the lrl (4 bytes per event) */
01004       pyevt = (DWORD *) (pevent + 1);
01005       pybkh = NULL;
01006       do {
01007          /* scan all banks */
01008          ni4 = ybk_iterate(pyevt, &pybkh, (void *) (&pydata));
01009          if (pybkh == NULL || ni4 == 0)
01010             break;
01011 
01012          /* find the corresponding odb type */
01013          tsize = odb_type = 0;
01014          for (i = 0; id_map[0].ybos_type > 0; i++) {
01015             if (pybkh->type == id_map[i].ybos_type) {
01016                odb_type = id_map[i].odb_type;
01017                tsize = id_map[i].tsize;
01018                break;
01019             }
01020          }
01021 
01022          /* extract bank name (key name) */
01023          *((DWORD *) name) = pybkh->name;
01024          name[4] = 0;
01025 
01026          /* reject EVID bank */
01027          if (strncmp(name, "EVID", 4) == 0)
01028             continue;
01029 
01030          /* correct YBS number of entries */
01031          if (pybkh->type == D8_BKTYPE)
01032             ni4 /= 2;
01033          if (pybkh->type == I2_BKTYPE)
01034             ni4 *= 2;
01035          if (pybkh->type == I1_BKTYPE || pybkh->type == A1_BKTYPE)
01036             ni4 *= 4;
01037 
01038          /* write bank to ODB, ni4 always in I*4 */
01039          size = ni4 * tsize;
01040          if ((status =
01041               db_set_value(hDB, hKey, name, pydata, size, ni4,
01042                            odb_type & 0xFF)) != DB_SUCCESS) {
01043             printf("status:%d odb_type:%d name:%s ni4:%d size:%d tsize:%d\n", status,
01044                    odb_type, name, ni4, size, tsize);
01045             for (i = 0; i < 6; i++) {
01046                printf("data: %f\n", *((float *) (pydata)));
01047                pydata += sizeof(float);
01048             }
01049          }
01050       } while (1);
01051 #endif                          /* YBOS_SUPPORT */
01052    }
01053 
01054    rpc_set_option(-1, RPC_OTRANSPORT, RPC_TCP);
01055 }
01056 
01057 /*------------------------------------------------------------------*/
01058 
01059 int send_event(INT idx)
01060 {
01061    EQUIPMENT_INFO *eq_info;
01062    EVENT_HEADER *pevent, *pfragment;
01063    char *pdata;
01064    unsigned char *pd;
01065    INT i, status;
01066    DWORD sent, size;
01067 
01068    eq_info = &equipment[idx].info;
01069 
01070    /* check for fragmented event */
01071    if (eq_info->eq_type & EQ_FRAGMENTED)
01072       pevent = frag_buffer;
01073    else
01074       pevent = (EVENT_HEADER *)event_buffer;
01075 
01076    /* compose MIDAS event header */
01077    pevent->event_id = eq_info->event_id;
01078    pevent->trigger_mask = eq_info->trigger_mask;
01079    pevent->data_size = 0;
01080    pevent->time_stamp = ss_time();
01081    pevent->serial_number = equipment[idx].serial_number++;
01082 
01083    equipment[idx].last_called = ss_millitime();
01084 
01085    /* call user readout routine */
01086    *((EQUIPMENT **) (pevent + 1)) = &equipment[idx];
01087    pevent->data_size = equipment[idx].readout((char *) (pevent + 1), 0);
01088 
01089    /* send event */
01090    if (pevent->data_size) {
01091       if (eq_info->eq_type & EQ_FRAGMENTED) {
01092          /* fragment event */
01093          if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size_frag) {
01094             cm_msg(MERROR, "send_event",
01095                    "Event size %ld larger than maximum size %d for frag. ev.",
01096                    (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01097                    max_event_size_frag);
01098             return SS_NO_MEMORY;
01099          }
01100 
01101          /* compose fragments */
01102          pfragment = event_buffer;
01103 
01104          /* compose MIDAS event header */
01105          memcpy(pfragment, pevent, sizeof(EVENT_HEADER));
01106          pfragment->event_id |= EVENTID_FRAG1;
01107 
01108          /* store total event size */
01109          pd = (unsigned char *) (pfragment + 1);
01110          size = pevent->data_size;
01111          for (i = 0; i < 4; i++) {
01112             pd[i] = (unsigned char) (size & 0xFF);      /* little endian, please! */
01113             size >>= 8;
01114          }
01115 
01116          pfragment->data_size = sizeof(DWORD);
01117 
01118          pdata = (char *) (pevent + 1);
01119 
01120          for (i = 0, sent = 0; sent < pevent->data_size; i++) {
01121             if (i > 0) {
01122                pfragment = event_buffer;
01123 
01124                /* compose MIDAS event header */
01125                memcpy(pfragment, pevent, sizeof(EVENT_HEADER));
01126                pfragment->event_id |= EVENTID_FRAG;
01127 
01128                /* copy portion of event */
01129                size = pevent->data_size - sent;
01130                if (size > max_event_size - sizeof(EVENT_HEADER))
01131                   size = max_event_size - sizeof(EVENT_HEADER);
01132 
01133                memcpy(pfragment + 1, pdata, size);
01134                pfragment->data_size = size;
01135                sent += size;
01136                pdata += size;
01137             }
01138 
01139             /* send event to buffer */
01140             if (equipment[idx].buffer_handle) {
01141                status = rpc_send_event(equipment[idx].buffer_handle, pfragment,
01142                                        pfragment->data_size + sizeof(EVENT_HEADER), SYNC, rpc_mode);
01143                if (status != RPC_SUCCESS) {
01144                   cm_msg(MERROR, "send_event", "rpc_send_event(SYNC) error %d", status);
01145                   return status;
01146                }
01147 
01148                /* flush events from buffer */
01149                rpc_flush_event();
01150             }
01151          }
01152 
01153          if (equipment[idx].buffer_handle) {
01154             /* flush buffer cache on server side */
01155             status = bm_flush_cache(equipment[idx].buffer_handle, SYNC);
01156             if (status != BM_SUCCESS) {
01157                cm_msg(MERROR, "send_event", "bm_flush_cache(SYNC) error %d", status);
01158                return status;
01159             }
01160          }
01161       } else {
01162          /* send unfragmented event */
01163 
01164          if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size) {
01165             cm_msg(MERROR, "send_event", "Event size %ld larger than maximum size %d",
01166                    (long) (pevent->data_size + sizeof(EVENT_HEADER)), max_event_size);
01167             return SS_NO_MEMORY;
01168          }
01169 
01170          /* send event to buffer */
01171          if (equipment[idx].buffer_handle) {
01172             status = rpc_send_event(equipment[idx].buffer_handle, pevent,
01173                                     pevent->data_size + sizeof(EVENT_HEADER), SYNC, rpc_mode);
01174             if (status != BM_SUCCESS) {
01175                cm_msg(MERROR, "send_event", "bm_send_event(SYNC) error %d", status);
01176                return status;
01177             }
01178             rpc_flush_event();
01179             status = bm_flush_cache(equipment[idx].buffer_handle, SYNC);
01180             if (status != BM_SUCCESS) {
01181                cm_msg(MERROR, "send_event", "bm_flush_cache(SYNC) error %d", status);
01182                return status;
01183             }
01184          }
01185 
01186          /* send event to ODB if RO_ODB flag is set or history is on. Do not
01187             send SLOW events since the class driver does that */
01188          if ((eq_info->read_on & RO_ODB) ||
01189              (eq_info->history > 0 && (eq_info->eq_type & ~EQ_SLOW))) {
01190             update_odb(pevent, equipment[idx].hkey_variables, equipment[idx].format);
01191             equipment[idx].odb_out++;
01192          }
01193       }
01194 
01195       equipment[idx].bytes_sent += pevent->data_size + sizeof(EVENT_HEADER);
01196       equipment[idx].events_sent++;
01197 
01198       equipment[idx].stats.events_sent += equipment[idx].events_sent;
01199       equipment[idx].events_sent = 0;
01200    } else
01201       equipment[idx].serial_number--;
01202 
01203    for (i = 0; equipment[i].name[0]; i++)
01204       if (equipment[i].buffer_handle) {
01205          status = bm_flush_cache(equipment[i].buffer_handle, SYNC);
01206          if (status != BM_SUCCESS) {
01207             cm_msg(MERROR, "send_event", "bm_flush_cache(SYNC) error %d", status);
01208             return status;
01209          }
01210       }
01211 
01212    return CM_SUCCESS;
01213 }
01214 
01215 /*------------------------------------------------------------------*/
01216 
01217 void send_all_periodic_events(INT transition)
01218 {
01219    EQUIPMENT_INFO *eq_info;
01220    INT i;
01221 
01222    for (i = 0; equipment[i].name[0]; i++) {
01223       eq_info = &equipment[i].info;
01224 
01225       if (!eq_info->enabled || equipment[i].status != FE_SUCCESS)
01226          continue;
01227 
01228       if (transition == TR_START && (eq_info->read_on & RO_BOR) == 0)
01229          continue;
01230       if (transition == TR_STOP && (eq_info->read_on & RO_EOR) == 0)
01231          continue;
01232       if (transition == TR_PAUSE && (eq_info->read_on & RO_PAUSE) == 0)
01233          continue;
01234       if (transition == TR_RESUME && (eq_info->read_on & RO_RESUME) == 0)
01235          continue;
01236 
01237       send_event(i);
01238    }
01239 }
01240 
01241 /*------------------------------------------------------------------*/
01242 
01243 static int _readout_enabled_flag = 0;
01244 
01245 int readout_enabled()
01246 {
01247    return _readout_enabled_flag;
01248 }
01249 
01250 void readout_enable(BOOL flag)
01251 {
01252    _readout_enabled_flag = flag;
01253 
01254    if (interrupt_eq) {
01255       if (flag)
01256          interrupt_configure(CMD_INTERRUPT_ENABLE, 0, 0);
01257       else
01258          interrupt_configure(CMD_INTERRUPT_DISABLE, 0, 0);
01259    }
01260 
01261    if (multithread_eq) {
01262       /* readout thread might still be in readout, so wait until finished */
01263       if (flag == 0)
01264          while (readout_thread_active);
01265    }
01266 }
01267 
01268 /*------------------------------------------------------------------*/
01269 
01270 void interrupt_routine(void)
01271 {
01272    int status;
01273    EVENT_HEADER *pevent;
01274    void *p;
01275 
01276    /* get pointer for upcoming event.
01277       This is a blocking call if no space available */
01278    status = rb_get_wp(rbh1, &p, 100000);
01279 
01280    if (status == DB_SUCCESS) {
01281       pevent = (EVENT_HEADER *)p;
01282 
01283       /* compose MIDAS event header */
01284       pevent->event_id = interrupt_eq->info.event_id;
01285       pevent->trigger_mask = interrupt_eq->info.trigger_mask;
01286       pevent->data_size = 0;
01287       pevent->time_stamp = actual_time;
01288       pevent->serial_number = interrupt_eq->serial_number++;
01289 
01290       /* call user readout routine */
01291       pevent->data_size = interrupt_eq->readout((char *) (pevent + 1), 0);
01292 
01293       /* send event */
01294       if (pevent->data_size) {
01295 
01296          /* put event into ring buffer */
01297          rb_increment_wp(rbh1, sizeof(EVENT_HEADER) + pevent->data_size);
01298 
01299       } else
01300          interrupt_eq->serial_number--;
01301    }
01302 }
01303 
01304 /*------------------------------------------------------------------*/
01305 
01306 int readout_thread(void *param)
01307 {
01308    int status, source;
01309    EVENT_HEADER *pevent;
01310    void *p;
01311 
01312    p = param; /* avoid compiler warning */
01313    do {
01314       /* obtain buffer space */
01315       if (rbh1_next) // if set by user code, use it
01316          rbh1 = rbh1_next;
01317 
01318       status = rb_get_wp(rbh1, &p, 10000);
01319       if (stop_all_threads)
01320          break;
01321       assert(status == DB_SUCCESS);
01322 
01323       if (readout_enabled()) {
01324         
01325          /* indicate activity for readout_enable() */
01326          readout_thread_active = 1;
01327 
01328          /* check for new event */
01329          if ((source = poll_event(multithread_eq->info.source, multithread_eq->poll_count, FALSE)) > 0) {
01330 
01331             if (stop_all_threads)
01332                break;
01333 
01334             pevent = (EVENT_HEADER *)p;
01335             /* put source at beginning of event, will be overwritten by 
01336                user readout code, just a special feature used by some 
01337                multi-source applications */
01338             *(INT *) (pevent + 1) = source;
01339             
01340             /* compose MIDAS event header */
01341             pevent->event_id = multithread_eq->info.event_id;
01342             pevent->trigger_mask = multithread_eq->info.trigger_mask;
01343             pevent->data_size = 0;
01344             pevent->time_stamp = actual_time;
01345             pevent->serial_number = multithread_eq->serial_number++;
01346 
01347             /* call user readout routine */
01348             pevent->data_size = multithread_eq->readout((char *) (pevent + 1), 0);
01349 
01350             /* check event size */
01351             if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size) {
01352                cm_msg(MERROR, "readout_thread",
01353                         "Event size %ld larger than maximum size %d",
01354                         (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01355                         max_event_size);
01356                assert(FALSE);
01357             }
01358 
01359             if (pevent->data_size > 0) {
01360                /* put event into ring buffer */
01361                rb_increment_wp(rbh1, sizeof(EVENT_HEADER) + pevent->data_size);
01362             } else
01363                multithread_eq->serial_number--;
01364          }
01365 
01366          readout_thread_active = 0;
01367 
01368       } else // readout_enabled
01369         ss_sleep(10);
01370 
01371    } while (!stop_all_threads);
01372 
01373    readout_thread_active = 0;
01374 
01375    return 0;
01376 }
01377 
01378 /*-- Receive event from readout thread or interrupt routine --------*/
01379 
01380 int receive_trigger_event(EQUIPMENT *eq)
01381 {
01382    int status;
01383    EVENT_HEADER *prb, *pevent;
01384    void *p;
01385 
01386    status = rb_get_rp(rbh2, &p, 10);
01387    prb = (EVENT_HEADER *)p;
01388    if (status == DB_TIMEOUT)
01389       return 0;
01390 
01391    pevent = (EVENT_HEADER *)event_buffer;
01392 
01393    /* copy event from ring buffer to local buffer */
01394    memcpy(pevent, prb, prb->data_size + sizeof(EVENT_HEADER));
01395 
01396    /* free up space in ring buffer */
01397    rb_increment_rp(rbh2, sizeof(EVENT_HEADER) + prb->data_size);
01398 
01399    /* send event */
01400    if (pevent->data_size) {
01401       if (eq->buffer_handle) {
01402          /* send first event to ODB if logger writes in root format */
01403          if (pevent->serial_number == 0)
01404             if (logger_root())
01405                update_odb(pevent, eq->hkey_variables, eq->format);
01406 
01407          status = rpc_send_event(eq->buffer_handle, pevent,
01408                                  pevent->data_size + sizeof(EVENT_HEADER),
01409                                  SYNC, rpc_mode);
01410 
01411          if (status != SUCCESS) {
01412             cm_msg(MERROR, "scheduler", "rpc_send_event error %d", status);
01413             return -1;
01414          }
01415 
01416          eq->bytes_sent += pevent->data_size + sizeof(EVENT_HEADER);
01417 
01418          if (eq->info.num_subevents)
01419             eq->events_sent += eq->subevent_number;
01420          else
01421             eq->events_sent++;
01422       }
01423    }
01424 
01425    return prb->data_size;
01426 }
01427 
01428 /*------------------------------------------------------------------*/
01429 
01430 int message_print(const char *msg)
01431 {
01432    char str[160];
01433 
01434    memset(str, ' ', 159);
01435    str[159] = 0;
01436 
01437    if (msg[0] == '[')
01438       msg = strchr(msg, ']') + 2;
01439 
01440    memcpy(str, msg, strlen(msg));
01441    ss_printf(0, 20, str);
01442 
01443    return 0;
01444 }
01445 
01446 /*------------------------------------------------------------------*/
01447 
01448 void display(BOOL bInit)
01449 {
01450    INT i, status;
01451    time_t full_time;
01452    char str[30];
01453 
01454    if (bInit) {
01455       ss_clear_screen();
01456 
01457       if (host_name[0])
01458          strcpy(str, host_name);
01459       else
01460          strcpy(str, "<local>");
01461 
01462       ss_printf(0, 0, "%s connected to %s. Press \"!\" to exit", full_frontend_name, str);
01463       ss_printf(0, 1,
01464                 "================================================================================");
01465       ss_printf(0, 2, "Run status:   %s",
01466                 run_state == STATE_STOPPED ? "Stopped" : run_state ==
01467                 STATE_RUNNING ? "Running" : "Paused");
01468       ss_printf(25, 2, "Run number %d   ", run_number);
01469       ss_printf(0, 3,
01470                 "================================================================================");
01471       ss_printf(0, 4,
01472                 "Equipment     Status     Events     Events/sec Rate[kB/s] ODB->FE    FE->ODB");
01473       ss_printf(0, 5,
01474                 "--------------------------------------------------------------------------------");
01475       for (i = 0; equipment[i].name[0]; i++)
01476          ss_printf(0, i + 6, "%s", equipment[i].name);
01477    }
01478 
01479    /* display time */
01480    time(&full_time);
01481    strcpy(str, ctime(&full_time) + 11);
01482    str[8] = 0;
01483    ss_printf(72, 0, "%s", str);
01484 
01485    for (i = 0; equipment[i].name[0]; i++) {
01486       status = equipment[i].status;
01487 
01488       if ((status == 0 || status == FE_SUCCESS) && equipment[i].info.enabled)
01489          ss_printf(14, i + 6, "OK       ");
01490       else if (!equipment[i].info.enabled)
01491          ss_printf(14, i + 6, "Disabled ");
01492       else if (status == FE_ERR_ODB)
01493          ss_printf(14, i + 6, "ODB Error");
01494       else if (status == FE_ERR_HW)
01495          ss_printf(14, i + 6, "HW Error ");
01496       else if (status == FE_ERR_DISABLED)
01497          ss_printf(14, i + 6, "Disabled ");
01498       else if (status == FE_ERR_DRIVER)
01499          ss_printf(14, i + 6, "Driver err");
01500       else
01501          ss_printf(14, i + 6, "Unknown  ");
01502 
01503       if (equipment[i].stats.events_sent > 1E9)
01504          ss_printf(25, i + 6, "%1.3lfG     ", equipment[i].stats.events_sent / 1E9);
01505       else if (equipment[i].stats.events_sent > 1E6)
01506          ss_printf(25, i + 6, "%1.3lfM     ", equipment[i].stats.events_sent / 1E6);
01507       else
01508          ss_printf(25, i + 6, "%1.0lf      ", equipment[i].stats.events_sent);
01509       ss_printf(36, i + 6, "%1.1lf      ", equipment[i].stats.events_per_sec);
01510       ss_printf(47, i + 6, "%1.1lf      ", equipment[i].stats.kbytes_per_sec);
01511       ss_printf(58, i + 6, "%ld       ", equipment[i].odb_in);
01512       ss_printf(69, i + 6, "%ld       ", equipment[i].odb_out);
01513    }
01514 
01515    /* go to next line */
01516    ss_printf(0, i + 6, "");
01517 }
01518 
01519 /*------------------------------------------------------------------*/
01520 
01521 BOOL logger_root()
01522 /* check if logger uses ROOT format */
01523 {
01524    int size, i, status;
01525    char str[80];
01526    HNDLE hKeyRoot, hKey;
01527 
01528    if (db_find_key(hDB, 0, "/Logger/Channels", &hKeyRoot) == DB_SUCCESS) {
01529       for (i = 0;; i++) {
01530          status = db_enum_key(hDB, hKeyRoot, i, &hKey);
01531          if (status == DB_NO_MORE_SUBKEYS)
01532             break;
01533 
01534          strcpy(str, "MIDAS");
01535          size = sizeof(str);
01536          db_get_value(hDB, hKey, "Settings/Format", str, &size, TID_STRING, TRUE);
01537 
01538          if (equal_ustring(str, "ROOT"))
01539             return TRUE;
01540       }
01541    }
01542 
01543    return FALSE;
01544 }
01545 
01546 /*------------------------------------------------------------------*/
01547 
01548 INT check_polled_events(void)
01549 {
01550    EQUIPMENT_INFO *eq_info;
01551    EQUIPMENT *eq;
01552    EVENT_HEADER *pevent, *pfragment;
01553    DWORD readout_start, sent, size;
01554    INT i, idx, source, events_sent, status;
01555    char *pdata;
01556    unsigned char *pd;
01557 
01558    events_sent = 0;
01559 
01560    /*---- loop over equipment table -------------------------------*/
01561    for (idx = 0;; idx++) {
01562       eq = &equipment[idx];
01563       eq_info = &eq->info;
01564 
01565       /* check if end of equipment list */
01566       if (!eq->name[0])
01567          break;
01568 
01569       if (!eq_info->enabled)
01570          continue;
01571 
01572       if (eq->status != FE_SUCCESS)
01573          continue;
01574 
01575       if ((eq_info->eq_type & EQ_POLLED) == 0)
01576          continue;
01577 
01578       /*---- check polled events ----*/
01579       readout_start = actual_millitime;
01580       pevent = NULL;
01581 
01582       while ((source = poll_event(eq_info->source, eq->poll_count, FALSE)) > 0) {
01583          
01584          if (eq_info->eq_type & EQ_FRAGMENTED)
01585             pevent = frag_buffer;
01586          else
01587             pevent = (EVENT_HEADER *)event_buffer;
01588 
01589          /* compose MIDAS event header */
01590          pevent->event_id = eq_info->event_id;
01591          pevent->trigger_mask = eq_info->trigger_mask;
01592          pevent->data_size = 0;
01593          pevent->time_stamp = actual_time;
01594          pevent->serial_number = eq->serial_number;
01595 
01596          /* put source at beginning of event, will be overwritten by 
01597             user readout code, just a special feature used by some 
01598             multi-source applications */
01599          *(INT *) (pevent + 1) = source;
01600 
01601          if (eq->info.num_subevents) {
01602             eq->subevent_number = 0;
01603             do {
01604                *(INT *) ((char *) (pevent + 1) + pevent->data_size) = source;
01605 
01606                /* call user readout routine for subevent indicating offset */
01607                size = eq->readout((char *) (pevent + 1), pevent->data_size);
01608                pevent->data_size += size;
01609                if (size > 0) {
01610                   if (pevent->data_size + sizeof(EVENT_HEADER) >
01611                       (DWORD) max_event_size) {
01612                      cm_msg(MERROR, "scheduler",
01613                             "Event size %ld larger than maximum size %d",
01614                             (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01615                             max_event_size);
01616                   }
01617 
01618                   eq->subevent_number++;
01619                   eq->serial_number++;
01620                }
01621 
01622                /* wait for next event */
01623                do {
01624                   source = poll_event(eq_info->source, eq->poll_count, FALSE);
01625 
01626                   if (source == FALSE) {
01627                      actual_millitime = ss_millitime();
01628 
01629                      /* repeat no more than period */
01630                      if (actual_millitime - readout_start > (DWORD) eq_info->period)
01631                         break;
01632                   }
01633                } while (source == FALSE);
01634 
01635             } while (eq->subevent_number < eq->info.num_subevents && source);
01636 
01637             /* notify readout routine about end of super-event */
01638             pevent->data_size = eq->readout((char *) (pevent + 1), -1);
01639          } else {
01640             /* call user readout routine indicating event source */
01641             pevent->data_size = eq->readout((char *) (pevent + 1), 0);
01642 
01643             /* check event size */
01644             if (eq_info->eq_type & EQ_FRAGMENTED) {
01645                if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size_frag) {
01646                   cm_msg(MERROR, "send_event",
01647                         "Event size %ld larger than maximum size %d for frag. ev.",
01648                         (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01649                         max_event_size_frag);
01650                   assert(FALSE);
01651                }
01652             } else {
01653                if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size) {
01654                   cm_msg(MERROR, "scheduler",
01655                         "Event size %ld larger than maximum size %d",
01656                         (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01657                         max_event_size);
01658                   assert(FALSE);
01659                }
01660             }
01661 
01662             /* increment serial number if event read out sucessfully */
01663             if (pevent->data_size)
01664                eq->serial_number++;
01665          }
01666 
01667          /* send event */
01668          if (pevent->data_size) {
01669 
01670             /* check for fragmented event */
01671             if (eq_info->eq_type & EQ_FRAGMENTED) {
01672                /* compose fragments */
01673                pfragment = event_buffer;
01674 
01675                /* compose MIDAS event header */
01676                memcpy(pfragment, pevent, sizeof(EVENT_HEADER));
01677                pfragment->event_id |= EVENTID_FRAG1;
01678 
01679                /* store total event size */
01680                pd = (unsigned char *) (pfragment + 1);
01681                size = pevent->data_size;
01682                for (i = 0; i < 4; i++) {
01683                   pd[i] = (unsigned char) (size & 0xFF);      /* little endian, please! */
01684                   size >>= 8;
01685                }
01686 
01687                pfragment->data_size = sizeof(DWORD);
01688 
01689                pdata = (char *) (pevent + 1);
01690 
01691                for (i = 0, sent = 0; sent < pevent->data_size; i++) {
01692                   if (i > 0) {
01693                      pfragment = event_buffer;
01694 
01695                      /* compose MIDAS event header */
01696                      memcpy(pfragment, pevent, sizeof(EVENT_HEADER));
01697                      pfragment->event_id |= EVENTID_FRAG;
01698 
01699                      /* copy portion of event */
01700                      size = pevent->data_size - sent;
01701                      if (size > max_event_size - sizeof(EVENT_HEADER))
01702                         size = max_event_size - sizeof(EVENT_HEADER);
01703 
01704                      memcpy(pfragment + 1, pdata, size);
01705                      pfragment->data_size = size;
01706                      sent += size;
01707                      pdata += size;
01708                   }
01709 
01710                   /* send event to buffer */
01711                   if (equipment[idx].buffer_handle) {
01712                      status = rpc_send_event(equipment[idx].buffer_handle, pfragment,
01713                                              pfragment->data_size + sizeof(EVENT_HEADER), SYNC, rpc_mode);
01714                      if (status != RPC_SUCCESS) {
01715                         cm_msg(MERROR, "send_event", "rpc_send_event(SYNC) error %d", status);
01716                         return status;
01717                      }
01718 
01719                      /* flush events from buffer */
01720                      rpc_flush_event();
01721                   }
01722                }
01723             
01724             } else { /*-------------------*/
01725 
01726                /* send unfragmented event */
01727 
01728                /* send first event to ODB if logger writes in root format */
01729                if (pevent->serial_number == 0)
01730                   if (logger_root())
01731                      update_odb(pevent, eq->hkey_variables, eq->format);
01732 
01733                status = rpc_send_event(eq->buffer_handle, pevent,
01734                                        pevent->data_size + sizeof(EVENT_HEADER),
01735                                        SYNC, rpc_mode);
01736 
01737                if (status != SUCCESS) {
01738                   cm_msg(MERROR, "scheduler", "rpc_send_event error %d", status);
01739                   break;
01740                }
01741             }
01742 
01743             eq->bytes_sent += pevent->data_size + sizeof(EVENT_HEADER);
01744 
01745             if (eq->info.num_subevents) {
01746                eq->events_sent += eq->subevent_number;
01747                events_sent += eq->subevent_number;
01748             } else {
01749                eq->events_sent++;
01750                events_sent++;
01751             }
01752          }
01753 
01754          actual_millitime = ss_millitime();
01755 
01756          /* repeat no more than period */
01757          if (actual_millitime - readout_start > (DWORD) eq_info->period)
01758             break;
01759 
01760          /* quit if event limit is reached */
01761          if (eq_info->event_limit > 0 &&
01762              eq->stats.events_sent + eq->events_sent >= eq_info->event_limit)
01763             break;
01764       }
01765    }
01766 
01767    return events_sent;
01768 }
01769 
01770 /*------------------------------------------------------------------*/
01771 
01772 INT scheduler(void)
01773 {
01774    EQUIPMENT_INFO *eq_info;
01775    EQUIPMENT *eq;
01776    EVENT_HEADER *pevent, *pfragment;
01777    DWORD last_time_network = 0, last_time_display = 0, last_time_flush = 0, 
01778       readout_start, sent, size;
01779    INT i, j, idx, status = 0, ch, source, state, old_flag;
01780    char str[80], *pdata;
01781    unsigned char *pd;
01782    BOOL buffer_done, flag, force_update = FALSE;
01783 
01784    INT opt_max = 0, opt_index = 0, opt_tcp_size = 128, opt_cnt = 0;
01785    INT err;
01786 
01787 #ifdef OS_VXWORKS
01788    rpc_set_opt_tcp_size(1024);
01789 #ifdef PPCxxx
01790    rpc_set_opt_tcp_size(NET_TCP_SIZE);
01791 #endif
01792 #endif
01793 
01794    /*----------------- MAIN equipment loop ------------------------------*/
01795 
01796    do {
01797       actual_millitime = ss_millitime();
01798       actual_time = ss_time();
01799 
01800       /*---- loop over equipment table -------------------------------*/
01801       for (idx = 0;; idx++) {
01802          eq = &equipment[idx];
01803          eq_info = &eq->info;
01804 
01805          /* check if end of equipment list */
01806          if (!eq->name[0])
01807             break;
01808 
01809          if (!eq_info->enabled)
01810             continue;
01811 
01812          if (eq->status != FE_SUCCESS)
01813             continue;
01814 
01815          /*---- call idle routine for slow control equipment ----*/
01816          if ((eq_info->eq_type & EQ_SLOW) && eq->status == FE_SUCCESS) {
01817             /* if equipment is multi-threaded, read all channel in one loop */
01818              
01819             if (eq_info->event_limit > 0) {
01820                if (actual_millitime - eq->last_idle >= (DWORD) eq_info->event_limit) {
01821                   eq->cd(CMD_IDLE, eq);
01822                   eq->last_idle = actual_millitime;
01823                }
01824             } else
01825                eq->cd(CMD_IDLE, eq);
01826          }
01827 
01828          if (run_state == STATE_STOPPED && (eq_info->read_on & RO_STOPPED) == 0)
01829             continue;
01830          if (run_state == STATE_PAUSED && (eq_info->read_on & RO_PAUSED) == 0)
01831             continue;
01832          if (run_state == STATE_RUNNING && (eq_info->read_on & RO_RUNNING) == 0)
01833             continue;
01834 
01835          /*---- check periodic events ----*/
01836          if ((eq_info->eq_type & EQ_PERIODIC) || (eq_info->eq_type & EQ_SLOW)) {
01837             if (eq_info->period == 0)
01838                continue;
01839 
01840             /* check if period over */
01841             if (actual_millitime - eq->last_called >= (DWORD) eq_info->period) {
01842                /* disable interrupts or readout thread during this event */
01843                old_flag = readout_enabled();
01844                if (old_flag)
01845                   readout_enable(FALSE);
01846 
01847                /* readout and send event */
01848                status = send_event(idx);
01849 
01850                if (status != CM_SUCCESS) {
01851                   cm_msg(MERROR, "scheduler", "send_event error %d", status);
01852                   goto net_error;
01853                }
01854 
01855                /* re-enable the interrupt or readout thread after periodic */
01856                if (old_flag)
01857                   readout_enable(TRUE);
01858             }
01859          }
01860 
01861          /*---- check polled events ----*/
01862          if (eq_info->eq_type & EQ_POLLED) {
01863             readout_start = actual_millitime;
01864             pevent = NULL;
01865 
01866             while ((source = poll_event(eq_info->source, eq->poll_count, FALSE)) > 0) {
01867                
01868                if (eq_info->eq_type & EQ_FRAGMENTED)
01869                   pevent = frag_buffer;
01870                else
01871                   pevent = (EVENT_HEADER *)event_buffer;
01872 
01873                /* compose MIDAS event header */
01874                pevent->event_id = eq_info->event_id;
01875                pevent->trigger_mask = eq_info->trigger_mask;
01876                pevent->data_size = 0;
01877                pevent->time_stamp = actual_time;
01878                pevent->serial_number = eq->serial_number;
01879 
01880                /* put source at beginning of event, will be overwritten by 
01881                   user readout code, just a special feature used by some 
01882                   multi-source applications */
01883                *(INT *) (pevent + 1) = source;
01884 
01885                if (eq->info.num_subevents) {
01886                   eq->subevent_number = 0;
01887                   do {
01888                      *(INT *) ((char *) (pevent + 1) + pevent->data_size) = source;
01889 
01890                      /* call user readout routine for subevent indicating offset */
01891                      size = eq->readout((char *) (pevent + 1), pevent->data_size);
01892                      pevent->data_size += size;
01893                      if (size > 0) {
01894                         if (pevent->data_size + sizeof(EVENT_HEADER) >
01895                             (DWORD) max_event_size) {
01896                            cm_msg(MERROR, "scheduler",
01897                                   "Event size %ld larger than maximum size %d",
01898                                   (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01899                                   max_event_size);
01900                         }
01901 
01902                         eq->subevent_number++;
01903                         eq->serial_number++;
01904                      }
01905 
01906                      /* wait for next event */
01907                      do {
01908                         source = poll_event(eq_info->source, eq->poll_count, FALSE);
01909 
01910                         if (source == FALSE) {
01911                            actual_millitime = ss_millitime();
01912 
01913                            /* repeat no more than period */
01914                            if (actual_millitime - readout_start > (DWORD) eq_info->period)
01915                               break;
01916                         }
01917                      } while (source == FALSE);
01918 
01919                   } while (eq->subevent_number < eq->info.num_subevents && source);
01920 
01921                   /* notify readout routine about end of super-event */
01922                   pevent->data_size = eq->readout((char *) (pevent + 1), -1);
01923                } else {
01924                   /* call user readout routine indicating event source */
01925                   pevent->data_size = eq->readout((char *) (pevent + 1), 0);
01926 
01927                   /* check event size */
01928                   if (eq_info->eq_type & EQ_FRAGMENTED) {
01929                      if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size_frag) {
01930                         cm_msg(MERROR, "send_event",
01931                               "Event size %ld larger than maximum size %d for frag. ev.",
01932                               (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01933                               max_event_size_frag);
01934                         assert(FALSE);
01935                      }
01936                   } else {
01937                      if (pevent->data_size + sizeof(EVENT_HEADER) > (DWORD) max_event_size) {
01938                         cm_msg(MERROR, "scheduler",
01939                               "Event size %ld larger than maximum size %d",
01940                               (long) (pevent->data_size + sizeof(EVENT_HEADER)),
01941                               max_event_size);
01942                         assert(FALSE);
01943                      }
01944                   }
01945 
01946                   /* increment serial number if event read out sucessfully */
01947                   if (pevent->data_size)
01948                      eq->serial_number++;
01949                }
01950 
01951                /* send event */
01952                if (pevent->data_size) {
01953 
01954                   /* check for fragmented event */
01955                   if (eq_info->eq_type & EQ_FRAGMENTED) {
01956                      /* compose fragments */
01957                      pfragment = event_buffer;
01958 
01959                      /* compose MIDAS event header */
01960                      memcpy(pfragment, pevent, sizeof(EVENT_HEADER));
01961                      pfragment->event_id |= EVENTID_FRAG1;
01962 
01963                      /* store total event size */
01964                      pd = (unsigned char *) (pfragment + 1);
01965                      size = pevent->data_size;
01966                      for (i = 0; i < 4; i++) {
01967                         pd[i] = (unsigned char) (size & 0xFF);      /* little endian, please! */
01968                         size >>= 8;
01969                      }
01970 
01971                      pfragment->data_size = sizeof(DWORD);
01972 
01973                      pdata = (char *) (pevent + 1);
01974 
01975                      for (i = 0, sent = 0; sent < pevent->data_size; i++) {
01976                         if (i > 0) {
01977                            pfragment = event_buffer;
01978 
01979                            /* compose MIDAS event header */
01980                            memcpy(pfragment, pevent, sizeof(EVENT_HEADER));
01981                            pfragment->event_id |= EVENTID_FRAG;
01982 
01983                            /* copy portion of event */
01984                            size = pevent->data_size - sent;
01985                            if (size > max_event_size - sizeof(EVENT_HEADER))
01986                               size = max_event_size - sizeof(EVENT_HEADER);
01987 
01988                            memcpy(pfragment + 1, pdata, size);
01989                            pfragment->data_size = size;
01990                            sent += size;
01991                            pdata += size;
01992                         }
01993 
01994                         /* send event to buffer */
01995                         if (equipment[idx].buffer_handle) {
01996                            status = rpc_send_event(equipment[idx].buffer_handle, pfragment,
01997                                                    pfragment->data_size + sizeof(EVENT_HEADER), SYNC, rpc_mode);
01998                            if (status != RPC_SUCCESS) {
01999                               cm_msg(MERROR, "send_event", "rpc_send_event(SYNC) error %d", status);
02000                               return status;
02001                            }
02002 
02003                            /* flush events from buffer */
02004                            rpc_flush_event();
02005                         }
02006                      }
02007                   
02008                   } else { /*-------------------*/
02009 
02010                      /* send unfragmented event */
02011 
02012                      /* send first event to ODB if logger writes in root format */
02013                      if (pevent->serial_number == 0)
02014                         if (logger_root())
02015                            update_odb(pevent, eq->hkey_variables, eq->format);
02016 
02017                      status = rpc_send_event(eq->buffer_handle, pevent,
02018                                              pevent->data_size + sizeof(EVENT_HEADER),
02019                                              SYNC, rpc_mode);
02020 
02021                      if (status != SUCCESS) {
02022                         cm_msg(MERROR, "scheduler", "rpc_send_event error %d", status);
02023                         goto net_error;
02024                      }
02025                   }
02026 
02027                   eq->bytes_sent += pevent->data_size + sizeof(EVENT_HEADER);
02028 
02029                   if (eq->info.num_subevents)
02030                      eq->events_sent += eq->subevent_number;
02031                   else
02032                      eq->events_sent++;
02033                }
02034 
02035                actual_millitime = ss_millitime();
02036 
02037                /* repeat no more than period */
02038                if (actual_millitime - readout_start > (DWORD) eq_info->period)
02039                   break;
02040 
02041                /* quit if event limit is reached */
02042                if (eq_info->event_limit > 0 &&
02043                    eq->stats.events_sent + eq->events_sent >= eq_info->event_limit)
02044                   break;
02045             }
02046 
02047          }
02048 
02049          /*---- send interrupt events ----*/
02050          if (eq_info->eq_type & (EQ_INTERRUPT | EQ_MULTITHREAD)) {
02051             readout_start = actual_millitime;
02052 
02053             do {
02054                size = receive_trigger_event(eq);
02055                if ((int)size == -1)
02056                   goto net_error;
02057 
02058                actual_millitime = ss_millitime();
02059 
02060                /* repeat no more than period */
02061                if (actual_millitime - readout_start > (DWORD) eq_info->period)
02062                   break;
02063 
02064                /* quit if event limit is reached */
02065                if (eq_info->event_limit > 0 &&
02066                    eq->stats.events_sent + eq->events_sent >= eq_info->event_limit)
02067                   break;
02068 
02069             } while (size > 0);
02070 
02071             /* send event to ODB */
02072             pevent = (EVENT_HEADER *)event_buffer;
02073             if (size > 0 && pevent->data_size && (eq_info->read_on & RO_ODB || eq_info->history)) {
02074                if (actual_millitime - eq->last_called > ODB_UPDATE_TIME && pevent != NULL) {
02075                   eq->last_called = actual_millitime;
02076                   update_odb(pevent, eq->hkey_variables, eq->format);
02077                   eq->odb_out++;
02078                }
02079             }
02080          }
02081 
02082          /*---- check if event limit is reached ----*/
02083          if (eq_info->eq_type != EQ_SLOW &&
02084              eq_info->event_limit > 0 &&
02085              eq->stats.events_sent + eq->events_sent >= eq_info->event_limit &&
02086              run_state == STATE_RUNNING) {
02087             /* stop run */
02088             if (cm_transition(TR_STOP, 0, str, sizeof(str), SYNC, FALSE) != CM_SUCCESS)
02089                cm_msg(MERROR, "scheduler", "cannot stop run: %s", str);
02090 
02091             /* check if autorestart, main loop will take care of it */
02092             size = sizeof(BOOL);
02093             flag = FALSE;
02094             db_get_value(hDB, 0, "/Logger/Auto restart", &flag, &size, TID_BOOL, TRUE);
02095 
02096             if (flag)
02097                auto_restart = ss_time() + 20;   /* restart in 20 sec. */
02098 
02099             /* update event display correctly */
02100             force_update = TRUE;
02101          }
02102       }
02103 
02104       /*---- call frontend_loop periodically -------------------------*/
02105       if (frontend_call_loop) {
02106          status = frontend_loop();
02107          if (status == RPC_SHUTDOWN || status == SS_ABORT) {
02108             status = RPC_SHUTDOWN;
02109             break;
02110          }
02111       }
02112 
02113       /*---- check for deferred transitions --------------------------*/
02114       cm_check_deferred_transition();
02115 
02116       /*---- check for manual triggered events -----------------------*/
02117       if (manual_trigger_event_id) {
02118          old_flag = readout_enabled();
02119          if (old_flag)
02120             readout_enable(FALSE);
02121 
02122          /* readout and send event */
02123          status = BM_INVALID_PARAM;
02124          for (i = 0; equipment[i].name[0]; i++)
02125             if (equipment[i].info.event_id == manual_trigger_event_id) {
02126                status = send_event(i);
02127                break;
02128             }
02129 
02130          manual_trigger_event_id = 0;
02131 
02132          if (status != CM_SUCCESS) {
02133             cm_msg(MERROR, "scheduler", "send_event error %d", status);
02134             goto net_error;
02135          }
02136 
02137          /* re-enable the interrupt after periodic */
02138          if (old_flag)
02139             readout_enable(TRUE);
02140       }
02141 
02142       /*---- calculate rates and update status page periodically -----*/
02143       if (force_update ||
02144           (display_period
02145            && actual_millitime - last_time_display > (DWORD) display_period)
02146           || (!display_period && actual_millitime - last_time_display > 3000)) {
02147          force_update = FALSE;
02148 
02149          /* calculate rates */
02150          if (actual_millitime != last_time_display) {
02151             max_bytes_per_sec = 0;
02152             for (i = 0; equipment[i].name[0]; i++) {
02153                eq = &equipment[i];
02154                eq->stats.events_sent += eq->events_sent;
02155                eq->stats.events_per_sec =
02156                    eq->events_sent / ((actual_millitime - last_time_display) / 1000.0);
02157                eq->stats.kbytes_per_sec =
02158                    eq->bytes_sent / 1024.0 / ((actual_millitime - last_time_display) /
02159                                               1000.0);
02160 
02161                if ((INT) eq->bytes_sent > max_bytes_per_sec)
02162                   max_bytes_per_sec = eq->bytes_sent;
02163 
02164                eq->bytes_sent = 0;
02165                eq->events_sent = 0;
02166             }
02167 
02168             max_bytes_per_sec = (DWORD)
02169                 ((double) max_bytes_per_sec /
02170                  ((actual_millitime - last_time_display) / 1000.0));
02171 
02172             /* tcp buffer size evaluation */
02173             if (optimize) {
02174                opt_max = MAX(opt_max, (INT) max_bytes_per_sec);
02175                ss_printf(0, opt_index, "%6d : %5.1lf %5.1lf", opt_tcp_size,
02176                          opt_max / 1024.0, max_bytes_per_sec / 1024.0);
02177                if (++opt_cnt == 10) {
02178                   opt_cnt = 0;
02179                   opt_max = 0;
02180                   opt_index++;
02181                   opt_tcp_size = 1 << (opt_index + 7);
02182                   rpc_set_opt_tcp_size(opt_tcp_size);
02183                   if (1 << (opt_index + 7) > 0x8000) {
02184                      opt_index = 0;
02185                      opt_tcp_size = 1 << 7;
02186                      rpc_set_opt_tcp_size(opt_tcp_size);
02187                   }
02188                }
02189             }
02190 
02191          }
02192 
02193          /* propagate changes in equipment to ODB */
02194          rpc_set_option(-1, RPC_OTRANSPORT, RPC_FTCP);
02195          db_send_changed_records();
02196          rpc_set_option(-1, RPC_OTRANSPORT, RPC_TCP);
02197 
02198          if (display_period) {
02199             display(FALSE);
02200 
02201             /* check keyboard */
02202             ch = 0;
02203             status = 0;
02204             while (ss_kbhit()) {
02205                ch = ss_getchar(0);
02206                if (ch == -1)
02207                   ch = getchar();
02208 
02209                if (ch == '!')
02210                   status = RPC_SHUTDOWN;
02211             }
02212 
02213             if (ch > 0)
02214                display(TRUE);
02215             if (status == RPC_SHUTDOWN)
02216                break;
02217          }
02218 
02219          last_time_display = actual_millitime;
02220       }
02221 
02222       /*---- check to flush cache ------------------------------------*/
02223       if (actual_millitime - last_time_flush > 1000) {
02224          last_time_flush = actual_millitime;
02225 
02226          /* if cache on server is not filled in one second at current
02227             data rate, flush it now to make events available to consumers */
02228 
02229          if (max_bytes_per_sec < SERVER_CACHE_SIZE) {
02230             old_flag = readout_enabled();
02231             if (old_flag)
02232                readout_enable(FALSE);
02233 
02234             for (i = 0; equipment[i].name[0]; i++) {
02235                if (equipment[i].buffer_handle) {
02236                   /* check if buffer already flushed */
02237                   buffer_done = FALSE;
02238                   for (j = 0; j < i; j++)
02239                      if (equipment[i].buffer_handle == equipment[j].buffer_handle) {
02240                         buffer_done = TRUE;
02241                         break;
02242                      }
02243 
02244                   if (!buffer_done) {
02245                      rpc_set_option(-1, RPC_OTRANSPORT, RPC_FTCP);
02246                      rpc_flush_event();
02247                      err = bm_flush_cache(equipment[i].buffer_handle, ASYNC);
02248                      if ((err != BM_SUCCESS) && (err != BM_ASYNC_RETURN)) {
02249                         cm_msg(MERROR, "scheduler", "bm_flush_cache(ASYNC) error %d",
02250                                err);
02251                         return err;
02252                      }
02253                      rpc_set_option(-1, RPC_OTRANSPORT, RPC_TCP);
02254                   }
02255                }
02256             }
02257 
02258             if (old_flag)
02259                readout_enable(TRUE);
02260          }
02261       }
02262 
02263       /*---- check for auto restart --------------------------------*/
02264       if (auto_restart > 0 && ss_time() > auto_restart) {
02265          /* check if really stopped */
02266          size = sizeof(state);
02267          status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
02268          if (status != DB_SUCCESS)
02269             cm_msg(MERROR, "scheduler", "cannot get Runinfo/State in database");
02270 
02271          if (state == STATE_STOPPED) {
02272             auto_restart = 0;
02273             size = sizeof(run_number);
02274             status =
02275                 db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT,
02276                              TRUE);
02277             assert(status == SUCCESS);
02278 
02279             if (run_number <= 0) {
02280                cm_msg(MERROR, "main", "aborting on attempt to use invalid run number %d",
02281                       run_number);
02282                abort();
02283             }
02284 
02285             cm_msg(MTALK, "main", "starting new run");
02286             status = cm_transition(TR_START, run_number + 1, NULL, 0, SYNC, FALSE);
02287             if (status != CM_SUCCESS)
02288                cm_msg(MERROR, "main", "cannot restart run");
02289          }
02290       }
02291 
02292       /*---- check network messages ----------------------------------*/
02293       if ((run_state == STATE_RUNNING && interrupt_eq == NULL) || slowcont_eq) {
02294          /* only call yield once every 100ms when running */
02295          if (actual_millitime - last_time_network > 100) {
02296             status = cm_yield(0);
02297             last_time_network = actual_millitime;
02298          } else
02299             status = RPC_SUCCESS;
02300       } else
02301          /* when run is stopped or interrupts used, 
02302             call yield with 100ms timeout */
02303          status = cm_yield(100);
02304 
02305       /* exit for VxWorks */
02306       if (fe_stop)
02307          status = RPC_SHUTDOWN;
02308 
02309       /* exit if CTRL-C pressed */
02310       if (cm_is_ctrlc_pressed())
02311          status = RPC_SHUTDOWN;
02312 
02313    } while (status != RPC_SHUTDOWN && status != SS_ABORT);
02314 
02315  net_error:
02316 
02317    return status;
02318 }
02319 
02320 /*------------------------------------------------------------------*/
02321 
02322 INT get_frontend_index()
02323 {
02324    return frontend_index;
02325 }
02326 
02327 /*------------------------------------------------------------------*/
02328 
02329 #ifdef OS_VXWORKS
02330 int mfe(char *ahost_name, char *aexp_name, BOOL adebug)
02331 #else
02332 int main(int argc, char *argv[])
02333 #endif
02334 {
02335    INT status, i, j;
02336    INT daemon_flag;
02337 
02338    host_name[0] = 0;
02339    exp_name[0] = 0;
02340    debug = FALSE;
02341    daemon_flag = 0;
02342 
02343    setbuf(stdout, 0);
02344    setbuf(stderr, 0);
02345 
02346 #ifdef SIGPIPE
02347    signal(SIGPIPE, SIG_IGN);
02348 #endif
02349 
02350 #ifdef OS_VXWORKS
02351    if (ahost_name)
02352       strcpy(host_name, ahost_name);
02353    if (aexp_name)
02354       strcpy(exp_name, aexp_name);
02355    debug = adebug;
02356 #else
02357 
02358    /* get default from environment */
02359    cm_get_environment(host_name, sizeof(host_name), exp_name, sizeof(exp_name));
02360 
02361    /* parse command line parameters */
02362    for (i = 1; i < argc; i++) {
02363       if (argv[i][0] == '-' && argv[i][1] == 'd')
02364          debug = TRUE;
02365       else if (argv[i][0] == '-' && argv[i][1] == 'D')
02366          daemon_flag = 1;
02367       else if (argv[i][0] == '-' && argv[i][1] == 'O')
02368          daemon_flag = 2;
02369       else if (argv[i][0] == '-') {
02370          if (i + 1 >= argc || argv[i + 1][0] == '-')
02371             goto usage;
02372          if (argv[i][1] == 'e')
02373             strcpy(exp_name, argv[++i]);
02374          else if (argv[i][1] == 'h')
02375             strcpy(host_name, argv[++i]);
02376          else if (argv[i][1] == 'i')
02377             frontend_index = atoi(argv[++i]);
02378          else {
02379           usage:
02380             printf
02381                 ("usage: frontend [-h Hostname] [-e Experiment] [-d] [-D] [-O] [-i n]\n");
02382             printf("         [-d]     Used to debug the frontend\n");
02383             printf("         [-D]     Become a daemon\n");
02384             printf("         [-O]     Become a daemon but keep stdout\n");
02385             printf("         [-i n]   Set frontend index (used for event building)\n");
02386             return 0;
02387          }
02388       }
02389    }
02390 #endif
02391 
02392    /* check event and buffer sizes */
02393    if (event_buffer_size < 2 * max_event_size) {
02394       cm_msg(MERROR, "mainFE", "event_buffer_size too small for max. event size\n");
02395       ss_sleep(5000);
02396       return 1;
02397    }
02398 
02399    if (max_event_size > MAX_EVENT_SIZE) {
02400       cm_msg(MERROR, "mainFE", "Requested max_event_size (%d) exceeds max. system event size (%d)",
02401              max_event_size, MAX_EVENT_SIZE);
02402       ss_sleep(5000);
02403       return 1;
02404    }
02405 
02406 #ifdef OS_VXWORKS
02407    /* override event_buffer_size in case of VxWorks
02408       take remaining free memory and use 20% of it for rb_ */
02409    event_buffer_size = 2 * 10 * (max_event_size + sizeof(EVENT_HEADER) + sizeof(INT));
02410    if (event_buffer_size > memFindMax()) {
02411       cm_msg(MERROR, "mainFE", "Not enough mem space for event size");
02412       return 0;
02413    }
02414    /* takes overall 20% of the available memory resource for rb_() */
02415    event_buffer_size = 0.2 * memFindMax();
02416 #endif
02417 
02418    /* retrieve frontend index from environment if defined */
02419    if (getenv("MIDAS_FRONTEND_INDEX"))
02420       frontend_index = atoi(getenv("MIDAS_FRONTEND_INDEX"));
02421 
02422    /* add frontend index to frontend name if present */
02423    strcpy(full_frontend_name, frontend_name);
02424    if (frontend_index >= 0)
02425       sprintf(full_frontend_name + strlen(full_frontend_name), "%02d", frontend_index);
02426 
02427    /* inform user of settings */
02428    printf("Frontend name          :     %s\n", full_frontend_name);
02429    printf("Event buffer size      :     %d\n", event_buffer_size);
02430    printf("System max event size  :     %d\n", MAX_EVENT_SIZE);
02431    printf("User max event size    :     %d\n", max_event_size);
02432    if (max_event_size_frag > 0)
02433       printf("User max frag. size    :     %d\n", max_event_size_frag);
02434    printf("# of events per buffer :     %d\n\n", event_buffer_size / max_event_size);
02435 
02436    if (daemon_flag) {
02437       printf("\nBecoming a daemon...\n");
02438       ss_daemon_init(daemon_flag == 2);
02439    }
02440 
02441    /* now connect to server */
02442    if (display_period) {
02443       if (host_name[0]) {
02444          if (exp_name[0])
02445             printf("Connect to experiment %s on host %s...\n", exp_name, host_name);
02446          else
02447             printf("Connect to experiment on host %s...\n", host_name);
02448       } else if (exp_name[0])
02449          printf("Connect to experiment %s...\n", exp_name);
02450       else
02451          printf("Connect to experiment...\n");
02452    }
02453 
02454    status = cm_connect_experiment1(host_name, exp_name, full_frontend_name,
02455                                    NULL, DEFAULT_ODB_SIZE, DEFAULT_FE_TIMEOUT);
02456    if (status != CM_SUCCESS) {
02457       /* let user read message before window might close */
02458       ss_sleep(5000);
02459       return 1;
02460    }
02461 
02462    if (display_period)
02463       printf("OK\n");
02464 
02465    /* allocate buffer space */
02466    event_buffer = malloc(max_event_size);
02467    if (event_buffer == NULL) {
02468       cm_msg(MERROR, "mainFE", "mfe: Not enough memory or event too big\n");
02469       return 1;
02470    }
02471 
02472    /* remomve any dead frontend */
02473    cm_cleanup(full_frontend_name, FALSE);
02474 
02475    /* shutdown previous frontend */
02476    status = cm_shutdown(full_frontend_name, FALSE);
02477    if (status == CM_SUCCESS && display_period) {
02478       printf("Previous frontend stopped\n");
02479 
02480       /* let user read message */
02481       ss_sleep(3000);
02482    }
02483 
02484    /* register transition callbacks */
02485    if (cm_register_transition(TR_START, tr_start, 500) != CM_SUCCESS ||
02486        cm_register_transition(TR_STOP, tr_stop, 500) != CM_SUCCESS ||
02487        cm_register_transition(TR_PAUSE, tr_pause, 500) != CM_SUCCESS ||
02488        cm_register_transition(TR_RESUME, tr_resume, 500) != CM_SUCCESS) {
02489       printf("Failed to start local RPC server");
02490       cm_disconnect_experiment();
02491 
02492       /* let user read message before window might close */
02493       ss_sleep(5000);
02494       return 1;
02495    }
02496 
02497    cm_get_experiment_database(&hDB, &status);
02498    /* set time from server */
02499 #ifdef OS_VXWORKS
02500    cm_synchronize(NULL);
02501 #endif
02502 
02503    /* turn off watchdog if in debug mode */
02504    if (debug)
02505       cm_set_watchdog_params(FALSE, 0);
02506 
02507    /* increase RPC timeout to 2min for logger with exabyte or blocked disk */
02508    rpc_set_option(-1, RPC_OTIMEOUT, 120000);
02509 
02510    /* set own message print function */
02511    if (display_period)
02512       cm_set_msg_print(MT_ALL, MT_ALL, message_print);
02513 
02514    /* call user init function */
02515    if (display_period)
02516       printf("Init hardware...\n");
02517    if (frontend_init() != SUCCESS) {
02518       if (display_period)
02519          printf("\n");
02520       cm_disconnect_experiment();
02521 
02522       /* let user read message before window might close */
02523       ss_sleep(5000);
02524       return 1;
02525    }
02526 
02527    /* reqister equipment in ODB */
02528    if (register_equipment() != SUCCESS) {
02529       if (display_period)
02530          printf("\n");
02531       cm_disconnect_experiment();
02532 
02533       /* let user read message before window might close */
02534       ss_sleep(5000);
02535       return 1;
02536    }
02537 
02538    if (display_period)
02539       printf("OK\n");
02540 
02541    /* initialize screen display */
02542    if (display_period) {
02543       ss_sleep(1000);
02544       display(TRUE);
02545    }
02546 
02547    /* switch on interrupts or readout thread if running */
02548    if (run_state == STATE_RUNNING)
02549       readout_enable(TRUE);
02550 
02551    /* initialize ss_getchar */
02552    ss_getchar(0);
02553 
02554    /* call main scheduler loop */
02555    status = scheduler();
02556 
02557    /* stop readout thread */
02558    stop_all_threads = 1;
02559    rb_set_nonblocking();
02560    while (readout_thread_active)
02561       ss_sleep(100);
02562 
02563    /* reset terminal */
02564    ss_getchar(TRUE);
02565 
02566    /* switch off interrupts */
02567    if (interrupt_eq) {
02568       interrupt_configure(CMD_INTERRUPT_DISABLE, 0, 0);
02569       interrupt_configure(CMD_INTERRUPT_DETACH, 0, 0);
02570    }
02571 
02572    /* detach interrupts */
02573    if (interrupt_eq != NULL)
02574       interrupt_configure(CMD_INTERRUPT_DETACH, interrupt_eq->info.source, 0);
02575 
02576    /* call user exit function */
02577    frontend_exit();
02578 
02579    /* close slow control drivers */
02580    for (i = 0; equipment[i].name[0]; i++)
02581       if ((equipment[i].info.eq_type & EQ_SLOW) && equipment[i].status == FE_SUCCESS) {
02582 
02583          for (j = 0; equipment[i].driver[j].name[0]; j++)
02584             if (equipment[i].driver[j].flags & DF_MULTITHREAD)
02585                break;
02586 
02587          /* stop all threads if multithreaded */
02588          if (equipment[i].driver[j].name[0] && equipment[i].status == FE_SUCCESS)
02589             equipment[i].cd(CMD_STOP, &equipment[i]);   /* stop all threads */
02590       }
02591    for (i = 0; equipment[i].name[0]; i++)
02592       if ((equipment[i].info.eq_type & EQ_SLOW) && equipment[i].status == FE_SUCCESS)
02593          equipment[i].cd(CMD_EXIT, &equipment[i]);      /* close physical connections */
02594 
02595    /* close network connection to server */
02596    cm_disconnect_experiment();
02597 
02598    if (display_period) {
02599       if (status == RPC_SHUTDOWN) {
02600          ss_clear_screen();
02601          ss_printf(0, 0, "Frontend shut down.");
02602          ss_printf(0, 1, "");
02603       }
02604    }
02605 
02606    if (status != RPC_SHUTDOWN)
02607       printf("Network connection aborted.\n");
02608 
02609    return 0;
02610 }

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