#!/bin/sh # # iocMakeApp # # Author: Marty Kraimer # #set -xv SAVEDIR=$PWD EXAMPLES="" Cleanup() { cd $SAVEDIR rtncode=$1 shift 1 for MESSAGE do echo "$MESSAGE" done exit $rtncode } USAGE="Usage: iocMakeApp -r release -e appname ... " if [ "$PWD" = "$HOME" ]; then Cleanup 1 "In Home Directory" "$USAGE"; fi; #look for options (Currently only -r release and -e) RELEASE= while getopts r:e OPT do case $OPT in r) RELEASE=$OPTARG ;; e) EXAMPLES=TRUE ;; \?) Cleanup 1 "$USAGE" ;; esac done shift `expr $OPTIND - 1` #if user did not specify release and none exists error if [ -z "$RELEASE" -a ! -f EPICS_BASE ]; then Cleanup 1 "No release " "$USAGE" fi if [ ! -z "$RELEASE" ]; then ${RELEASE}/bin/${HOST_ARCH}/getrel ${RELEASE} if [ ! -f EPICS_BASE ]; then Cleanup 1 "Did not detect successful getrel" "$USAGE" fi fi EPICS_BASE=`sed -e "s%EPICS_BASE=%%" < EPICS_BASE` if [ ! -f Makefile ]; then cat > Makefile <<-\END TOP = . include $(TOP)/config/CONFIG_APP #add directories as follows #DIRS += include $(EPICS_BASE)/config/RULES_TOP END fi if [ ! -d config ]; then mkdir config cat > config/CONFIG <<-\END # Add any changes to make rules here END cat > config/CONFIG_APP <<-\END #CONFIG_APP DO NOT EDIT THIS FILE include $(TOP)/EPICS_BASE include $(EPICS_BASE)/config/CONFIG include $(TOP)/config/CONFIG END cat > config/RULES.Unix <<-\END include $(EPICS_BASE)/config/RULES.Unix END cat > config/RULES.Vx <<-\END include $(EPICS_BASE)/config/RULES.Vx END cat > config/RULES_DIRS <<-\END include $(EPICS_BASE)/config/RULES_DIRS END fi if [ $# = 0 ]; then Cleanup 0; fi; for APPNAME do if [ -d "${APPNAME}" ]; then continue; fi mkdir ${APPNAME} if [ $? -ne 0 ]; then Cleanup 1 "$USAGE"; fi cd ${APPNAME} cat > Makefile <<-\END TOP = .. include $(TOP)/config/CONFIG_APP DIRS = src include $(EPICS_BASE)/config/RULES_DIRS END mkdir db if [ -n "${EXAMPLES}" ]; then #if source starts with blank tab replace by tab sed -e "s/ / /" > db/dbExample.db <<-\END record(ai, "$(user):aiExample") { field(DESC, "Analog input") field(INP, "$(user):calcExample.VAL NPP NMS") field(EGUF, "10") field(EGU, "Counts") field(HOPR, "10") field(LOPR, "0") field(HIHI, "8") field(HIGH, "6") field(LOW, "4") field(LOLO, "2") field(HHSV, "MAJOR") field(HSV, "MINOR") field(LSV, "MINOR") field(LLSV, "MAJOR") } record(calc, "$(user):calcExample") { field(DESC, "Counter") field(SCAN,"1 second") field(FLNK, "$(user):aiExample") field(CALC, "(A src/Makefile <<-\END TOP=../.. include $(TOP)/config/CONFIG_APP include $(EPICS_BASE)/config/RULES_ARCHS END sed -e "s/APPNAME/${APPNAME}/" > src/Makefile.Vx <<-\END # Makefile.Vx TOP = ../../.. include $(TOP)/config/CONFIG_APP INSTALL_LOCATION = $(TOP) #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE END if [ -n "${EXAMPLES}" ]; then sed -e "s/APPNAME/${APPNAME}/" >> src/Makefile.Vx <<-\END RECTYPES += xxxRecord.h USR_INCLUDES += -I$(EPICS_BASE)/include USER_DBDFLAGS += -I .. -I $(EPICS_BASE)/dbd DBDEXPAND = APPNAMEInclude.dbd DBDNAME = APPNAME.dbd include ../baseLIBOBJS LIBOBJS += xxxRecord.o LIBOBJS += devXxxSoft.o LIBOBJS += sncExample.o LIBNAME = APPNAMESupport SCRIPTS += st.APPNAME SCRIPTS += vxWorks vxWorks.sym iocCore seq END else sed -e "s/APPNAME/${APPNAME}/" >> src/Makefile.Vx <<-\END #RECTYPES += xxxRecord.h USR_INCLUDES += -I$(EPICS_BASE)/include USER_DBDFLAGS += -I .. -I $(EPICS_BASE)/dbd #DBDEXPAND = APPNAMEInclude.dbd #DBDNAME = APPNAME.dbd #include ../baseLIBOBJS #LIBOBJS += xxx.o #LIBNAME = APPNAMESupport #SCRIPTS += st.APPNAME #SCRIPTS += vxWorks vxWorks.sym iocCore seq END fi sed -e "s/APPNAME/${APPNAME}/" >> src/Makefile.Vx <<-\END include $(TOP)/config/RULES.Vx #---------------------------------------- # ADD RULES AFTER THIS LINE $(LIBNAME): ../baseLIBOBJS $(DBDNAME): ../base.dbd ../APPNAMEInclude.dbd $(DBDNAME): $(RECTYPES:%.h=%.dbd) END cat > src/Makefile.Unix <<-\END # Makefile.Unix TOP = ../../.. include $(TOP)/config/CONFIG_APP INSTALL_LOCATION = $(TOP) #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE #USR_CFLAGS += USR_INCLUDES += -I$(EPICS_BASE)/include DEPLIBS += $(EPICS_BASE_LIB)/libca.a DEPLIBS += $(EPICS_BASE_LIB)/libCom.a USR_LDFLAGS = -L$(EPICS_BASE_LIB) USR_LDLIBS = -lca -lDb -lCom END if [ -n "${EXAMPLES}" ]; then sed -e "s/APPNAME/${APPNAME}/" >> src/Makefile.Unix <<-\END OBJS += caExample.o PROD += caExample END else sed -e "s/APPNAME/${APPNAME}/" >> src/Makefile.Unix <<-\END #OBJS += xxx.o #PROD += xxx END fi sed -e "s/APPNAME/${APPNAME}/" >> src/Makefile.Unix <<-\END include $(TOP)/config/RULES.Unix #---------------------------------------- # ADD RULES AFTER THIS LINE END if [ -n "${EXAMPLES}" ]; then cat > src/${APPNAME}Include.dbd <<-\END include "base.dbd" include "xxxRecord.dbd" device(xxx,CONSTANT,devXxxSoft,"SoftChannel") END else cat > src/${APPNAME}Include.dbd <<-\END include "base.dbd" #private database definitions go here #device(xxx,CONSTANT,devXxxSoft,"SoftChannel") END fi cp ${EPICS_BASE}/dbd/base.dbd src/base.dbd cp ${EPICS_BASE}/dbd/baseLIBOBJS src/baseLIBOBJS sed -e "s/USER/${USER}/" \ -e "s/APPNAME/${APPNAME}/" > src/st.${APPNAME} <<-\END # Example vxWorks startup file #Following must be added for many board support packages #cd ld < iocCore ld < seq ld < APPNAMESupport dbLoadDatabase("../../dbd/APPNAME.dbd") END if [ -n "${EXAMPLES}" ]; then sed -e "s/USER/${USER}/" \ -e "s/APPNAME/${APPNAME}/" >> src/st.${APPNAME} <<-\END dbLoadRecords("../../APPNAME/db/dbExample.db","user=USER") iocInit seq &snctest END else sed -e "s/USER/${USER}/" \ -e "s/APPNAME/${APPNAME}/" >> src/st.${APPNAME} <<-\END #dbLoadRecords("../../APPNAME/db/xxx.db","user=USER") iocInit #start sequence programs #seq &sncxxx END fi if [ -z "${EXAMPLES}" ]; then Cleanup 0; fi; #if source starts with blank tab replace by tab sed -e "s/ / /" > src/caExample.c <<-\END /*caExample.c*/ #include #include #include #include #include main(int argc,char **argv) { double data; int status; chid mychid; if(argc != 2) { fprintf(stderr,"usage: caExample pvname\n"); exit(1); } SEVCHK(ca_task_initialize(),"ca_task_initialize"); SEVCHK(ca_search(argv[1],&mychid),"ca_search failure"); SEVCHK(ca_pend_io(5.0),"ca_pend_io failure"); SEVCHK(ca_get(DBR_DOUBLE,mychid,(void *)&data),"ca_get failure"); SEVCHK(ca_pend_io(5.0),"ca_pend_io failure"); printf("%s %f\n",argv[1],data); return(0); } END #if source starts with blank tab replace by tab sed -e "s/ / /" > src/xxxRecord.c <<-\END /* recXxx.c */ /* Example record support module */ #include #include #include #include #include #include #include #include #include #include #include #include #define GEN_SIZE_OFFSET #include #undef GEN_SIZE_OFFSET /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(); static long process(); #define special NULL #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(); static long get_precision(); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(); static long get_control_double(); static long get_alarm_double(); struct rset xxxRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double}; typedef struct xxxset { /* xxx input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_xxx; }xxxdset; static void alarm(xxxRecord *pxxx); static void convert(xxxRecord *pxxx); static void monitor(xxxRecord *pxxx); static long init_record(void *precord,int pass) { xxxRecord *pxxx = (xxxRecord *)precord; xxxdset *pdset; long status; if (pass==0) return(0); if(!(pdset = (xxxdset *)(pxxx->dset))) { recGblRecordError(S_dev_noDSET,(void *)pxxx,"xxx: init_record"); return(S_dev_noDSET); } /* must have read_xxx function defined */ if( (pdset->number < 5) || (pdset->read_xxx == NULL) ) { recGblRecordError(S_dev_missingSup,(void *)pxxx,"xxx: init_record"); return(S_dev_missingSup); } if( pdset->init_record ) { if((status=(*pdset->init_record)(pxxx))) return(status); } return(0); } static long process(void *precord) { xxxRecord *pxxx = (xxxRecord *)precord; xxxdset *pdset = (xxxdset *)(pxxx->dset); long status; unsigned char pact=pxxx->pact; if( (pdset==NULL) || (pdset->read_xxx==NULL) ) { pxxx->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)pxxx,"read_xxx"); return(S_dev_missingSup); } /* pact must not be set until after calling device support */ status=(*pdset->read_xxx)(pxxx); /* check if device support set pact */ if ( !pact && pxxx->pact ) return(0); pxxx->pact = TRUE; recGblGetTimeStamp(pxxx); /* check for alarms */ alarm(pxxx); /* check event list */ monitor(pxxx); /* process the forward scan link record */ recGblFwdLink(pxxx); pxxx->pact=FALSE; return(status); } static long get_units(DBADDR *paddr, char *units) { xxxRecord *pxxx=(xxxRecord *)paddr->precord; strncpy(units,pxxx->egu,DB_UNITS_SIZE); return(0); } static long get_precision(DBADDR *paddr, long *precision) { xxxRecord *pxxx=(xxxRecord *)paddr->precord; *precision = pxxx->prec; if(paddr->pfield == (void *)&pxxx->val) return(0); recGblGetPrec(paddr,precision); return(0); } static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) { xxxRecord *pxxx=(xxxRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if(fieldIndex == xxxRecordVAL || fieldIndex == xxxRecordHIHI || fieldIndex == xxxRecordHIGH || fieldIndex == xxxRecordLOW || fieldIndex == xxxRecordLOLO || fieldIndex == xxxRecordHOPR || fieldIndex == xxxRecordLOPR) { pgd->upper_disp_limit = pxxx->hopr; pgd->lower_disp_limit = pxxx->lopr; } else recGblGetGraphicDouble(paddr,pgd); return(0); } static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd) { xxxRecord *pxxx=(xxxRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if(fieldIndex == xxxRecordVAL || fieldIndex == xxxRecordHIHI || fieldIndex == xxxRecordHIGH || fieldIndex == xxxRecordLOW || fieldIndex == xxxRecordLOLO) { pcd->upper_ctrl_limit = pxxx->hopr; pcd->lower_ctrl_limit = pxxx->lopr; } else recGblGetControlDouble(paddr,pcd); return(0); } static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad) { xxxRecord *pxxx=(xxxRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if(fieldIndex == xxxRecordVAL) { pad->upper_alarm_limit = pxxx->hihi; pad->upper_warning_limit = pxxx->high; pad->lower_warning_limit = pxxx->low; pad->lower_alarm_limit = pxxx->lolo; } else recGblGetAlarmDouble(paddr,pad); return(0); } static void alarm(xxxRecord *pxxx) { double val; float hyst, lalm, hihi, high, low, lolo; unsigned short hhsv, llsv, hsv, lsv; if(pxxx->udf == TRUE ){ recGblSetSevr(pxxx,UDF_ALARM,INVALID_ALARM); return; } hihi = pxxx->hihi; lolo = pxxx->lolo; high = pxxx->high; low = pxxx->low; hhsv = pxxx->hhsv; llsv = pxxx->llsv; hsv = pxxx->hsv; lsv = pxxx->lsv; val = pxxx->val; hyst = pxxx->hyst; lalm = pxxx->lalm; /* alarm condition hihi */ if (hhsv && (val >= hihi || ((lalm==hihi) && (val >= hihi-hyst)))){ if (recGblSetSevr(pxxx,HIHI_ALARM,pxxx->hhsv)) pxxx->lalm = hihi; return; } /* alarm condition lolo */ if (llsv && (val <= lolo || ((lalm==lolo) && (val <= lolo+hyst)))){ if (recGblSetSevr(pxxx,LOLO_ALARM,pxxx->llsv)) pxxx->lalm = lolo; return; } /* alarm condition high */ if (hsv && (val >= high || ((lalm==high) && (val >= high-hyst)))){ if (recGblSetSevr(pxxx,HIGH_ALARM,pxxx->hsv)) pxxx->lalm = high; return; } /* alarm condition low */ if (lsv && (val <= low || ((lalm==low) && (val <= low+hyst)))){ if (recGblSetSevr(pxxx,LOW_ALARM,pxxx->lsv)) pxxx->lalm = low; return; } /* we get here only if val is out of alarm by at least hyst */ pxxx->lalm = val; return; } static void monitor(xxxRecord *pxxx) { unsigned short monitor_mask; double delta; monitor_mask = recGblResetAlarms(pxxx); /* check for value change */ delta = pxxx->mlst - pxxx->val; if(delta<0.0) delta = -delta; if (delta > pxxx->mdel) { /* post events for value change */ monitor_mask |= DBE_VALUE; /* update last value monitored */ pxxx->mlst = pxxx->val; } /* check for archive change */ delta = pxxx->alst - pxxx->val; if(delta<0.0) delta = -delta; if (delta > pxxx->adel) { /* post events on value field for archive change */ monitor_mask |= DBE_LOG; /* update last archive value monitored */ pxxx->alst = pxxx->val; } /* send out monitors connected to the value field */ if (monitor_mask){ db_post_events(pxxx,&pxxx->val,monitor_mask); } return; } END #if source starts with blank tab replace by tab sed -e "s/ / /" > src/xxxRecord.dbd <<-\END recordtype(xxx) { include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Current EGU Value") asl(ASL0) pp(TRUE) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup(GUI_INPUTS) special(SPC_NOMOD) interest(1) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup(GUI_DISPLAY) interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup(GUI_DISPLAY) interest(1) size(16) } field(HOPR,DBF_FLOAT) { prompt("High Operating Range") promptgroup(GUI_DISPLAY) interest(1) } field(LOPR,DBF_FLOAT) { prompt("Low Operating Range") promptgroup(GUI_DISPLAY) interest(1) } field(HIHI,DBF_FLOAT) { prompt("Hihi Alarm Limit") promptgroup(GUI_ALARMS) pp(TRUE) interest(1) } field(LOLO,DBF_FLOAT) { prompt("Lolo Alarm Limit") promptgroup(GUI_ALARMS) pp(TRUE) interest(1) } field(HIGH,DBF_FLOAT) { prompt("High Alarm Limit") promptgroup(GUI_ALARMS) pp(TRUE) interest(1) } field(LOW,DBF_FLOAT) { prompt("Low Alarm Limit") promptgroup(GUI_ALARMS) pp(TRUE) interest(1) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup(GUI_ALARMS) pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup(GUI_ALARMS) pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup(GUI_ALARMS) pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup(GUI_ALARMS) pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup(GUI_ALARMS) interest(1) } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup(GUI_DISPLAY) interest(1) } field(MDEL,DBF_DOUBLE) { prompt("Monitor Deadband") promptgroup(GUI_DISPLAY) interest(1) } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_DOUBLE) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } } END #if source starts with blank tab replace by tab sed -e "s/ / /" > src/devXxxSoft.c <<-\END /* devXxxSoft.c */ /* Example device support module */ #include #include #include #include #include #include #include #include #include #include #include #include /* Added for Channel Access Links */ long dbCaAddInlink(); long dbCaGetLink(); /*Create the dset for devXxxSoft */ static long init_record(); static long read_xxx(); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_xxx; }devXxxSoft={ 5, NULL, NULL, init_record, NULL, read_xxx, }; static long init_record(pxxx) struct xxxRecord *pxxx; { long status; /* xxx.inp must be a CONSTANT or a PV_LINK or a DB_LINK or a CA_LINK*/ switch (pxxx->inp.type) { case (CONSTANT) : if(recGblInitConstantLink(&pxxx->inp,DBF_DOUBLE,&pxxx->val)) pxxx->udf = FALSE; break; case (PV_LINK) : case (CA_LINK) : case (DB_LINK) : break; default : recGblRecordError(S_db_badField, (void *)pxxx, "devXxxSoft (init_record) Illegal INP field"); return(S_db_badField); } return(0); } static long read_xxx(pxxx) struct xxxRecord *pxxx; { long status; status = dbGetLink(&(pxxx->inp),DBF_DOUBLE, &(pxxx->val),0,0); if (RTN_SUCCESS(status)) pxxx->udf = FALSE; return(0); } END #if source starts with blank tab replace by tab sed -e "s/USER/${USER}/" \ -e "s/ / /" > src/sncExample.st <<-\END program snctest float v; assign v to "USER:xxxExample"; monitor v; ss ss1 { state low { when(v>5.0) { printf("changing to high\n"); } state high when(delay(.1)) { } state low } state high { when(v<=5.0) { printf("changing to low\n"); } state low when(delay(.1)) { } state high } } END cd .. echo "add line to Makefile DIRS+=$APPNAME " done Cleanup 0