diff --git a/app/Db/Makefile b/app/Db/Makefile index 0152c16..8f36895 100644 --- a/app/Db/Makefile +++ b/app/Db/Makefile @@ -28,6 +28,8 @@ DB += EthercatMCslit_soft.template DB += caPutLog.db DB += TwinCAT_TaskInfo.db DB += TwinCAT_AppInfo.db +DB += TwinCAT_Dependency.db +DB += TwinCAT_Project.db DB_INSTALLS += $(IOCADMIN)/db/iocSoft.db DB_INSTALLS += $(IOCADMIN)/db/devIocInfo.db diff --git a/app/Db/TwinCAT_Dependency.db b/app/Db/TwinCAT_Dependency.db new file mode 100644 index 0000000..a8578d6 --- /dev/null +++ b/app/Db/TwinCAT_Dependency.db @@ -0,0 +1,17 @@ +record(lsi, "$(PREFIX):ProjectInfo:$(DEPENDENCY)") { + field(DISP, 1) + field(DTYP, "Soft Channel") + field(INP, ["$(VERSION) -> $(VENDOR)"]) + field(PINI, "YES") + field(SIZV, 100) + info(archive, "monitor 1: VAL") +} + +record(lsi, "$(PREFIX):ProjectInfo:$(DEPENDENCY):Vendor") { + field(DISP, 1) + field(DTYP, "Soft Channel") + field(INP, ["$(VENDOR)"]) + field(PINI, "YES") + field(SIZV, 100) + info(archive, "monitor 1: VAL") +} diff --git a/app/Db/TwinCAT_Project.db b/app/Db/TwinCAT_Project.db new file mode 100644 index 0000000..93e3518 --- /dev/null +++ b/app/Db/TwinCAT_Project.db @@ -0,0 +1,40 @@ +record(lsi, "$(PREFIX):ProjectInfo:PLCHost") { + field(DISP, "1") + field(DTYP, "Soft Channel") + field(INP, ["$(PLC_HOST)"]) + field(PINI, "YES") + info(archive, "monitor 1: VAL") +} + +record(lsi, "$(PREFIX):ProjectInfo:Project") { + field(DISP, "1") + field(DTYP, "Soft Channel") + field(INP, ["$(PROJECT)"]) + field(PINI, "YES") + info(archive, "monitor 1: VAL") +} + +record(lsi, "$(PREFIX):ProjectInfo:Hash") { + field(DISP, "1") + field(DTYP, "Soft Channel") + field(INP, ["$(HASH)"]) + field(PINI, "YES") + field(SIZV, 100) + info(archive, "monitor 1: VAL") +} + +record(lsi, "$(PREFIX):ProjectInfo:Version") { + field(DISP, "1") + field(DTYP, "Soft Channel") + field(INP, ["$(VERSION)"]) + field(PINI, "YES") + info(archive, "monitor 1: VAL") +} + +record(lsi, "$(PREFIX):ProjectInfo:Pytmc") { + field(DISP, "1") + field(DTYP, "Soft Channel") + field(INP, ["$(PYTMC)"]) + field(PINI, "YES") + info(archive, "monitor 1: VAL") +} diff --git a/app/Db/TwinCAT_TaskInfo.db b/app/Db/TwinCAT_TaskInfo.db index 7a47240..c899820 100644 --- a/app/Db/TwinCAT_TaskInfo.db +++ b/app/Db/TwinCAT_TaskInfo.db @@ -1,4 +1,5 @@ -record(longin,"$(PREFIX):TaskInfo:1:CycleTime"){ + +record(longin,"$(PREFIX):TaskInfo:$(IDX):CycleTime"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") @@ -7,7 +8,7 @@ record(longin,"$(PREFIX):TaskInfo:1:CycleTime"){ info(archive, "monitor 1: VAL") } -record(longin,"$(PREFIX):TaskInfo:1:Priority"){ +record(longin,"$(PREFIX):TaskInfo:$(IDX):Priority"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") @@ -16,7 +17,7 @@ record(longin,"$(PREFIX):TaskInfo:1:Priority"){ info(archive, "monitor 1: VAL") } -record(waveform,"$(PREFIX):TaskInfo:1:TaskName"){ +record(waveform,"$(PREFIX):TaskInfo:$(IDX):TaskName"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt8ArrayIn") @@ -28,14 +29,14 @@ record(waveform,"$(PREFIX):TaskInfo:1:TaskName"){ } -record(longin,"$(PREFIX):TaskInfo:1:AdsPort"){ +record(longin,"$(PREFIX):TaskInfo:$(IDX):AdsPort"){ field(DTYP, "asynInt32") field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[1].AdsPort?") info(archive, "monitor 1: VAL") field(SCAN,"I/O Intr") } -record(longin,"$(PREFIX):TaskInfo:1:CycleCount"){ +record(longin,"$(PREFIX):TaskInfo:$(IDX):CycleCount"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") @@ -44,7 +45,7 @@ record(longin,"$(PREFIX):TaskInfo:1:CycleCount"){ info(archive, "monitor 1: VAL") } -record(longin,"$(PREFIX):TaskInfo:1:DcTaskTime"){ +record(longin,"$(PREFIX):TaskInfo:$(IDX):DcTaskTime"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") @@ -53,7 +54,7 @@ record(longin,"$(PREFIX):TaskInfo:1:DcTaskTime"){ info(archive, "monitor 1: VAL") } -record(longin,"$(PREFIX):TaskInfo:1:LastExecTime"){ +record(longin,"$(PREFIX):TaskInfo:$(IDX):LastExecTime"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") @@ -62,7 +63,7 @@ record(longin,"$(PREFIX):TaskInfo:1:LastExecTime"){ info(archive, "monitor 1: VAL") } -record(bi,"$(PREFIX):TaskInfo:1:FirstCycle"){ +record(bi,"$(PREFIX):TaskInfo:$(IDX):FirstCycle"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") @@ -73,7 +74,7 @@ record(bi,"$(PREFIX):TaskInfo:1:FirstCycle"){ info(archive, "monitor 1: VAL") } -record(bi,"$(PREFIX):TaskInfo:1:CycleTimeExceeded"){ +record(bi,"$(PREFIX):TaskInfo:$(IDX):CycleTimeExceeded"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") @@ -84,7 +85,7 @@ record(bi,"$(PREFIX):TaskInfo:1:CycleTimeExceeded"){ info(archive, "monitor 1: VAL") } -record(longin, "$(PREFIX):TaskInfo:1:ExceedCount_RBV"){ +record(longin, "$(PREFIX):TaskInfo:$(IDX):ExceedCount_RBV"){ field(DTYP, "asynInt32") field(INP, "@asyn($(PORT),0,1)ADSPORT=$(TASK1_PORT=350)/TS_MS=1000/.ADR.16#F200,16#100,4,19?") field(PINI, "1") @@ -93,7 +94,7 @@ record(longin, "$(PREFIX):TaskInfo:1:ExceedCount_RBV"){ info(archive, "monitor 1: VAL") } -record(bi,"$(PREFIX):TaskInfo:1:InCallAfterOutputUpdate"){ +record(bi,"$(PREFIX):TaskInfo:$(IDX):InCallAfterOutputUpdate"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") @@ -104,7 +105,7 @@ record(bi,"$(PREFIX):TaskInfo:1:InCallAfterOutputUpdate"){ info(archive, "monitor 1: VAL") } -record(bi,"$(PREFIX):TaskInfo:1:RTViolation"){ +record(bi,"$(PREFIX):TaskInfo:$(IDX):RTViolation"){ field(PINI, "1") field(TSE, -2) field(DTYP, "asynInt32") @@ -114,238 +115,3 @@ record(bi,"$(PREFIX):TaskInfo:1:RTViolation"){ field(ONAM,"TRUE") info(archive, "monitor 1: VAL") } - - -record(longin,"$(PREFIX):TaskInfo:2:CycleTime"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].CycleTime?") - field(SCAN,"I/O Intr") -info(archive, "monitor 1: VAL") -} - -record(longin,"$(PREFIX):TaskInfo:2:Priority"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].Priority?") - field(SCAN,"I/O Intr") - info(archive, "monitor 1: VAL") -} - -record(waveform,"$(PREFIX):TaskInfo:2:TaskName"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt8ArrayIn") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].TaskName?") - field(NELM,63) - field(SCAN,"I/O Intr") - field(FTVL,"CHAR") - info(archive, "monitor 1: VAL") -} - - -record(longin,"$(PREFIX):TaskInfo:2:AdsPort"){ - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].AdsPort?") - info(archive, "monitor 1: VAL") - field(SCAN,"I/O Intr") -} - -record(longin,"$(PREFIX):TaskInfo:2:CycleCount"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].CycleCount?") - field(SCAN,"I/O Intr") -info(archive, "monitor 1: VAL") -} - -record(longin,"$(PREFIX):TaskInfo:2:DcTaskTime"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].DcTaskTime?") - field(SCAN,"I/O Intr") -info(archive, "monitor 1: VAL") -} - -record(longin,"$(PREFIX):TaskInfo:2:LastExecTime"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].LastExecTime?") - field(SCAN,"I/O Intr") -info(archive, "monitor 1: VAL") -} - -record(bi,"$(PREFIX):TaskInfo:2:FirstCycle"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].FirstCycle?") - field(SCAN,"I/O Intr") - field(ZNAM,"FALSE") - field(ONAM,"TRUE") -info(archive, "monitor 1: VAL") -} - -record(bi,"$(PREFIX):TaskInfo:2:CycleTimeExceeded"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].CycleTimeExceeded?") - field(SCAN,"I/O Intr") - field(ZNAM,"FALSE") - field(ONAM,"TRUE") -info(archive, "monitor 1: VAL") -} - -record(longin, "$(PREFIX):TaskInfo:2:ExceedCount_RBV"){ - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=$(TASK2_PORT=351)/TS_MS=1000/.ADR.16#F200,16#100,4,19?") - field(PINI, "1") - field(SCAN, "I/O Intr") - field(TSE, "-2") - info(archive, "monitor 1: VAL") -} - -record(bi,"$(PREFIX):TaskInfo:2:InCallAfterOutputUpdate"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].InCallAfterOutputUpdate?") - field(SCAN,"I/O Intr") - field(ZNAM,"FALSE") - field(ONAM,"TRUE") -info(archive, "monitor 1: VAL") -} - -record(bi,"$(PREFIX):TaskInfo:2:RTViolation"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[2].RTViolation?") - field(SCAN,"I/O Intr") - field(ZNAM,"FALSE") - field(ONAM,"TRUE") -info(archive, "monitor 1: VAL") -} - -record(longin,"$(PREFIX):TaskInfo:3:CycleTime"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].CycleTime?") - field(SCAN,"I/O Intr") -info(archive, "monitor 1: VAL") -} - -record(longin,"$(PREFIX):TaskInfo:3:Priority"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].Priority?") - field(SCAN,"I/O Intr") - info(archive, "monitor 1: VAL") -} - -record(waveform,"$(PREFIX):TaskInfo:3:TaskName"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt8ArrayIn") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].TaskName?") - field(NELM,63) - field(SCAN,"I/O Intr") - field(FTVL,"CHAR") - info(archive, "monitor 1: VAL") -} - - -record(longin,"$(PREFIX):TaskInfo:3:AdsPort"){ - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].AdsPort?") - info(archive, "monitor 1: VAL") - field(SCAN,"I/O Intr") -} - -record(longin,"$(PREFIX):TaskInfo:3:CycleCount"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].CycleCount?") - field(SCAN,"I/O Intr") -info(archive, "monitor 1: VAL") -} - -record(longin,"$(PREFIX):TaskInfo:3:DcTaskTime"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].DcTaskTime?") - field(SCAN,"I/O Intr") -info(archive, "monitor 1: VAL") -} - -record(longin,"$(PREFIX):TaskInfo:3:LastExecTime"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].LastExecTime?") - field(SCAN,"I/O Intr") -info(archive, "monitor 1: VAL") -} - -record(bi,"$(PREFIX):TaskInfo:3:FirstCycle"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].FirstCycle?") - field(SCAN,"I/O Intr") - field(ZNAM,"FALSE") - field(ONAM,"TRUE") -info(archive, "monitor 1: VAL") -} - -record(bi,"$(PREFIX):TaskInfo:3:CycleTimeExceeded"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].CycleTimeExceeded?") - field(SCAN,"I/O Intr") - field(ZNAM,"FALSE") - field(ONAM,"TRUE") -info(archive, "monitor 1: VAL") -} - -record(longin, "$(PREFIX):TaskInfo:3:ExceedCount_RBV"){ - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=$(TASK3_PORT=352)/TS_MS=1000/.ADR.16#F200,16#100,4,19?") - field(PINI, "1") - field(SCAN, "I/O Intr") - field(TSE, "-2") - info(archive, "monitor 1: VAL") -} - -record(bi,"$(PREFIX):TaskInfo:3:InCallAfterOutputUpdate"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].InCallAfterOutputUpdate?") - field(SCAN,"I/O Intr") - field(ZNAM,"FALSE") - field(ONAM,"TRUE") -info(archive, "monitor 1: VAL") -} - -record(bi,"$(PREFIX):TaskInfo:3:RTViolation"){ - field(PINI, "1") - field(TSE, -2) - field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),0,1)ADSPORT=851/POLL_RATE=1/TwinCAT_SystemInfoVarList._TaskInfo[3].RTViolation?") - field(SCAN,"I/O Intr") - field(ZNAM,"FALSE") - field(ONAM,"TRUE") -info(archive, "monitor 1: VAL") -} diff --git a/app/src/Makefile b/app/src/Makefile index aba22b0..99e7c82 100644 --- a/app/src/Makefile +++ b/app/src/Makefile @@ -20,6 +20,7 @@ adsIoc_DBD += ads.dbd adsIoc_DBD += asyn.dbd adsIoc_DBD += drvAsynIPPort.dbd adsIoc_DBD += dbArchive.dbd +adsIoc_DBD += system.dbd ifneq (,$(findstring /,$(CAPUTLOG))) adsIoc_DBD += caPutLog.dbd diff --git a/configure/RELEASE b/configure/RELEASE index fd125e2..fdab345 100644 --- a/configure/RELEASE +++ b/configure/RELEASE @@ -42,6 +42,9 @@ CAPUTLOG=$(EPICS_MODULES)/caPutLog/$(CAPUTLOG_MODULE_VERSION) # =============================================================== EPICS_BASE=$(EPICS_SITE_TOP)/base/$(BASE_MODULE_VERSION) +# Set externally by tools that want to introspect Makefiles: +ifneq ($(_DEPENDENCY_CHECK_),1) + # Check for valid EPICS_BASE ifeq ($(wildcard $(EPICS_BASE)/include),) $(error Invalid EPICS_BASE: $(EPICS_BASE)/include) @@ -75,3 +78,5 @@ endif ifeq ($(wildcard $(CAPUTLOG)/lib/$(T_A)),) $(error Invalid CAPUTLOG: $(CAPUTLOG)/lib/$(T_A)) endif + +endif diff --git a/iocBoot/templates/Makefile.base b/iocBoot/templates/Makefile.base index 999ba37..a18ca42 100644 --- a/iocBoot/templates/Makefile.base +++ b/iocBoot/templates/Makefile.base @@ -13,6 +13,7 @@ SHELL = /bin/bash # Set defaults: PYTMC_OPTS ?= +PYTMC_TEMPLATE_OPTS ?= PYTMC_DB_OPTS ?= PREFIX ?= HOST_ARCH ?= rhel7-x86_64 @@ -21,30 +22,45 @@ DBD_PATH ?= $(IOC_TOP)/dbd/adsIoc.dbd PROJECT_PATH ?= $(IOC_INSTANCE_PATH)/../../$(PROJECT_NAME)/$(PROJECT_NAME).tsproj TEMPLATE_PATH ?= $(IOC_TOP)/iocBoot/templates STCMD_TEMPLATE ?= st.cmd.template -IOC_NAME := $(shell basename "$(IOC_INSTANCE_PATH)") +ADS_IOC_NAME := $(shell basename "$(IOC_INSTANCE_PATH)") PRODUCTION_IOC ?= 0 -PYTMC_MIN_VERSION ?= 2.7.7 - -ifeq ($(PRODUCTION_IOC), 1) - IOC_DATA_PATH ?= /reg/d/iocData -else - IOC_DATA_PATH ?= $(HOME)/iocData -endif - -ARCHIVE_PATH ?= $(IOC_DATA_PATH)/$(IOC_NAME)/archive +PYTMC_MIN_VERSION ?= 2.14.0 +# The minimum callback queue size for EPICS IOCs: +QUEUE_SIZE_BASE ?= 2000 +# Additional records generated from the PLC project will increase the callback +# queue size, scaled by this number: +QUEUE_SIZE_SCALE ?= 2 +# The separator for PV names: +PV_DELIMITER ?= ':' +# The PLC hostname which can be used in place of the auto-detected IP address: +PLC_HOSTNAME ?= +# The PLC AMS Net ID which can be used in place of the auto-detected one: +PLC_AMS_NET_ID ?= +# Ignore linter errors, if possible. This is disabled by default. +IGNORE_LINTER_ERRORS ?= 0 +# Add a PLC route automatically at startup +ADD_PLC_ROUTE ?= 1 +# IOC host IP regular expression +IOC_HOST_IP_REGEX ?= ^172.*$ +# This is where IOC data goes: +IOC_DATA_PATH ?= /cds/data/iocData +# This is where the scripts and database are built: BUILD_PATH ?= $(IOC_INSTANCE_PATH)/.pytmc_build +# This is the default set of macro parameters used for all database files, +# including $PREFIX, $IOCNAME, and $IOC. DB_PARAMETERS ?= 'PREFIX=$(PREFIX):,IOCNAME=$$(IOC),IOC=$$(IOC)' +# Additional database files can be specified in this space-delimited list: +ADDITIONAL_DB_FILES ?= # abspath is failing with spaces - falling back to Python here: pyabspath = $(shell python -c "import os, sys; print(os.path.abspath(os.path.expanduser(sys.argv[1])))" "$(1)" ) -all: build paths clean +all: build clean help: @echo "Available commands:" @echo " all: builds the IOC and cleans the temporary files" @echo " build: builds the st.cmd, databases, envPaths, etc." @echo " clean: cleans temporary pytmc files in $(BUILD_PATH)" - @echo " paths: make relevant directories (autosave, etc.)" @echo " lint: lint the pragmas" @echo " lintdb: lint the generated .db file against the IOC dbd" @echo " summary: tool shortcut - display a PLC project summary" @@ -60,21 +76,32 @@ help: _check_versions: - @python -c "import sys, pytmc, distutils.version; print('* Found pytmc v', pytmc.__version__); sys.exit(not (distutils.version.LooseVersion(pytmc.__version__) >= distutils.version.LooseVersion('$(PYTMC_MIN_VERSION)')))" || (echo "A newer pytmc is required: >= $(PYTMC_MIN_VERSION)" && exit 1) + @python -c "import sys, pytmc, distutils.version; print('* Found pytmc v', pytmc.__version__); sys.exit(not (distutils.version.LooseVersion(pytmc.__version__) >= distutils.version.LooseVersion('$(PYTMC_MIN_VERSION)')))" || (echo "A newer pytmc is required: >= $(PYTMC_MIN_VERSION)" 2> /dev/null && exit 1) st.cmd: _check_versions - @echo "Executing pytmc stcmd: creating st.cmd..." + @echo "Executing pytmc template: creating st.cmd and database files..." cd "$(BUILD_PATH)" && \ - pytmc stcmd \ - --name "$(IOC_NAME)" \ - --plc "$(PLC)" \ - --template-path "$(TEMPLATE_PATH)" \ - --template "$(STCMD_TEMPLATE)" \ - --hashbang "$(BINARY_PATH)" \ - --only-motor \ - -p "$(PREFIX)" \ + pytmc template \ + --macro prefix="$(PREFIX)" \ + --macro plc="$(PLC)" \ + --macro ads_ioc_name="$(ADS_IOC_NAME)" \ + --macro user="$(USER)" \ + --macro hashbang="$(BINARY_PATH)" \ + --macro db_parameters=$(DB_PARAMETERS) \ + --macro queue_size_base=$(QUEUE_SIZE_BASE) \ + --macro queue_size_scale=$(QUEUE_SIZE_SCALE) \ + --macro pv_delimiter=$(PV_DELIMITER) \ + --macro plc_hostname_override='$(PLC_HOSTNAME)' \ + --macro plc_ams_net_id_override='$(PLC_AMS_NET_ID)' \ + --macro ignore_linter_errors='$(IGNORE_LINTER_ERRORS)' \ + --macro ioc_host_ip_regex='$(IOC_HOST_IP_REGEX)' \ + --macro add_plc_route='$(ADD_PLC_ROUTE)' \ + --macro production_ioc='$(PRODUCTION_IOC)' \ + --macro additional_db_files='$(ADDITIONAL_DB_FILES)' \ + --template "$(TEMPLATE_PATH)/$(STCMD_TEMPLATE)":"st.cmd" \ $(PYTMC_OPTS) \ - "$(call pyabspath,$(PROJECT_PATH))" > st.cmd + $(PYTMC_TEMPLATE_OPTS) \ + "$(call pyabspath,$(PROJECT_PATH))" db: _check_versions @echo "Executing pytmc db: creating $(PLC).db and $(PLC).archive..." @@ -86,14 +113,14 @@ db: _check_versions "$(PLC).db" envPaths: - @echo "* Copying envPaths and fixing IOC_TOP..." + @echo "* Copying envPaths and fixing variables..." @cat "$(TEMPLATE_PATH)"/envPaths | \ - sed -e 's/^epicsEnvSet("IOC",.*)/epicsEnvSet("IOC","$(IOC_NAME)")/' \ + sed -e 's/^epicsEnvSet("IOC",.*)/epicsEnvSet("IOC","$${IOC=$(ADS_IOC_NAME)}")/' \ > "$(IOC_INSTANCE_PATH)"/envPaths @echo 'epicsEnvSet("IOC_TOP", "$(IOC_INSTANCE_PATH)")' >> "$(IOC_INSTANCE_PATH)"/envPaths @echo 'epicsEnvSet("IOC_DATA", "$(IOC_DATA_PATH)")' >> "$(IOC_INSTANCE_PATH)"/envPaths -install: +copy_build_output: @echo "" @echo "* Copying st.cmd and databases..." @ if [ -f "$(IOC_INSTANCE_PATH)"/st.cmd ]; then \ @@ -102,51 +129,32 @@ install: fi @if test -n "`shopt -s nullglob; echo "$(BUILD_PATH)"/*.db`"; then \ - echo "* Found database files to install; creating load_plc_databases.cmd..."; \ - find "$(BUILD_PATH)" -name '*.db' -print0 | \ - PREFIX=$(PREFIX) DELIM=":" DB_PARAMETERS=$(DB_PARAMETERS) \ - python $(TEMPLATE_PATH)/dbloads.py \ - > "$(BUILD_PATH)/load_plc_databases.cmd"; \ - \ + echo "* Found database files to install..."; \ for filename in "$(BUILD_PATH)"/*.db; \ do \ echo "* Installing: $$(basename $$filename)"; \ install -p -m 0444 "$$filename" "$(IOC_INSTANCE_PATH)"; \ done \ - else \ - touch "$(BUILD_PATH)/load_plc_databases.cmd"; \ fi @echo "* Installing: st.cmd" install -p -m 0555 "$(BUILD_PATH)/st.cmd" "$(IOC_INSTANCE_PATH)"; - @echo "* Installing: load_plc_databases.cmd" - install -p -m 0444 "$(BUILD_PATH)/load_plc_databases.cmd" "$(IOC_INSTANCE_PATH)"; - @shopt -s nullglob; \ for filename in "$(BUILD_PATH)"/*.archive; \ do \ - if [ -d "$(ARCHIVE_PATH)" ]; then \ - echo "* Installing archive file: $$(basename $$filename)"; \ - install -p -m 0444 "$$filename" "$(ARCHIVE_PATH)"; \ - else \ - echo "* Warning: ${ARCHIVE_PATH} does not exist;" \ - "copying archive file to the IOC instance path."; \ - install -p -m 0444 "$$filename" "$(IOC_INSTANCE_PATH)"; \ - fi; \ + install -p -m 0444 "$$filename" "$(IOC_INSTANCE_PATH)"; \ done @cd "$(IOC_INSTANCE_PATH)" - build: @echo "-----------------------------------------------------------" @echo "Build path: $(BUILD_PATH)" @echo "PLC: $(PLC)" - @echo "IOC name: $(IOC_NAME)" + @echo "IOC name: $(ADS_IOC_NAME) (* Does this match IOC Manager? *)" @echo "PYTMC_OPTS: $(PYTMC_OPTS)" @echo "PROJECT_PATH: $(PROJECT_PATH)" - @echo "ARCHIVE_PATH: $(ARCHIVE_PATH)" @echo "Absolute project path: " $(call pyabspath,$(PROJECT_PATH)) @echo "-----------------------------------------------------------" @echo "" @@ -155,9 +163,8 @@ build: mkdir -p "$(BUILD_PATH)" @$(MAKE) st.cmd - @$(MAKE) db @$(MAKE) envPaths - @$(MAKE) install + @$(MAKE) copy_build_output @echo "" @echo "Build complete." @@ -168,20 +175,12 @@ clean: @echo "* Cleaning..." @if [ -d "$(BUILD_PATH)" ]; then \ shopt -s nullglob; \ - rm -f "$(BUILD_PATH)"/st.cmd "$(BUILD_PATH)"/load_plc_databases.cmd \ - "$(BUILD_PATH)"/*.db "$(BUILD_PATH)"/*.archive; \ + rm -f "$(BUILD_PATH)"/st.cmd "$(BUILD_PATH)"/*.db "$(BUILD_PATH)"/*.archive; \ rmdir "$(BUILD_PATH)"; \ fi @echo "Done" -paths: - @echo "Creating IOC data directory" - install --mode 0775 --group ps-ioc --directory $(IOC_DATA_PATH)/$(IOC_NAME)/{iocInfo,archive,logs,autosave} - @echo "Creating autosave files directory" - install --mode 0775 --group ps-ioc --directory $(IOC_INSTANCE_PATH)/autosave - - lint: _check_versions @echo "Linting the pytmc pragmas. Use \`make $@ PYTMC_OPTS=...\` to pass args to pytmc." > /dev/stderr pytmc pragmalint "$(call pyabspath,$(PROJECT_PATH))" $(PYTMC_OPTS) @@ -220,4 +219,4 @@ types: _check_versions pytmc types "$(call pyabspath,$(PROJECT_PATH))" $(PYTMC_OPTS) -.PHONY: all _check_versions st.cmd db envPaths install build clean paths lint lintdb summary code outline debug types +.PHONY: all _check_versions st.cmd db envPaths copy_build_output build clean lint lintdb summary code outline debug types diff --git a/iocBoot/templates/Makefile.ioc b/iocBoot/templates/Makefile.ioc index 89ef18a..75b8b07 100644 --- a/iocBoot/templates/Makefile.ioc +++ b/iocBoot/templates/Makefile.ioc @@ -8,11 +8,18 @@ PROJECT_NAME := {{ project_name }} PROJECT_PATH := {{ tsproj_path }} PLC := {{ plc_name }} -PYTMC_OPTS := +PYTMC_OPTS := PREFIX := PLC:$(PLC) # With two $$, as in $$(IOC) below, this will be expanded in the # environment of st.cmd: DB_PARAMETERS := 'PREFIX=$(PREFIX):,IOCNAME=$$(IOC),IOC=$$(IOC)' +# If you know the netconfig name of the PLC, enter it here. If specified, this +# will be used in place of the auto-detected IP address from the PLC project: +PLC_HOSTNAME := +# You may also specify the AMS Net ID of the PLC, but it's usually OK to +# leave this blank as it can be found in the project: +PLC_AMS_NET_ID := + include $(IOC_TOP)/iocBoot/templates/Makefile.base diff --git a/iocBoot/templates/dbloads.py b/iocBoot/templates/dbloads.py deleted file mode 100644 index 60ea0ae..0000000 --- a/iocBoot/templates/dbloads.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -''' -Usage: dbloads.py -Input: list of all database db_filenames, delimited by \0 -Output: list of dbLoadRecords(...) calls - -Relevant environment variables: - * DB_PARAMETERS: parameters to pass to dbLoadRecords() - * QUEUE_SIZE_BASE: the minimum callback queue size - * QUEUE_SIZE_SCALE: total_records * scale - -The callback queue size will be calculated as follows: - - (QUEUE_SIZE_SCALE * total_records) + QUEUE_SIZE_BASE -''' - -import os -import sys - - -def count_number_of_records(db_filename): - """Count the number of records in `db_filename`.""" - with open(db_filename, 'rt') as f: - return len( - [line for line in f.readlines() - if line.startswith('record(')] - ) - - -def load_record_entry(db_filename): - """Format dbLoadRecords() based on env variable DB_PARAMETERS.""" - env = dict(os.environ) - db_filename = os.path.split(db_filename)[-1] - env['db_filename'] = db_filename - - return ( - 'dbLoadRecords("{db_filename}", "PORT=ASYN_PLC,{DB_PARAMETERS}")' - ''.format(**env) - ) - - -def main(): - db_filenames = sys.stdin.read().strip('\0').split('\0') - - if db_filenames: - total_records = sum(count_number_of_records(fn) for fn in db_filenames) - else: - total_records = 0 - - # Multiply the number of records by the scale: - queue_size_scale = int(os.environ.get('QUEUE_SIZE_SCALE', '2')) - - # The minimum size of the queue, outside of those for additional records: - queue_size_base = int(os.environ.get('QUEUE_SIZE_BASE', '2000')) - queue_size = queue_size_scale * total_records + queue_size_base - - print('# Total records: {0}'.format(total_records)) - print('callbackSetQueueSize({0})'.format(queue_size)) - print('\n'.join(load_record_entry(fn) for fn in db_filenames)) - - -if __name__ == '__main__': - main() diff --git a/iocBoot/templates/st.cmd.template b/iocBoot/templates/st.cmd.template index 2d3c53d..0732511 100644 --- a/iocBoot/templates/st.cmd.template +++ b/iocBoot/templates/st.cmd.template @@ -1,5 +1,60 @@ #!{{hashbang}} -###### AUTO-GENERATED DO NOT EDIT ############## +################### AUTO-GENERATED DO NOT EDIT ################### +{% set production_ioc = production_ioc | int %} +{%- set project, plc = get_plc_by_name(projects, plc) -%} +{%- set plc_host = plc_hostname_override or plc.target_ip %} +{%- set plc_ams_net_id = plc_ams_net_id_override or plc.ams_id %} +# +# Project: {{ project.filename.name }} +# PLC name: {{ plc.name }} ({{ plc.link_name }}) +# Generated using: pytmc {{ pytmc_version}} +# Project version: {{ project.git_info.describe }} +# Project hash: {{ project.git_info.sha }} +{% if plc_hostname_override %} +# PLC IP/host: {{ plc_hostname_override }} (Specified in Makefile; project has: {{ plc.target_ip }}) +{% else %} +# PLC IP/host: {{ plc.target_ip}} +{% endif %} +{% if plc_ams_net_id_override %} +# PLC Net ID: {{ plc_ams_net_id_override }} (Specified in Makefile; project has: {{ plc.ams_id }}) +{% else %} +# PLC Net ID: {{ plc.ams_id}} +{% endif %} +{% if production_ioc %} +# ** Production mode IOC ** +# Using /cds/data/iocData for autosave and archiver settings. +{% else %} +# ** DEVELOPMENT MODE IOC ** +# * Using IOC boot directory for autosave. +# * Archiver settings will not be configured. +{% endif %} +# +# Libraries: +# +{% for library, info in get_library_versions(plc) | dictsort %} +{% if "PlaceholderReference" in info and "PlaceholderResolution" in info %} +# {{ library }}: {{ info.PlaceholderReference.version }} -> {{ info.PlaceholderResolution.version }} ({{ info.PlaceholderResolution.vendor }}) +{% elif "PlaceholderReference" in info %} +# {{ library }}: {{ info.PlaceholderReference.version }} ({{ info.PlaceholderReference.vendor }}) +{% endif %} +{% endfor %} +# +################### AUTO-GENERATED DO NOT EDIT ################### +{%- if plc.tmc %} + +{%- set allow_errors = ignore_linter_errors | int %} +{%- set db_filenames, records = generate_records(plc, allow_errors=allow_errors) %} +{%- set num_records = records | length %} +{%- set max_params = num_records + 1000 %} + +{%- else %} + +{%- set db_filenames = [] %} +{%- set records = {} %} +{%- set num_records = 0 %} +{%- set max_params = 10000 %} + +{%- endif %} < envPaths @@ -10,23 +65,30 @@ epicsEnvSet("LOCATION", "{{prefix}}" ) epicsEnvSet("IOCSH_PS1", "$(IOC)> " ) epicsEnvSet("ACF_FILE", "$(ADS_IOC_TOP)/iocBoot/templates/unrestricted.acf") +{% if production_ioc %} # Run common startup commands for linux soft IOC's < /reg/d/iocCommon/All/pre_linux.cmd +{% endif %} # Register all support components dbLoadDatabase("$(ADS_IOC_TOP)/dbd/adsIoc.dbd") adsIoc_registerRecordDeviceDriver(pdbbase) -epicsEnvSet("ASYN_PORT", "{{asyn_port}}") -epicsEnvSet("IPADDR", "{{plc_ip}}") -epicsEnvSet("AMSID", "{{plc_ams_id}}") -epicsEnvSet("AMS_PORT", "{{plc_ads_port}}") -epicsEnvSet("ADS_MAX_PARAMS", "10000") +epicsEnvSet("ASYN_PORT", "ASYN_PLC") +epicsEnvSet("IPADDR", "{{ plc_host }}") +epicsEnvSet("AMSID", "{{ plc_ams_net_id }}") +epicsEnvSet("AMS_PORT", "{{ plc.port }}") +epicsEnvSet("ADS_MAX_PARAMS", "{{ max_params }}") epicsEnvSet("ADS_SAMPLE_MS", "50") epicsEnvSet("ADS_MAX_DELAY_MS", "100") epicsEnvSet("ADS_TIMEOUT_MS", "1000") epicsEnvSet("ADS_TIME_SOURCE", "0") +{% if add_plc_route | int != 0 %} +# Add a route to the PLC automatically: +system("${ADS_IOC_TOP}/scripts/add_route.sh {{ plc_host }} {{ ioc_host_ip_regex }}") + +{% endif %} # adsAsynPortDriverConfigure(portName, ipaddr, amsaddr, amsport, # asynParamTableSize, priority, noAutoConnect, defaultSampleTimeMS, # maxDelayTimeMS, adsTimeoutMS, defaultTimeSource) @@ -54,10 +116,12 @@ adsAsynPortDriverConfigure("$(ASYN_PORT)", "$(IPADDR)", "$(AMSID)", "$(AMS_PORT) cd "$(ADS_IOC_TOP)/db" +{% set motors = get_motors(plc) %} {% if motors %} +{%- set nc = get_nc(project) %} -epicsEnvSet("MOTOR_PORT", "{{motor_port}}") -epicsEnvSet("PREFIX", "{{prefix}}{{delim}}") +epicsEnvSet("MOTOR_PORT", "PLC_ADS") +epicsEnvSet("PREFIX", "{{prefix}}{{pv_delimiter}}") epicsEnvSet("NUMAXES", "{{nc.axis_by_id|max}}") epicsEnvSet("MOVE_POLL_RATE", "200") epicsEnvSet("IDLE_POLL_RATE", "1000") @@ -109,41 +173,85 @@ dbLoadRecords("EthercatMCdebug.template", "PREFIX=$(MOTOR_PREFIX), MOTOR_NAME=$( {% endif %} dbLoadRecords("iocSoft.db", "IOC={{prefix}}") -dbLoadRecords("save_restoreStatus.db", "P={{prefix}}{{delim}}") -dbLoadRecords("caPutLog.db", "IOC=${IOC}") +dbLoadRecords("save_restoreStatus.db", "P={{prefix}}{{pv_delimiter}}") +dbLoadRecords("caPutLog.db", "IOC=$(IOC)") -## TwinCat System Databse files ## -dbLoadRecords("TwinCAT_TaskInfo.db", "PORT={{ asyn_port }}, PREFIX={{prefix}}") -dbLoadRecords("TwinCAT_AppInfo.db", "PORT={{ asyn_port }}, PREFIX={{prefix}}") +## TwinCAT task, application, and project information databases ## +{% for priority, (_, task) in plc.instance.task_pous.items() %} +dbLoadRecords("TwinCAT_TaskInfo.db", "PORT=$(ASYN_PORT),PREFIX={{prefix}},IDX={{task.array_index}}") +{% endfor %} +dbLoadRecords("TwinCAT_AppInfo.db", "PORT=$(ASYN_PORT), PREFIX={{prefix}}") + +{% set git_sha = project.git_info.sha or "" %} +{%- set short_git_sha = git_sha[:7] %} +dbLoadRecords("TwinCAT_Project.db", "PREFIX={{prefix}},PROJECT={{ project.filename.name }},HASH={{ short_git_sha }},VERSION={{ project.git_info.describe }},PYTMC={{ pytmc_version}},PLC_HOST={{ plc_host }}") + +{% for library, info in get_library_versions(plc) | dictsort %} +{%- set libraryname = library | replace(" ", "_") %} +{%- if "PlaceholderReference" in info and "PlaceholderResolution" in info %} +# {{ library }}: {{ info.PlaceholderReference.version }} -> {{ info.PlaceholderResolution.version }} ({{ info.PlaceholderResolution.vendor }}) +dbLoadRecords("TwinCAT_Dependency.db", "PREFIX={{prefix}},DEPENDENCY={{ libraryname }},VERSION={{ info.PlaceholderResolution.version }},VENDOR={{ info.PlaceholderResolution.vendor }}") +{% elif "PlaceholderReference" in info %} +# {{ library }}: {{ info.PlaceholderReference.version }} ({{ info.PlaceholderReference.vendor }}) +dbLoadRecords("TwinCAT_Dependency.db", "PREFIX={{prefix}},DEPENDENCY={{ libraryname }},VERSION={{ info.PlaceholderReference.version }},VENDOR={{ info.PlaceholderReference.vendor }}") +{% endif %} +{%- endfor %} cd "$(IOC_TOP)" -## Database files ## -< "$(IOC_TOP)/load_plc_databases.cmd" +## PLC Project Database files ## +{% if plc.tmc %} +{% for db in db_filenames %} +dbLoadRecords("{{ db }}", "PORT=$(ASYN_PORT),{{ db_parameters }}") +{% endfor %} +{%- set queue_size_base = queue_size_base | int %} +{%- set queue_size_scale = queue_size_scale | int %} +{%- set callback_queue_size = queue_size_base + queue_size_scale * num_records %} -{% if additional_db_files %} -{% for db in additional_db_files %} -dbLoadRecords("{{ db.file }}", "PORT={{ asyn_port }},PREFIX={{prefix}}{{delim}},IOCNAME=$(IOC),{{ db.macros }}") +# Total records: {{ records | length }} +callbackSetQueueSize({{ callback_queue_size }}) +{% endif %} +{% if additional_db_files %} +{% for db in additional_db_files.split(" ") %} +# Load user-specified database: {{ db }} +dbLoadRecords("{{ db }}", "PORT=$(ASYN_PORT),{{ db_parameters }}") {% endfor %} -{% endif %} -# Setup autosave -set_savefile_path( "$(IOC_DATA)/$(IOC)/autosave" ) -set_requestfile_path( "$(IOC_TOP)/autosave" ) +{% endif %} +# Autosave and archive settings: +save_restoreSet_status_prefix("{{prefix}}{{pv_delimiter}}") +save_restoreSet_IncompleteSetsOk(1) +save_restoreSet_DatedBackupFiles(1) +set_pass0_restoreFile("info_positions.sav") +set_pass1_restoreFile("info_settings.sav") + +{% if production_ioc %} +# ** Production IOC Settings ** +set_savefile_path("$(IOC_DATA)/$(IOC)/autosave") +set_requestfile_path("$(IOC_DATA)/$(IOC)/autosave") + +# Production IOC autosave files go in iocData: +cd "$(IOC_DATA)/$(IOC)/autosave" + +# Create info_positions.req and info_settings.req +makeAutosaveFiles() -save_restoreSet_status_prefix( "{{prefix}}{{delim}}" ) -save_restoreSet_IncompleteSetsOk( 1 ) -save_restoreSet_DatedBackupFiles( 1 ) -set_pass0_restoreFile( "info_positions.sav" ) -set_pass1_restoreFile( "info_settings.sav" ) +cd "$(IOC_DATA)/$(IOC)/archive" -cd "$(IOC_TOP)/autosave" -makeAutosaveFiles() +# Create $(IOC).archive +makeArchiveFromDbInfo("$(IOC).archive", "archive") +cd "$(IOC_TOP)" +{% else %} +# ** Development IOC Settings ** +# Development IOC autosave and archive files go in the IOC top directory: cd "$(IOC_TOP)" -# Create the archiver file -makeArchiveFromDbInfo("$(IOC_DATA)/$(IOC)/archive/$(IOC).archive", "archive") +# (Development mode) Create info_positions.req and info_settings.req +makeAutosaveFiles() +# (Development mode) Create the archiver file +makeArchiveFromDbInfo("$(IOC).archive", "archive") +{% endif %} # Configure access security: this is required for caPutLog. asSetFilename("$(ACF_FILE)") @@ -155,7 +263,7 @@ iocInit() iocLogInit() # Configure and start the caPutLogger after iocInit -epicsEnvSet(EPICS_AS_PUT_LOG_PV, "${IOC}:caPutLog:Last") +epicsEnvSet(EPICS_AS_PUT_LOG_PV, "$(IOC):caPutLog:Last") # caPutLogInit("HOST:PORT", config) # config options: @@ -163,11 +271,13 @@ epicsEnvSet(EPICS_AS_PUT_LOG_PV, "${IOC}:caPutLog:Last") # caPutLogOnChange 0: log only on value change # caPutLogAll 1: log all puts # caPutLogAllNoFilter 2: log all puts no filtering on same PV -caPutLogInit("${EPICS_CAPUTLOG_HOST}:${EPICS_CAPUTLOG_PORT}", 0) +caPutLogInit("$(EPICS_CAPUTLOG_HOST):$(EPICS_CAPUTLOG_PORT)", 0) # Start autosave backups create_monitor_set( "info_positions.req", 10, "" ) create_monitor_set( "info_settings.req", 60, "" ) +{% if production_ioc %} # All IOCs should dump some common info after initial startup. < /reg/d/iocCommon/All/post_linux.cmd +{% endif %} diff --git a/scripts/add_route.sh b/scripts/add_route.sh new file mode 100755 index 0000000..77e101c --- /dev/null +++ b/scripts/add_route.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# Usage: add_route.sh [plc-hostname] [ip-regex] + +if [ $# -ne 2 ]; then + echo "Usage: $0 [plc hostname] [ip regex]" > /dev/stderr + exit 1 +fi + +CONDA_BIN=${CONDA_BIN=/cds/group/pcds/pyps/conda/py39/envs/pcds-5.4.1/bin/conda} + +if [ ! -f "$CONDA_BIN" ]; then + echo "Conda unavailable. Skipping route addition." > /dev/stderr + exit 1 +fi + +set -e + +add_route() { + local plc_host + local ioc_hostname + local ioc_host_ip + + plc_host=$1 + ioc_hostname=$2 + ioc_host_ip=$3 + ioc_host_net_id="${ioc_host_ip}.1.1" + + if [ -z "$plc_host" ]; then + echo "PLC hostname unspecified" > /dev/stderr + exit 1 + fi + + if [ -z "$ioc_host_ip" ]; then + echo "IOC IP address unspecified" > /dev/stderr + exit 1 + fi + + if [ -z "$ioc_hostname" ]; then + echo "IOC hostname unspecified" > /dev/stderr + exit 1 + fi + + echo "Running ads-async to add a route to ${ioc_hostname} for IOC host ${ioc_hostname} (${ioc_host_ip})..." + set -x + $CONDA_BIN run ads-async route \ + --route-name="${ioc_hostname}" \ + "${plc_host}" \ + "${ioc_host_net_id}" \ + "${ioc_host_ip}" + { set +x; } 2> /dev/null + echo "Done." +} + +find_ioc_ip() { + local ipaddr + local regex + regex=$1 + + if [ -z "$regex" ]; then + echo "IOC IP match regex unspecified" > /dev/stderr + exit 1 + fi + + for ipaddr in $(hostname -I); do + if [[ "$ipaddr" =~ ${regex} ]]; then + echo "$ipaddr" + return + fi + done +} + + +plc_host=$1 +host_ip_regex=$2 +ioc_hostname=$(hostname -s) +ioc_ip=$(find_ioc_ip "${host_ip_regex}") + +add_route "${plc_host}" "${ioc_hostname}" "${ioc_ip}"