/* *************************************************** * File: /home/lando/slentz/sync/unix_gsd_sync.c * Modifications: * 09/18/90 ges creation * 09/21/90 ges desk check * 10/15/90 ges add gsd_copy_buffer routine * 10/17/90 ges clean-up and add to documentation * add timeout parameter to gsd_sync_read * 11/01/90 ges add gsd_sync_clear function * 11/05/90 ges add linked list event data approach * 11/16/90 ges make special UNIX version for OPI * 11/20/90 ges make gsd_sync_init return VOID * for consistency * between UNIX and VxWorks versions * 11/26/90 ges use ca_build_and_connect in gsd_sync_init * 11/27/90 ges add NULL ptr return 0 to gsd_tss_eq * add NULL ptr return 0 to gsd_tss_gtr * change from (gsd_tss_gtr) to (gsd_tss_gtr || * gsd_tss_eq) in gsd_findlargest_ts * 12/14/90 ges add events at gsd_sync_read instead of gsd_sync_init * return most recent chan data if chan failed to * produce data with the associated time stamp * 09/03/91 joh fixed includes for V5 vxWorks * 06/26/92 joh now uses ca_printf instead of printf * * * Compile: * for Unix: * compile: * make * execute: * unix_gsd_sync *------------------------------------------------- * Usage: * 1) A single initial call to gsd_sync_init must be made before * any additional calls to gsd_sync_read". * 2) Calls to UNIX version of "gsd_sync_read": * 1) returns 1 if done, either timed out or has valid * synchronous data on all channels of interest * returns 0 if not done, allowing the calling program * to continue polling for done by repeated * calls to gsd_sync_read * 2) with NEXTSET_SYNC_DATA: Pushes any currently saved valid * event data down to the "previous" level store. * At invocation, time stamp data monitors are established * for each channel named. * All event data is then received and stored in sequence * until either: * 1) event data containg identical time stamps has been * received from all channels named (this time stamp * is selected to be associated with this sync * data call) or * 2) time out occurs (in this case the most recent * time stamp of all the collected event data * is selected as the time stamp associated with * this sync data call) * Data that has the associated time stamp * will be marked as valid and will be copied to the * synchronous data buffers. If a channel has no data with * the associated time stamp, then its most recent received * data will be copied to the synchronous data buffers and * will be marked not valid. * 3) with PREVIOUS_SYNC_DATA: yields the collected and * saved data that currently reside in the "previous" * level store. * 3) Interpretation of flags: * 1) gsd_sync_data.svalid: is 1 if new sync event data has come in * over that channel during its synchronous read window. * The pointer gsd_sync_data.pSdata points to the * DBR_TIME_XXXX structure containing this new data. * : is 0 if no new synchronous * event data has come in * over that channel during its synchronous read window. * The pointer gsd_sync_data.pSdata points the the * DBR_TIME_XXXX structure and will contain the remaining * old residual data unchanged from the most recent * successful synchronous event. * 2) gsd_sync_data.pSdata: is 0 (i.e. NULL) if that channel * has never established network connection. If NULL then * no DBR_TIME_XXXX data is yet available. *************************************************** */ static char *sccsId = "@(#)gsd_sync_subr.c 1.8\t11/5/92"; #if defined(UNIX) # include # include # include # include #else # if defined(vxWorks) # define abort(A) taskSuspend(taskIdSelf()) # ifdef V5vxWorks # include # else # include # endif # if 0 /* needed ?? */ # include # include # include # endif # else @@@@ dont compile in this case @@@@ # endif #endif #include #include /* *-------------------------------------------- * Function prototypes *-------------------------------------------- */ VOID gsd_fd_register(); VOID gsd_ca_service(); VOID gsd_ca_task_initialize(); VOID gsd_ca_pend_event(); VOID *gsd_sync_init(); VOID gsd_connevent_handler(); VOID gsd_event_handler(); int gsd_sync_read(); TS_STAMP *gsd_complete_set(); VOID gsd_copy_buffer(); int gsd_timeout(); VOID gsd_sync_clear(); int gsd_tss_within_delta(); int gsd_tss_eq(); int gsd_tss_gtr(); TS_STAMP *gsd_findlargest_ts(); VOID gsd_freeevent_mem(); VOID gsd_syncset_findcopy(); TS_STAMP *gsd_get_ts(); /*-------------------------------------------*/ struct gsd_sync_linked { struct gsd_sync_linked *pNstruct; /* ptr to next gsd_sync_linked struct */ VOID *pNdata; /* ptr to event TBR_TIME_xxxx data */ }; struct gsd_sync_ctrl { struct gsd_sync_data *pDstruct; /* ptr to assoc gsd_sync_data struc */ int nvalid; /* next data's valid flag */ int avalid; /* already data's valid flag */ VOID *pNdata; /* ptr to next data set */ VOID *pAdata; /* ptr to immediately previous (already) data set */ struct gsd_sync_linked *pNstruct; /* ptr to head linked data sets */ int en_event; /* enable the storage of event data */ evid event_id; /* event id storage for this channel */ }; struct gsd_sync_compctrl { struct timeval start_time; /* sys time store at initial call */ struct gsd_sync_ctrl *pCtrl; /* ptr to gsd_sync_ctrl array */ }; /*---------------------------------------------*/ #define ONESEC_IN_TICKS (sysClkRateGet()) #define PEND_EVENT_DELAY 0.0001 /* standard 0.0001sec event delay */ #define USEC_TIME_OUT 100 /* 100 usec timeval's timeout */ /* test only !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ /* *------------------------------------------------------ * For stand-alone operation remove the comments that bracket this * mainline driver and call: * main() *------------------------------------------------------ */ #define CHAN_COUNT 4 /* 4 channels for this example test code */ #define SYNC_TIMEOUT 1.0 /* timeout for data reception in float secs */ struct gsd_sync_data sync_data_array[CHAN_COUNT]; struct gsd_sync_data *pSync_data_array = &sync_data_array[0]; /* main() { VOID *pfdctx = NULL; struct gsd_sync_compctrl *pSync_compctrl; struct gsd_sync_ctrl *pSync_ctrl; int i; sync_data_array[0].pName = "IOC21:CALC:S00:00"; sync_data_array[1].pName = "IOC21:CALC:S00:01"; sync_data_array[2].pName = "BSD_41N:00:00"; sync_data_array[3].pName = "BSD_21:00:00"; SEVCHK(ca_task_initialize(), "main: C.A. initialize failure.\n"); pSync_compctrl = (struct gsd_sync_compctrl *) gsd_sync_init(pSync_data_array, CHAN_COUNT, &pfdctx, NULL); if(NULL == pSync_compctrl) { ca_printf("unable to continue, gsd_sync_init failed\n"); return 1; } pSync_ctrl = pSync_compctrl->pCtrl; gsd_sync_read(PREVIOUS_SYNC_DATA, pSync_compctrl, CHAN_COUNT, &pfdctx, SYNC_TIMEOUT); ca_printf("result of sync_read(PREVIOUS_SYNC_DATA)\n"); for (i = 0; i < CHAN_COUNT; ++i) { ca_printf("%s valid = %d ptr = %d \n", (pSync_ctrl->pDstruct)->pName, (pSync_ctrl->pDstruct)->svalid, (pSync_ctrl->pDstruct)->pSdata); if((pSync_ctrl->pDstruct)->pSdata) { ca_printf("ts sec = %ld ts nsec = %ld value = %f\n", ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.secPastEpoch, ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.nsec, ((struct dbr_time_float*) ((pSync_ctrl->pDstruct)->pSdata))->value); } ++pSync_ctrl; } pSync_ctrl = pSync_compctrl->pCtrl; ca_printf("\n"); while(!gsd_sync_read(NEXTSET_SYNC_DATA, pSync_compctrl, CHAN_COUNT, &pfdctx, SYNC_TIMEOUT)) { ; } ca_printf("result of sync_read(NEXTSET_SYNC_DATA)\n"); for (i = 0; i < CHAN_COUNT; ++i) { ca_printf("%s valid = %d ptr = %d \n", (pSync_ctrl->pDstruct)->pName, (pSync_ctrl->pDstruct)->svalid, (pSync_ctrl->pDstruct)->pSdata); if((pSync_ctrl->pDstruct)->pSdata) { ca_printf("ts sec = %ld ts nsec = %ld value = %f\n", ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.secPastEpoch, ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.nsec, ((struct dbr_time_float*) ((pSync_ctrl->pDstruct)->pSdata))->value); } ++pSync_ctrl; } pSync_ctrl = pSync_compctrl->pCtrl; ca_printf("\n"); gsd_sync_read(PREVIOUS_SYNC_DATA, pSync_compctrl, CHAN_COUNT, &pfdctx, SYNC_TIMEOUT); ca_printf("result of sync_read(PREVIOUS_SYNC_DATA)\n"); for (i = 0; i < CHAN_COUNT; ++i) { ca_printf("%s valid = %d ptr = %d \n", (pSync_ctrl->pDstruct)->pName, (pSync_ctrl->pDstruct)->svalid, (pSync_ctrl->pDstruct)->pSdata); if((pSync_ctrl->pDstruct)->pSdata) { ca_printf("ts sec = %ld ts nsec = %ld value = %f\n", ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.secPastEpoch, ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.nsec, ((struct dbr_time_float*) ((pSync_ctrl->pDstruct)->pSdata))->value); } ++pSync_ctrl; } pSync_ctrl = pSync_compctrl->pCtrl; ca_printf("\n"); while(!gsd_sync_read(NEXTSET_SYNC_DATA, pSync_compctrl, CHAN_COUNT, &pfdctx, SYNC_TIMEOUT)) { ; } ca_printf("result of sync_read(NEXTSET_SYNC_DATA)\n"); for (i = 0; i < CHAN_COUNT; ++i) { ca_printf("%s valid = %d ptr = %d \n", (pSync_ctrl->pDstruct)->pName, (pSync_ctrl->pDstruct)->svalid, (pSync_ctrl->pDstruct)->pSdata); if((pSync_ctrl->pDstruct)->pSdata) { ca_printf("ts sec = %ld ts nsec = %ld value = %f\n", ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.secPastEpoch, ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.nsec, ((struct dbr_time_float*) ((pSync_ctrl->pDstruct)->pSdata))->value); } ++pSync_ctrl; } pSync_ctrl = pSync_compctrl->pCtrl; ca_printf("\n"); gsd_sync_read(PREVIOUS_SYNC_DATA, pSync_compctrl, CHAN_COUNT, &pfdctx, SYNC_TIMEOUT); ca_printf("result of sync_read(PREVIOUS_SYNC_DATA)\n"); for (i = 0; i < CHAN_COUNT; ++i) { ca_printf("%s valid = %d ptr = %d \n", (pSync_ctrl->pDstruct)->pName, (pSync_ctrl->pDstruct)->svalid, (pSync_ctrl->pDstruct)->pSdata); if((pSync_ctrl->pDstruct)->pSdata) { ca_printf("ts sec = %ld ts nsec = %ld value = %f\n", ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.secPastEpoch, ((struct dbr_time_float *) ((pSync_ctrl->pDstruct)->pSdata))->stamp.nsec, ((struct dbr_time_float*) ((pSync_ctrl->pDstruct)->pSdata))->value); } ++pSync_ctrl; } pSync_ctrl = pSync_compctrl->pCtrl; ca_printf("\n"); gsd_sync_clear(pSync_compctrl, CHAN_COUNT); ca_printf("End of test and demo.\n\n"); return 0; } */ /* end test!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ /* ******************************************************* * Description: * 1) Entry with pSync containing the base address of the array of * gsd_sync_data structures (chan_count of these) * 2) Alloc memory for a composite control structure instance and * an array of gsd_sync_ctrl structures, * one for each gsd_sync_data structure * 3) Establish a connection event handler * 4) For each gsd_sync_ctrl structure * 1) init pCtrl->Dstruct to point to its associated * gsd_sync_data structure * 2) init to NULL the pSdata ptr in associated pSync * (this is used as a flag to indicate if channel has * ever been up in function "gsd_connevent_handler") * 3) broadcast chan access search * 5) Flush i/o for channel access * 6) Returns pCompctrl, the address of the composite control struct * which must be used for any subsequent gsd_sync_read(flag,pCtrl, * chan_count) calls * pCompctrl == NULL: error, unable to allocate memory. * pCompctrl != NULL: success, normal execution. ******************************************************* */ VOID *gsd_sync_init(pSync, chan_count, pfdctx, pMac_pairs) struct gsd_sync_data *pSync; unsigned int chan_count; VOID **pfdctx; char *pMac_pairs[]; { int i; struct gsd_sync_compctrl *pCompctrl; struct gsd_sync_ctrl *pCtrl; if(*pfdctx == NULL) { gsd_ca_task_initialize(pfdctx); } pCompctrl = (struct gsd_sync_compctrl *) calloc(1, sizeof(struct gsd_sync_compctrl)); if(pCompctrl == NULL) { ca_printf("gsd_sync_init: mem alloc failed\n"); return NULL; } pCtrl = (struct gsd_sync_ctrl *) calloc(chan_count, sizeof(struct gsd_sync_ctrl)); if(pCtrl == NULL) { ca_printf("gsd_sync_init: mem alloc failed\n"); free(pCompctrl); return NULL; } pCompctrl->pCtrl = pCtrl; for(i = 0; i < chan_count; i++) { pCtrl->pDstruct = pSync; pSync->pSdata = NULL; if(strlen(pSync->pName) > 0){ char name_str[db_name_dim]; if(pMac_pairs != NULL){ gsdl_main_macro(name_str, pSync->pName, pMac_pairs, db_name_dim); strcpy(pSync->pName, name_str); } /* ca_printf("new_str = %s\n", pSync->pName);*/ SEVCHK(ca_build_and_connect(pSync->pName, TYPENOTCONN, 0, &(pSync->pChid), (VOID *) NULL, gsd_connevent_handler, (VOID *) pCtrl), "gs_sync_init: broadcast search failed\n"); ca_flush_io(); } ++pSync; ++pCtrl; } gsd_ca_pend_event(pfdctx); return (VOID *) pCompctrl; } /* **************************************************** * Description: * 1) If first time channel connect has been up (i.e. no valid * memory address for pSdata) then * 1) Allocate memory for the sync data and fill pSdata * with its address. If fail, return. (no monitor started) * 2) Allocate memory for the next data. If fail, * free memory gotten so far, and return. * (no monitor started) * 3) Allocate memory for the already data. If fail, * free memory gotten so far, and return. * (no monitor started) * 4) Start event monitor on channel * 5) Flush i/o for channel access ***************************************************** */ VOID gsd_connevent_handler(args) struct connection_handler_args args; { struct gsd_sync_ctrl *pCtrl = ca_puser(args.chid); struct gsd_sync_data *pSync = pCtrl->pDstruct; if(args.op == CA_OP_CONN_UP) { if(pSync->pSdata == NULL) { pSync->pSdata = (VOID *) calloc(1, dbr_size_n( dbf_type_to_DBR_TIME(ca_field_type(args.chid)), ca_element_count(args.chid))); if(pSync->pSdata == NULL) { ca_printf("gsd_connevent_handler: mem alloc fail\n"); return; } pCtrl->pNdata = (VOID *) calloc(1, dbr_size_n( dbf_type_to_DBR_TIME(ca_field_type(args.chid)), ca_element_count(args.chid))); if(pCtrl->pNdata == NULL) { free(pSync->pSdata); pSync->pSdata = NULL; ca_printf("gsd_connevent_handler: mem alloc fail\n"); return; } pCtrl->pAdata = (VOID *) calloc(1, dbr_size_n( dbf_type_to_DBR_TIME(ca_field_type(args.chid)), ca_element_count(args.chid))); if(pCtrl->pAdata == NULL) { free(pSync->pSdata); pSync->pSdata = NULL; free(pCtrl->pNdata); pCtrl->pNdata = NULL; ca_printf("gsd_connevent_handler: mem alloc fail\n"); return; } pSync->time_type = dbf_type_to_DBR_TIME( ca_field_type(pSync->pChid)); pSync->count = ca_element_count(pSync->pChid); /* ca_printf("%s type = %d count = %d\n", pSync->pName, pSync->time_type, pSync->count); */ } } return; } /* *********************************************************** * Description: * 1) if (event is enabled) then * 1) if (event is the 1st for this channel (pN == NULL)) then * 1) alloc mem for a gsd_sync_linked structure and * place its addr into the pN ptr * else * 1) traverse linked list until pN points to last * gsd_sync_linked structure * 2) allocate memory for a new gsd_sync_linked * structure and append it to end of linked list * 3) Update pN to point to this new last structure * 2) allocate memory to hold the data * 3) copy the data into this buffer *********************************************************** */ VOID gsd_event_handler(args) struct event_handler_args args; { struct gsd_sync_ctrl *pCtrl = args.usr; struct gsd_sync_linked *pN = pCtrl->pNstruct; struct gsd_sync_linked *pAlloc_linked; VOID *pAlloc_data; if(pCtrl->en_event) { pAlloc_linked = (struct gsd_sync_linked *) calloc(1, sizeof(struct gsd_sync_linked)); pAlloc_data = (VOID *) calloc(1, dbr_size_n(args.type,args.count)); if((NULL == pAlloc_linked) || (NULL == pAlloc_data)) { ca_printf("gsd_event_handler: mem alloc failed.\n"); /* clean-up unused memory */ if(pAlloc_linked != NULL) free(pAlloc_linked); if(pAlloc_data != NULL) free(pAlloc_data); return; } if(pN == NULL) { pCtrl->pNstruct = pAlloc_linked; pN = pAlloc_linked; } else { while(pN->pNstruct) pN = pN->pNstruct; pN->pNstruct = pAlloc_linked; pN = pN->pNstruct; } pN->pNdata = pAlloc_data; gsd_copy_buffer(args.dbr, pN->pNdata, dbr_size_n(args.type, args.count)); /* ca_printf("%s ts sec = %ld ts nsec = %ld value = %f\n", (pCtrl->pDstruct)->pName, ((struct dbr_time_float *) (pN->pNdata))->stamp.secPastEpoch, ((struct dbr_time_float *) (pN->pNdata))->stamp.nsec, ((struct dbr_time_float *) (pN->pNdata))->value); */ } return; } /* ***************************************************** * Description: * 1) if (PREVIOUS_SYNC_DATA) then * 1) for each channel * 1) if channel has ever been up then * 1) copy already data to sync data * 2) copy already valid flag to sync valid flag * else * 1) place NULL in sync data ptr * 2) clear sync valid flag to zero * 2) if (NEXTSET_SYNC_DATA) then * 1) if first call indicated by 0 start time then * 1) for each channel * 1) if channel has ever been up then * 1) copy old next data to already level data * copy old next valid flags to already * level valid flags * 2) clear next valid flags * 3) enable capture of monitor data until * a full set is acquired or timeout * 4) add monitor event * 2) fill with system time * 3) return 0 indicating not done * 2) if complete set received or timed out then * 1) clear start time to 0 to prepare for * possible next call * 2) for each channel * 1) disable further capture of data * 2) clear monitor event * 3) copy chosen time stamp data to next level * (if no data received, don't over copy to * next level, but clear valid flag on next levl * 4) for each channel * 1) if channel has ever been up then * 1) copy next level data to sync data * 2) copy next level valid flag to * sync valid flag * 3) free memory of linked list and * its associated data buffer * else * 1) place NULL in sync data ptr * 2) set sync valid flag to zero * 5) return 1 indicating done * 3) return 0 indicating not done * 3) return 1 indicating done with unknown request ***************************************************** */ int gsd_sync_read(flag, pCompctrl,chan_count, pfdctx, timeout_secs) char flag; struct gsd_sync_compctrl *pCompctrl; unsigned int chan_count; VOID *pfdctx; float timeout_secs; { struct gsd_sync_ctrl *pCtrl = pCompctrl->pCtrl; TS_STAMP *pTs; struct timezone zone; int i; if(NULL == pCompctrl) { ca_printf("gsd_sync_read: error NULL arg for pCompctrl\n"); return 0; } if(flag == PREVIOUS_SYNC_DATA) { for (i = 0; i < chan_count; i++) { if(pCtrl->pAdata != NULL) { gsd_copy_buffer(pCtrl->pAdata, (pCtrl->pDstruct)->pSdata, dbr_size_n((pCtrl->pDstruct)->time_type, (pCtrl->pDstruct)->count)); (pCtrl->pDstruct)->svalid = pCtrl->avalid; } else { (pCtrl->pDstruct)->pSdata = NULL; (pCtrl->pDstruct)->svalid = 0; } ++pCtrl; } return 1; /* done with PREVIOUS_SYNC_DATA request */ } if(flag == NEXTSET_SYNC_DATA) { if(((pCompctrl->start_time).tv_sec == 0) && ((pCompctrl->start_time).tv_usec == 0)) { for (i = 0; i < chan_count; i++) { if(pCtrl->pNdata != NULL) { gsd_copy_buffer(pCtrl->pNdata, pCtrl->pAdata, dbr_size_n((pCtrl->pDstruct)->time_type, (pCtrl->pDstruct)->count)); pCtrl->avalid = pCtrl->nvalid; pCtrl->nvalid = 0; pCtrl->en_event = 1; SEVCHK(ca_add_array_event( (pCtrl->pDstruct)->time_type, (pCtrl->pDstruct)->count, (pCtrl->pDstruct)->pChid, gsd_event_handler, pCtrl, (float) 0, (float) 0, (float) 0, &(pCtrl->event_id)), "gsd_connevent_handler: add event failed\n"); } ++pCtrl; } ca_flush_io(); gettimeofday(&(pCompctrl->start_time), &zone); return 0; /* not done */ } if((pTs = gsd_complete_set(pCompctrl->pCtrl, chan_count)) || (gsd_timeout(pCompctrl, timeout_secs, pfdctx))) { (pCompctrl->start_time).tv_sec = 0; (pCompctrl->start_time).tv_usec = 0; pCtrl = pCompctrl->pCtrl; for (i = 0; i < chan_count; ++i) { pCtrl->en_event = 0; if(pCtrl->event_id) ca_clear_event(pCtrl->event_id); ++pCtrl; } ca_flush_io(); pCtrl = pCompctrl->pCtrl; if(pTs != NULL) { /* ca_printf("got sync data set\n"); */ gsd_syncset_findcopy(pCtrl, chan_count, pTs); } else { /* ca_printf("settle for last \n"); */ pTs = gsd_findlargest_ts(pCtrl, chan_count); gsd_syncset_findcopy(pCtrl, chan_count, pTs); } for(i = 0; i < chan_count; i++) { if(pCtrl->pNdata != NULL) { gsd_copy_buffer(pCtrl->pNdata, (pCtrl->pDstruct)->pSdata, dbr_size_n((pCtrl->pDstruct)->time_type, (pCtrl->pDstruct)->count)); (pCtrl->pDstruct)->svalid = pCtrl->nvalid; gsd_freeevent_mem(pCtrl); } else { (pCtrl->pDstruct)->pSdata = NULL; (pCtrl->pDstruct)->svalid = 0; } ++pCtrl; } return 1; /* done with NEXT_SYNC_DATA request, */ /* either fullset or timeout */ } return 0; /* not done with NEXT_SYNC_DATA request */ } return 1; /* done, unknown request */ } /* ************************************************ * Description: * 1) If a set of identically time stamped data is found in * each channel of interest then * 1) return a ptr to one of the identical time stamps * 2) Otherwise return NULL ptr ************************************************ */ TS_STAMP *gsd_complete_set(pCtrl, chan_count) struct gsd_sync_ctrl *pCtrl; unsigned int chan_count; { struct gsd_sync_linked *pNfirst_chan = pCtrl->pNstruct; struct gsd_sync_linked *pNchan = NULL; TS_STAMP *pEqual = NULL; int i; struct gsd_sync_ctrl *pCtrl_copy; while(pNfirst_chan != NULL) { /* for every data rec receiv in 1st chn*/ pCtrl_copy = pCtrl; for(i = 0; i < chan_count; ++i) { /* for every channel in chan array */ pEqual = NULL; pNchan = pCtrl_copy->pNstruct; while(pNchan != NULL) { /* for every data record received by this channel */ /* may want to use gsd_tss_within_delta instead of eq */ if(gsd_tss_eq( gsd_get_ts((pCtrl->pDstruct)->time_type, pNfirst_chan->pNdata), gsd_get_ts((pCtrl_copy->pDstruct)->time_type, pNchan->pNdata))) { pEqual = gsd_get_ts( (pCtrl->pDstruct)->time_type, pNfirst_chan->pNdata); break; } pNchan = pNchan->pNstruct; } if(NULL == pEqual) break; /* no ts match in this chan, exit the for, */ /* and try next 1st channel time stamp */ pCtrl_copy++; } if(pEqual != NULL) return pEqual; /* success */ pNfirst_chan = pNfirst_chan->pNstruct; /* try next data record of 1st channel */ } return NULL; /* failed, returning NULL ptr */ } /* *************************** * Description: * 1) returns ptr to the time stamp structure within the data buffer of * types DBR_TIME_xxxx (an accessor routine) ****************************** */ TS_STAMP *gsd_get_ts(time_type, pNdata) int time_type; void *pNdata; { switch(time_type) { case DBR_TIME_STRING: return &(((struct dbr_time_string *) pNdata)->stamp); case DBR_TIME_SHORT: return &(((struct dbr_time_short *) pNdata)->stamp); case DBR_TIME_FLOAT: return &(((struct dbr_time_float *) pNdata)->stamp); case DBR_TIME_ENUM: return &(((struct dbr_time_enum *) pNdata)->stamp); case DBR_TIME_CHAR: return &(((struct dbr_time_char *) pNdata)->stamp); case DBR_TIME_LONG: return &(((struct dbr_time_long *) pNdata)->stamp); case DBR_TIME_DOUBLE: return &(((struct dbr_time_double *) pNdata)->stamp); default: return NULL; } } /* ************************************************ * Description: * 1) returns ptr to the largest time stamp found in * all of the channel data collected. If no data then * returns NULL. ************************************************ */ TS_STAMP *gsd_findlargest_ts(pCtrl, chan_count) struct gsd_sync_ctrl *pCtrl; int chan_count; { TS_STAMP ts; TS_STAMP *pTs = &ts; struct gsd_sync_linked *pNchan; int i; ts.secPastEpoch = 0; ts.nsec = 0; for (i = 0; i < chan_count; ++i) { pNchan = pCtrl->pNstruct; while(pNchan != NULL) { /* ca_printf(" %ld %ld >? %ld %ld\n", (gsd_get_ts((pCtrl->pDstruct)->time_type, pNchan->pNdata))->secPastEpoch, (gsd_get_ts((pCtrl->pDstruct)->time_type, pNchan->pNdata))->nsec, pTs->secPastEpoch, pTs->nsec); */ if(gsd_tss_gtr( gsd_get_ts((pCtrl->pDstruct)->time_type,pNchan->pNdata), pTs) || gsd_tss_eq( gsd_get_ts((pCtrl->pDstruct)->time_type, pNchan->pNdata), pTs)) { pTs = gsd_get_ts((pCtrl->pDstruct)->time_type,pNchan->pNdata); } pNchan = pNchan->pNstruct; } pCtrl++; } if(pTs == &ts) { return NULL; } else { return pTs; } } /* ******************************************* * Description: * 1) Frees all event memory that was allocated on the fly by * following each channel's linked list of gsd_sync_linked structures ******************************************* */ VOID gsd_freeevent_mem(pCtrl) struct gsd_sync_ctrl *pCtrl; { struct gsd_sync_linked *pN; struct gsd_sync_linked *pN_copy; pN = pCtrl->pNstruct; while(pN != NULL) { if(pN->pNdata != NULL) { /* ca_printf("free %s %ld %ld\n", (pCtrl->pDstruct)->pName, ((struct dbr_time_float *) (pN->pNdata))->stamp.secPastEpoch, ((struct dbr_time_float *) (pN->pNdata))->stamp.nsec); */ free(pN->pNdata); } pN_copy = pN; pN = pN->pNstruct; free(pN_copy); } pCtrl->pNstruct = NULL; return; } /* ******************************************* * Description: * 1) for every channel * 1) If finds linked list data that is time stamped the * same as pTs. * 1) copies this data to the Next data buffer of * the gsd_sync_ctrl structure * 2) sets nvalid = 1 * else * 1) copies most recently received data to the * Next data buffer of the gsd_sync_ctrl structure * 2) clears nvalid = 0 ******************************************* */ VOID gsd_syncset_findcopy(pCtrl, chan_count, pTs) struct gsd_sync_ctrl *pCtrl; int chan_count; TS_STAMP *pTs; { struct gsd_sync_linked *pNchan; int i; for (i = 0; i < chan_count; ++i) { pCtrl->nvalid = 0; pNchan = pCtrl->pNstruct; while(pNchan != NULL) { if(gsd_tss_eq(gsd_get_ts((pCtrl->pDstruct)->time_type, pNchan->pNdata), pTs)) { gsd_copy_buffer(pNchan->pNdata, pCtrl->pNdata, dbr_size_n((pCtrl->pDstruct)->time_type, (pCtrl->pDstruct)->count)); pCtrl->nvalid = 1; break; } pNchan = pNchan->pNstruct; } if((pCtrl->nvalid) != 1) { if(pNchan = pCtrl->pNstruct) { while(pNchan->pNstruct) pNchan = pNchan->pNstruct; gsd_copy_buffer(pNchan->pNdata, pCtrl->pNdata, dbr_size_n((pCtrl->pDstruct)->time_type, (pCtrl->pDstruct)->count)); } } pCtrl++; } return; } /* ************************************************* * Description: * 1) byte block transfer ************************************************* */ VOID gsd_copy_buffer(pFrom, pTo, byte_count) char *pFrom; char *pTo; unsigned int byte_count; { unsigned int i; for(i = 0; i < byte_count; ++i) { *pTo++ = *pFrom++; } return; } /* ********************************************** * Description: * 1) Frees all allocated memory which resulted from calls to * gsd_sync_init * gsd_sync_read ********************************************** */ VOID gsd_sync_clear(pSync_compctrl, chan_count) struct gsd_sync_compctrl *pSync_compctrl; unsigned int chan_count; { int i; struct gsd_sync_ctrl *pSync_ctrl = pSync_compctrl->pCtrl; if(NULL == pSync_compctrl) { ca_printf("gsd_sync_clear: error NULL pSync_compctrl\n"); return; } if(NULL == pSync_ctrl) { free(pSync_compctrl); ca_printf("gsd_sync_clear: error NULL pSync_ctrl\n"); return; } for(i = 0; i < chan_count; ++i) { ca_clear_channel((pSync_ctrl->pDstruct)->pChid); ca_flush_io(); if(pSync_ctrl->pNdata != NULL) free(pSync_ctrl->pNdata); if(pSync_ctrl->pAdata != NULL) free(pSync_ctrl->pAdata); if((pSync_ctrl->pDstruct)->pSdata != NULL) free((pSync_ctrl->pDstruct)->pSdata); (pSync_ctrl->pDstruct)->pSdata = NULL; pSync_ctrl++; } free(pSync_compctrl->pCtrl); free(pSync_compctrl); return; } /* ********************************************** * Description: * 1) if the two time stamps are within delta of each other then * 1) return 1 * 2) otherwise return 0 ********************************************** */ int gsd_tss_within_delta(pTs_s, pTs_l, pDelta) TS_STAMP *pTs_s; TS_STAMP *pTs_l; TS_STAMP *pDelta; { TS_STAMP temp; TS_STAMP *pTemp; if(gsd_tss_eq(pTs_s, pTs_l)) return 1; /* equal, so within delta */ /* ensure that *pTs_l is the larger time stamp */ if(gsd_tss_gtr(pTs_s, pTs_l)) { pTemp = pTs_l; pTs_l = pTs_s; pTs_s = pTemp; } temp.secPastEpoch = pTs_s->secPastEpoch + pDelta->secPastEpoch; temp.nsec = pTs_s->nsec + pDelta->nsec; if((gsd_tss_gtr(&temp, pTs_l)) || (gsd_tss_eq(&temp, pTs_l))) return 1; /* within delta */ return 0; /* not within delta */ } /* ********************************************** * Description: * 1) if the two time stamps are equal then * 1) return 1 * 2) otherwise return 0 ********************************************** */ int gsd_tss_eq(pTs_s, pTs_l) TS_STAMP *pTs_s; TS_STAMP *pTs_l; { if((pTs_s == NULL) || (pTs_l == NULL)) return 0; /* not equal ts */ if((pTs_s->secPastEpoch == pTs_l->secPastEpoch) && (pTs_s->nsec == pTs_l->nsec)) return 1; /* equal time stamps */ return 0; /* not equal time stamps */ } /* ********************************************** * Description: * 1) if time stamp 1 > time stamp 2 then * 1) return 1 * 2) otherwise return 0 ********************************************** */ int gsd_tss_gtr(pTs_l, pTs_s) TS_STAMP *pTs_l; TS_STAMP *pTs_s; { if((pTs_l == NULL) || (pTs_s == NULL)) return 0; /* not gtr than */ if(pTs_l->secPastEpoch > pTs_s->secPastEpoch) return 1; /* gtr than */ if((pTs_l->secPastEpoch == pTs_s->secPastEpoch) && (pTs_l->nsec > pTs_s->nsec)) return 1; /* greater than */ return 0; /* not greater than */ } /* * Note: UNIX specific routines below: !!!!!!!!!!!!!!!!!!!!!!!!! *!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ /* ************************************************ * Description: * 1) indicate when timeout occurs * Note: UNIX gettimeofday apparently is the only C/system time * routine to yield resolution below 1 second, but it is * represented as being inaccurate in its usec element * (see man gettimeofday) ************************************************ */ int gsd_timeout(pCompctrl, timeout_secs, pfdctx) struct gsd_sync_compctrl *pCompctrl; float timeout_secs; VOID **pfdctx; { struct timeval new_gmt; struct timezone zone; float dif_secs; gsd_ca_pend_event(pfdctx); /* allow C.A. events to occur */ /* in the Unix environment */ gettimeofday(&new_gmt, &zone); if(new_gmt.tv_usec >= (pCompctrl->start_time).tv_usec) { /* no borrow */ dif_secs = ((float) (new_gmt.tv_sec - (pCompctrl->start_time).tv_sec)) + (((float) (new_gmt.tv_usec - (pCompctrl->start_time).tv_usec)) / 1000000.0); } else { /* borrow */ dif_secs = ((float) (new_gmt.tv_sec - (pCompctrl->start_time).tv_sec - 1)) + (((float) (1000000 + new_gmt.tv_usec - (pCompctrl->start_time).tv_usec)) / 1000000.0); } if(dif_secs > timeout_secs) { return 1; } return 0; } /* ********************************************** ********************************************** */ void gsd_ca_task_initialize(pfdctx) void **pfdctx; { *pfdctx = (void *) fdmgr_init(); if(!(*pfdctx)) { ca_printf("gsd_ca_task_initialize: fdmgr_init failed.\n"); abort(); } SEVCHK(ca_add_fd_registration(gsd_fd_register, *pfdctx), "gsd_ca_task_initialize: Fd registration failed.\n"); return; } /* ********************************************** ********************************************** */ void gsd_ca_pend_event(pfdctx) void **pfdctx; { static struct timeval timeout = {0, USEC_TIME_OUT}; fdmgr_pend_event(*pfdctx, &timeout); return; } /* ************************************************ * NAME * gsd_fd_register(pfdcts, fd, condition) * DESCRIPTION * 1) if (condition is true) then * 1) add file descriptor to fd manager * else * 1) delete file descriptor from fd manager ************************************************ */ void gsd_fd_register(pfdctx, fd, condition) void **pfdctx; int fd; int condition; { if(condition) { fdmgr_add_fd(pfdctx, fd, gsd_ca_service, NULL); } else { fdmgr_clear_fd(pfdctx, fd); } } /* ************************************************* * NAME * gsd_ca_service() * DESCRIPTION * 1) call ca_pend_event to allow event handler execution *************************************************** */ void gsd_ca_service() { ca_pend_event(PEND_EVENT_DELAY); }