/************************** datum_9310.c *********************************** * * This is a Concurrent Computer Corp. Laboratory Workbench 2.0 user function * intended to provide support for a custom LWB instrument. This module * controls one of two Datum model 9310 IRIG time code readers via a custom * interface connected to a Concurrent Computer std+ bus EF12M module. * * This module generates handshake sequences necessary to cause the Datum 9310 * to transfer three 16 bit time words into a global buffer. The handshake * can be initiated after reciept of either the LWB MENU or RUN message tokens. * * After each data transfer, the module implements a custom decode routine that * stores the 3 Datum words into a 5 word time structure that represents the * decoded days, hours, minutes, seconds and milliseconds as discreet integers. * * Upon receipt of the first SETUP message (LWB UF link time), this module * intializes itself and the interface hardware to their default states. * This involves configuring the dacp ucode, Datum 9310 driver and setting up * error handlers. Any subsequent SETUP messages (LWB init RUN mode, LWB * SETUP menu option selected) cause the module to configure an output * buffer that has the same characteristics as that passed to the SETUP * handler in the input message. The SETUP token is then allowed to * propogate to any other LWB instruments * * Upon receipt of any MENU message, the module implements one of four * menu options: * * 1) If the 'Select port' toggle is operated, global symbol 'datum_port' * is alternated between 0x00 and 0x10. This value is used to weight * datum 9310 commands in the driver in order to select one of the * two datum 9310's via the interface. * * 2) If the 'ABOUT' field is selected, this functions' version and date * are reported. * * 3) If 'Get time' is selected the current IRIG time sensed by the interface * is obtained and shown on the module label. * * 4) If the 'Set trigger' option is chosen, an integer value is obtained * from the LWB user. The user should express an integer that * represents time in the form HHMMSS. This value is then used * in the RUN code described below: * * Upon receipt of any RUN message, the Datum 9310 driver is enabled and * the resulting decoded current time is displayed on the module label * which is then visible to the LWB user. * * The RUN module checks a 'past threshold' flag. The flag is set in the MENU * code and cleared in the RUN routine. If the flag is set, the run code * compares the converted time against the threshold entered by the user from * the MENU routine. If the current time is less then the threshold, the input * buffer is discarded and not copied to this modules' output connector. * If the current time is at or past the threshold, the flag is disabled and * the input buffer is copied to the output. This is done in order to implement * a record data trigger. For this reason, the module must be placed in series * with the data source and data collector for it to be effective. The RUN * code could be modified to use the decoded time structure to tag each buffer * although this version (v1.0) does not implement any such functionality. * * When used in a LWB 2.0 instrument, connect the input to point 'A' on the * module and pass the output on point 'X'. * * See the Datum 9310 / EF12M TCR Interface Users' Guide for more info. * * This module was developed at NUWC, Division Keyport. * * Sept. 1996 v1.0 - initial build. * J. Martin, ATK * **************************************************************************/ /* include some needed headers.. */ #include "/usr/include/lwbufunc.h" #include "/usr/include/errors.h" /* C DACP header file */ #include "/usr/include/mr.h" /* mr and system error code definitions */ #include "/usr/include/mrerrs.h" #include "/usr/include/errno.h" /* include some common headers */ #include "/usr/include/stdio.h" #include "/usr/include/signal.h" /* I/O data pointers */ short *idata; /* no sure what would happen if idata is */ short *odata; /* not cast short, probably core dump */ char *module_label[10]; /* buffer to hold module label strings */ int omsg = 0, /* new message header */ token = 0, /* message type identifier */ datum_port = 0, /* used by interface data selectors */ dx = 0, /* set debug mode */ links = 0, /* used to check message state */ sizes[3] = {0,0,0}, /* default sizes for 3 dimensions */ dimension = 0, /* number of data buffer dimensions */ count = 0, /* stores data buffer sizes and dimensions */ counter = 0, /* general purpose int */ time_threshold = 0, /* time beyond which record trigger enabled */ current_time = 0, /* used to compare decoded datum 9310 time to a user defined threshold */ /* mr event wait time out values (500 and 5000 millisecs) */ out_timeout = 500, in_timeout = 5000, /* -1 value allows dacp to assign paths to H/W when mropen is issued */ pathout = -1, pathin = -1, /* ef12m pi access mode (varible used in mropen) grants exclusive R/W access to these paths */ inmode = 0, outmode = 0, /* returns transfer status from mrevck */ in_status, out_status, /* ef12m input/output buffer size (8 bytes or four 16 bit words) */ buflen = 8, /* flag to tell when system default error handler is being hooked */ hooking = 1, /* flag to cause some parts of setup code to execute only on the 1st time */ setup_1st_time = 1, /* enable threshold checking */ check_threshold = 1, /* dummy varible so i don't forget to end int declarations with a ';' */ LAST_GLOBAL_INT; /* whole numbers to store time words decodes in note: if you write "struct tcr_time ...ect...ect", cc compiler says "syntax error", omit the _, and it compiles. */ struct tcr { int days, hours, minutes, seconds, milliseconds; /* SEI, datum decoding error */ /* LOS; datum loss of signal detected (both not used) */ } decoded; /* only purpose of unionizing the ef12 i/0 buffers with a dummy varible is to make the buffers align on a 32 bit boundry in order to remain complient with dacp uCode. Best to configure two buffers instead of using the same one for both. ef12m input buffer, 32-bit (long word) aligned */ union { unsigned short ef12ibuf[4]; int dummy } in_buf; /* ef12m output buffer, 32-bit (long word) aligned */ union { unsigned short ef12obuf[4]; int dummy1 } out_buf; /* ef12m paralell output port device file path */ char devout[] = "/dev/dacp0/pdo0", /* ef12m paralell input port device file path */ devin[] = "/dev/dacp0/pdi0"; /**************************** datum_9310.c *********************************/ /************************ * Entry point from LWB * ***********************/ datum_9310( action, inA, inB, inC, inD, outX, outY, debug ) int *action, *inA, *inB, *inC, *inD, outX, outY, *debug; { int status; dx = *debug; switch( *action ) /* provide handlers for any LWB message type */ { case SETUPCMD: status = ufsetup( *inA, outX ); break; case RUNCMD: status = ufrun( *inA, outX ); break; case STOPCMD: status = ufstop( *inA, outX); break; case MENUCMD: status = ufmenu( *inA ); break; case DROPCMD: status = ufdrop( *inA, outX); break; default: break; } return(status); }/********************* end of datum_9310 **************************/ /************************* get_9310_data ********************************* * * disables system / LWB error handling facitity, passing control to * handler 'tcrerror' upon first bus error. * * initiates EF12M inbound and outbound transfers using DACP mr library * calls in order to cause DATUM 9310 interface HW to generate 9310 commands. * * calls custom decode routine to store resulting 9310 TCR data in a global * time structure. * * displays decoded time in LWB 2.0 user function module label * *************************************************************************/ get_9310_data() { int (*syshandler)(); /* pointer to system error */ syshandler = signal(SIGBUS,tcrerror); /* hook next bus error if any then return control to system */ /* select an interface port and start the transfer. write HEX 8 and HEX 4 to the interface 0x08 causes interface to operate 9310 reset line, 0x04 presets interface counter with 0x0c ( 8 bit tied to vcc ). */ out_buf.ef12obuf[0] = datum_port+0x08; /* datum_port */ out_buf.ef12obuf[1] = datum_port+0x04; /* controls bit 4 */ mrevck( pathout, &out_status ); /* get status */ if( !out_status || out_status == 2 ) /* no outbounds */ { mrpdxout( pathout, 2, out_buf.ef12obuf); /* start one */ counter=50; /* editable */ while( out_status == 1 || out_status == -1 ) /* outbound active */ { mrevck( pathout, &out_status); counter--; /* countdown */ if( counter <= 0 ) /* timeout */ { return(-1); } } } /* transfer 4 words from tcr interface (first word is meaningless). dacp uCode and HW may prefer even numbered xfers */ mrevck( pathin, &in_status); if( !in_status || in_status == 2 ) { mrpdxin( pathin, 4, in_buf.ef12ibuf); counter=50; while( in_status == 1 || in_status == -1 ) { mrevck( pathin, &in_status); counter--; if( counter <= 0 ) { return(-2); } } } /* decode datum time words */ decode_time(); sprintf( module_label,"%02d.%02d.%02d.%03d", decoded.hours, decoded.minutes, decoded.seconds, decoded.milliseconds ); /* display result in module label */ if( !lwbsetlabel( module_label ) ) return(0); return(1); }/****************** get_9310_data *********************************/ /************************ decode_time *********************************** * * the datum 9310 presents time words to the interface in the following * format: * * 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 * * wd 0: don't care. * * * wd 1: HOURS MINUTES SECONDS * units tens units tens units * 2 1 4 2 1 8 4 2 1 4 2 1 8 4 2 1 * * * wd 2: DAYS HOURS * hundreds tens units tens units * 8 4 2 1 8 4 2 1 8 4 2 1 2 1 8 4 * * * wd 3: MILLISECONDS STATUS * hundreds tens units sei los * 8 4 2 1 8 4 2 1 8 4 2 1 1 0 1 0 * * 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 * * After an ef12m transaction ( 3 time words in user buffer), this routine * decodes the time words into the following decimal format: * * wd 1: DDD ;days, 3 digits * wd 2: HH ;hours, 2 digits * wd 3: MM ;minutes 2 digits * wd 4: SS ;seconds 2 digits * wd 5: mmmm ;milliseconds 3 digits * wd 6: SEI ;9310 error 1 digit * wd 7: LOS ;9310 loss of signal 1 digits * * The decoded values are stored in a global sturcture. * * note on milliseconds: the resolution of the datum 9310 appears to be * .999 seconds. The 4 lsbs of word 4 may be used to transmit status * (ie signal loss, some other error). although there is space alloted * for status in the output, their values never change. This appears * to be a hardware characteristic of the datum 9310. - Jeff * ****************************************************************************/ decode_time() { int hundreds, tens, units; /************** decode hours **************************/ decoded.hours = 0; /* remove any previous decodes */ /* tens */ tens = in_buf.ef12ibuf[2]; /* keep buffer contents intact */ tens = tens >> 2; /* binary shift right 2 bits */ tens &= 0x0003; /* clear any set bits, preserve */ tens *= 10; /* 2 lsb's. decimal shift left */ decoded.hours += tens; /* store hours tens */ /* units ( hours units are split between two words) */ units = in_buf.ef12ibuf[2]; units &= 0x0003; units = units << 2; decoded.hours += units; units = in_buf.ef12ibuf[1]; units = units >> 14; units &= 0x0003; decoded.hours += units; /**************** decode minutes ************************/ decoded.minutes = 0; /* tens */ tens = in_buf.ef12ibuf[1]; tens = tens >> 11; tens &= 0x0007; tens *= 10; decoded.minutes += tens; /* units */ units = in_buf.ef12ibuf[1]; units = units >> 7; units &= 0x000f; decoded.minutes += units; /*************** decode seconds *********************/ decoded.seconds = 0; /* tens */ tens = in_buf.ef12ibuf[1]; tens = tens >> 4; tens &= 0x0007; tens *= 10; decoded.seconds += tens; /* units */ units = in_buf.ef12ibuf[1]; units &= 0x000f; decoded.seconds += units; /******************* decode milliseconds **********************/ decoded.milliseconds=0; /* hundreds */ hundreds = in_buf.ef12ibuf[3]; hundreds = hundreds >> 12; hundreds &= 0x000f; hundreds *= 100; decoded.milliseconds += hundreds; /* tens */ tens = in_buf.ef12ibuf[3]; tens = tens >> 8; tens &= 0x000f; tens *= 10; decoded.milliseconds += tens; /* units */ units = in_buf.ef12ibuf[3]; units = units >> 4; units &= 0x000f; decoded.milliseconds += units; /******************* decode time status *********************************** units = in_buf.ef12ibuf[3]; units &= 0x0008; decoded.SEI = units * 1; units = in_buf.ef12ibuf[3]; units &= 0x0002; decoded.LOS = units * 1; ********************* decode time status commented out ******************/ /********************** decode days ************************************/ decoded.days = 0; /* hundreds */ hundreds = in_buf.ef12ibuf[2]; hundreds = hundreds >> 12; hundreds &= 0x000f; hundreds *= 100; decoded.days += hundreds; /* tens */ tens = in_buf.ef12ibuf[2]; tens = tens >> 8; tens &= 0x000f; tens *= 10; decoded.days += tens; /* units */ units = in_buf.ef12ibuf[2]; units = units >> 4; units &= 0x000f; decoded.days += units; return(1); }/********************************************************************/ /************************ ufsetup *********************************** * * responds to SETUP messages. first msg initializes datum 9310 * driver and dacp ucode. Subsequent SETUPs ascertain input buffer * conditions and creates duplicate for output messages used * during RUN code. * *******************************************************************/ ufsetup( in, out ) int in, out; { /* assign starting module label */ if( !lwbsetlabel( "DATUM 9310" ) ) return(0); /* initialize and label menu defaults */ if( !lwblabelmenu( 1, "Select Port" ) ) return(0); if( !lwbupdatemenu(1, "%x", (datum_port / 16) ) ) return(0); if( !lwblabelmenu( 4, "Get time") ) return(0); if( !lwbupdatemenu(4, "%s"," " ) ) return(0); if( !lwblabelmenu( 5, "ABOUT") ) return(0); if( !lwbupdatemenu(5, "%s"," " ) ) return(0); /* get sizes and dimensions of input msg data buffer, store in global 'count'. This value holds number of data items in input and is used in RUN handler. */ lwbgetparam(in, 0, LWBMSGDIM, &dimension); lwbgetparam(in, 0, LWBMSGSIZES, sizes); if( dimension == 2 ) count = sizes[0] * sizes[1]; else if( dimension == 1) count = sizes[0]; /* create a new message for output data */ if( !lwbnewmsg( &omsg ) ) return(0); /* initialize output message, make it the same as the input */ if( !lwbcopymsg( in, omsg ) ) return(0); if( !lwbsamerange( in, omsg ) ) return(0); if( !lwbsamedomain( in, omsg ) ) return(0); /* create a data buffer for output message */ if( !lwbnewbuf( omsg ) ) return(0); /* omsg is a message handle, odata is pointer to a buffer of data that has same characteristics as input. here a link is established between the output message header and the data */ if( !lwbgetdatbuf( omsg, &odata ) ) return(0); /* initialize some datum 9310 driver settings and error handlers once */ if( setup_1st_time ) { setup_1st_time = 0; /* disable setup flag */ /* pull down menu 2 and 3 are managed via MENU/RUN code after first time.. */ if( !lwblabelmenu( 2, "Set trigger") ) return(0); if( !lwbupdatemenu(2, "%d",time_threshold) ) return(0); if( !lwblabelmenu( 3, " ") ) return(0); if( !lwbupdatemenu(3, "%s"," " ) ) return(0); /* cause mr errors trapped by dacp to be ignored and then default to system level bus errors so i can use system service "signal" to trap them. Yes, I know its slimey.. */ seterropt(FATAL-1,FATAL-1,0,3,0); /* continue even with fatal errors */ hooking=1; /* tell my handler that system error reporting facility is surrendering to it */ seterrtrap(tcrerror(), DEFAULT); /* invoke my handler for first time.. no actual error cause hooking true */ /* allow dacp to open ef12m paths and assign exculsive use to this function */ mropen( &pathout, devout, outmode ); mropen( &pathin, devin, inmode ); /* set inbound and outbound modes. refer to DAQ APPL PROG MAN pg 9-163 thru 9-166 for parameter descriptions. read tcr interface users manual to see how the values were obtained. (probably ok to leave these alone) */ mrpeimod( pathin,0,0,1,1,1,-1,-1); mrpeomod( pathout,0,1,0,1); } /* end setup_1st_time */ /* send SETUP msg on its way */ lwbwrite( omsg, out ); /* and release input */ lwbrelease( in ); /* no errors... */ return( 1 ); }/********************** end of ufsetup *******************************/ /********************* ufrun ***************************************** * * this module performs two main tasks: * * 1) invokes Datum 9310 driver and displays the decoded time values * on the module label. * * 2) either discards the incoming data buffer or copies it intact to * the output depending on whether or not the user specified time * threshold has past. * *********************************************************************/ ufrun( in, out) int in, out; { register int i = 0; /* check to see that some messages are occuring at any point (sanity check) */ if( !in || !omsg ) return(0); /* sample and display decoded datum 9310 time */ get_9310_data(); /* check to see if input message is available for use */ if( !lwbgetparam( omsg, 0, LWBMSGLINKS, &links) ) return(0); if( links > 0 ) { lwbunget( in ); /* push input message back into stream */ return(1); /* good status so we'll try next time. */ } /* get input data buffer */ if( !lwbgetdatbuf( in, &idata) ) return(0); /* check to see if current time is beyond time threshold. if so process and pass packet on to rest of instrument, if not then current packet is discarded here.. first translate decoded hours, minutes and seconds to a whole number in the form HHMMSS */ if( check_threshold ) { decoded.hours *= 10000; current_time = decoded.hours; decoded.minutes *= 100; current_time += decoded.minutes; current_time += decoded.seconds; /* check threshold */ if( time_threshold <= current_time ) { check_threshold = 0; /* disable checking, enable output */ if( !lwblabelmenu( 2, "Set trigger") ) return(0); if( !lwblabelmenu( 3, "Last trig.") ) return(0); if( !lwbupdatemenu(3, "%s", module_label ) ) return(0); } } /* end check_threshold */ if( !check_threshold ) { /* copy input data buffer to output (!! THIS CODE COULD MODIFY DATA !! */ for( i = 0; i < count; i++ ) { odata[i] = idata[i]; } /* write the output message */ lwbwrite( omsg, out ); } /* end !check_threshold if */ /* release the RUN token */ lwbrelease( in ); return(1); }/********************* end of ufrun ******************************/ /************************* ufstop ********************************* * * traps LWB STOP messages and returns... modify it to suit... * *****************************************************************/ ufstop( in, out) int in, out; { /* check to see that some messages are occuring at any point (sanity check) */ if( !in || !omsg ) return(0); /* to do.. add some STOP processing */ /* pass msg on rest of instrument */ lwbwrite( omsg, out ); lwbrelease( in ); return(1); }/****************** end of ufstop *********************************/ /*********************** ufmenu ********************************* * * implements a pull down menu in order to acheive the following: * * 1) Allow the user to specify what HW port the interface driver * should use. * * 2) Provide a user defined trigger to control when data packets are * released from this module to any other within the LWB instrument. * * 3) Provide a means for the user to operate the DATUM 9310 driver * and display the decoded result on the module label without having * to set the rest of the instrument into RUN mode. * * 4) Provide a means for the function to report its version and date * at LWB run time. * *****************************************************************/ ufmenu( key ) int key; { /* select a 9310 interface port ( bit 4 connected to all '153's */ if( key == 1 ) { if( datum_port == 0x10 ) { datum_port = 0x00; } else datum_port = 0x10; if( !lwbupdatemenu( 1, "%x", (datum_port / 16) ) ) { return(0); } /* 'get_9310_data' invokes driver and custom decode */ get_9310_data(); get_9310_data(); } /* end key == 1 */ /* get a new time threshold */ if( key == 2 ) { if( !lwbgetint("Enter a time threshold in the form HHMMSS", 000000, 240000, &time_threshold) ) return(0); if( !lwblabelmenu( 2, "Waiting for") ) return(0); if( !lwbupdatemenu(2, "%d",time_threshold) ) return(0); check_threshold = 1; /* enable threshold checking in RUN code */ } /* end key == 2 */ /* show current time from interface */ if( key == 4 ) { get_9310_data(); get_9310_data(); } /* service menu selection 5 (ABOUT) */ if( key == 5 ) { lwbupdatemenu(5, "%s", "v1.0 Sep 96"); } return(1); }/****************** end of ufmenu *********************************/ /*********************** ufdrop ********************************** * * message type is intended to provide user with code to execute * if function is deleted at LWB after link has occured. not used * here... write some if you want it.. * ******************************************************************/ ufdrop() { /* must nonetheless trap for it */ return(1); }/******************** end if ufdrop *************************/ /************************* tcrerror *********************************** * * this module currently does nothing else but provide the system and * LWB something to do other than core dumps if some problem occured * with the interface and driver only. It operates in a one-shot * fashion and must be re-armed by the Datum 9310 driver each time * the driver is invoked. Otherwise, normal system level / LWB * error handling is restored. * *********************************************************************/ tcrerror(sig) int sig; { /* stop all current xfers mrstop( pathout, 1, &out_status ); mrstop( pathin, 1, &in_status ); */ if( !hooking ) /* from driver */ { /* to do.... add some error handling here... */ } hooking=0; return(0); }/*******************************************************************/