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

Midas DOC Version 2.0.1 ---- 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