refactor: atInit changed to afterIocRunning

This commit is contained in:
Jerzy Jamroz
2025-06-19 10:48:57 +02:00
committed by Andrew Johnson
parent 2fc955bf8a
commit 70d5df05c7
8 changed files with 118 additions and 108 deletions

View File

@@ -1,8 +1,8 @@
### New `atInit` IOC Shell Command Added
### New `afterIocRunning` IOC Shell Command Added
This release incorporates [PR #558](https://github.com/epics-base/epics-base/pull/558) which added a new IOC shell command `atInit`. This command allows startup scripts to schedule arbitrary commands to be executed automatically after the IOC initialization phase (`iocInit`).
This release incorporates [PR #558](https://github.com/epics-base/epics-base/pull/558) which added a new IOC shell command `afterIocRunning`. This command allows startup scripts to schedule arbitrary commands to be executed automatically after the IOC initialization phase (`iocInit`).
`atInit` allows you to write better-structured IOC shell files to include in your startup scripts without tracking where `iocInit` is located (and how IOC is deployed) e.g.:
`afterIocRunning` allows you to write better-structured IOC shell files to include in your startup scripts without tracking where `iocInit` is located (and how IOC is deployed) e.g.:
- to achieve the best maintainability (e.g. encapsulation of the context into one file),
- to improve writing boot sequences,
- to improve IOC startup flexibility and scripting capabilities,
@@ -17,7 +17,7 @@ This release incorporates [PR #558](https://github.com/epics-base/epics-base/pul
- Executes following `iocInit` and `autosave` initialization (important for proper PV configuration).
- Supports any valid IOC shell command as an argument.
- Example usages:
- `atInit "dbpf <PV> <VAL>"`
- `atInit "date"`
- `atInit "dbpf $(P)EvtClkSource-Sel 'Upstream (fanout)'"`
- `atInit "dbpf $(P)Enable-Sel Enabled"`
- `afterIocRunning "dbpf <PV> <VAL>"`
- `afterIocRunning "date"`
- `afterIocRunning "dbpf $(P)EvtClkSource-Sel 'Upstream (fanout)'"`
- `afterIocRunning "dbpf $(P)Enable-Sel Enabled"`

View File

@@ -16,7 +16,7 @@ Com_SRCS += iocsh.cpp
Com_SRCS += initHooks.c
Com_SRCS += registry.c
Com_SRCS += libComRegister.c
Com_SRCS += atInit.c
Com_SRCS += afterIocRunning.c
iocsh_CXXFLAGS += -DEPICS_COMMANDLINE_LIBRARY=EPICS_COMMANDLINE_LIBRARY_$(COMMANDLINE_LIBRARY)
iocsh_INCLUDES += $(INCLUDES_$(COMMANDLINE_LIBRARY))

View File

@@ -13,18 +13,20 @@
#include <iocsh.h>
#include <string.h>
#include "atInit.h"
#include "afterIocRunning.h"
static const iocshArg atInitArg0 = {"command (before iocInit)", iocshArgString};
static const iocshArg *const atInitArgs[] = {&atInitArg0};
static const iocshFuncDef atInitDef = {
"atInit",
static const iocshArg afterIocRunningArg0 = {"command (before iocInit)", iocshArgString};
static const iocshArg *const afterIocRunningArgs[] = {&afterIocRunningArg0};
static const iocshFuncDef afterIocRunningDef = {
"afterIocRunning",
1,
atInitArgs,
afterIocRunningArgs,
"Allows you to define commands to be run after the iocInit\n"
"Example commands:\n"
" atInit \"dbpf <PV> <VAL>\"\n"
" atInit \"date\"\n"};
" afterIocRunning \"dbpf <PV> <VAL>\"\n"
" afterIocRunning \"date\"\n"
" afterIocRunning \"dbpf $(P)EvtClkSource-Sel 'Upstream (fanout)'\"\n"
" afterIocRunning \"dbpf $(P)Enable-Sel Enabled\"\n"};
struct cmditem {
ELLNODE node;
@@ -34,7 +36,7 @@ struct cmditem {
static ELLLIST cmdList = ELLLIST_INIT;
static int initEndFlag = 0; // Defines the end of the initialization
static void atInitHook(const initHookState state)
static void afterIocRunningHook(const initHookState state)
{
struct cmditem *item = NULL;
@@ -42,10 +44,10 @@ static void atInitHook(const initHookState state)
return;
while ((item = (struct cmditem *)ellGet(&cmdList))) {
printf(ANSI_GREEN("atInit:") " %s\n", item->cmd);
printf(ANSI_GREEN("afterIocRunning:") " %s\n", item->cmd);
if (iocshCmd(item->cmd))
printf(ERL_ERROR " atInit: "
printf(ERL_ERROR " afterIocRunning: "
"command '%s' failed to run\n",
item->cmd);
@@ -58,7 +60,7 @@ static void atInitHook(const initHookState state)
static struct cmditem *newItem(const char *cmd)
{
const size_t cmd_len = strlen(cmd) + 1;
struct cmditem *const item = mallocMustSucceed(sizeof(struct cmditem) + cmd_len, "atInit");
struct cmditem *const item = mallocMustSucceed(sizeof(struct cmditem) + cmd_len, "afterIocRunning");
item->cmd = (char *)(item + 1);
memcpy(item->cmd, cmd, cmd_len);
@@ -67,18 +69,18 @@ static struct cmditem *newItem(const char *cmd)
return item;
}
static void atInitFunc(const iocshArgBuf *args)
static void afterIocRunningFunc(const iocshArgBuf *args)
{
const char *const cmd = args[0].sval;
if (initEndFlag) {
printf(ERL_WARNING " atInit: "
printf(ERL_WARNING " afterIocRunning: "
"can only be used before 'iocInit'\n");
return;
}
if (!cmd || !cmd[0]) {
printf(ERL_ERROR " atInit: "
printf(ERL_ERROR " afterIocRunning: "
"received an empty 'command' argument\n");
return;
}
@@ -86,12 +88,12 @@ static void atInitFunc(const iocshArgBuf *args)
newItem(cmd);
}
void atInitRegister(void)
void afterIocRunningRegister(void)
{
static int first_time = 1;
if (first_time) {
first_time = 0;
iocshRegister(&atInitDef, atInitFunc);
initHookRegister(atInitHook);
iocshRegister(&afterIocRunningDef, afterIocRunningFunc);
initHookRegister(afterIocRunningHook);
}
}

View File

@@ -6,9 +6,9 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#ifndef INC_atInit_H
#define INC_atInit_H
#ifndef INC_afterIocRunning_H
#define INC_afterIocRunning_H
void atInitRegister(void);
void afterIocRunningRegister(void);
#endif /* INC_atInit_H */
#endif /* INC_afterIocRunning_H */

View File

@@ -27,7 +27,7 @@
#include "epicsGeneralTime.h"
#include "freeList.h"
#include "libComRegister.h"
#include "atInit.h"
#include "afterIocRunning.h"
/* Register the PWD environment variable when the cd IOC shell function is
* registered. This variable contains the current directory path.
@@ -513,7 +513,7 @@ void epicsStdCall libComRegister(void)
iocshRegister(&generalTimeReportFuncDef,generalTimeReportCallFunc);
iocshRegister(&installLastResortEventProviderFuncDef, installLastResortEventProviderCallFunc);
atInitRegister();
afterIocRunningRegister();
comDefs[0].pval = &asCheckClientIP;
comDefs[1].pval = &freeListBypass;

View File

@@ -256,10 +256,10 @@ osiSockTest_SRCS += osiSockTest.c
testHarness_SRCS += osiSockTest.c
TESTS += osiSockTest
TESTPROD_HOST += atInitTest
atInitTest_SRCS += atInitTest.cpp
TESTS += atInitTest
testHarness_SRCS += atInitTest.cpp
TESTPROD_HOST += afterIocRunningTest
afterIocRunningTest_SRCS += afterIocRunningTest.cpp
TESTS += afterIocRunningTest
testHarness_SRCS += afterIocRunningTest.cpp
TESTPROD_HOST += testexecname
testexecname_SRCS += testexecname.c

View File

@@ -0,0 +1,80 @@
/*************************************************************************\
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <epicsThread.h>
#include <epicsUnitTest.h>
#include <initHooks.h>
#include <iocsh.h>
#include <libComRegister.h>
#include <stdlib.h>
#include <string.h>
#include <testMain.h>
static void testOkEnv(const char *varName, const char *varValue)
{
const char *val = getenv(varName);
testOk(val && strcmp(val, varValue) == 0,
"%s=%s", varName, val ? val : "(null)");
}
static void iocshCmdDebug(const char *cmd)
{
printf("%s\n", cmd);
iocshCmd(cmd);
}
MAIN(afterIocRunningTest)
{
testPlan(12);
libComRegister();
// Reset environment variables
iocshCmdDebug("epicsEnvSet \"TEST_VAR\" \"BeforeIocInit\"");
iocshCmdDebug("epicsEnvSet \"TEST_VAR_ONE\" \"Before(Ioc)Init\"");
iocshCmdDebug("epicsEnvSet 'TEST_VAR_SPACES' 'Before Ioc Init'");
printf("Test whether the variables are initialized correctly.\n");
testOkEnv("TEST_VAR", "BeforeIocInit");
testOkEnv("TEST_VAR_ONE", "Before(Ioc)Init");
testOkEnv("TEST_VAR_SPACES", "Before Ioc Init");
printf("===================\n");
epicsThreadSleep(1.0);
// Basic test
iocshCmdDebug("afterIocRunning \"epicsEnvSet TEST_VAR AfterIocInit\"");
iocshCmdDebug("afterIocRunning \"epicsEnvSet TEST_VAR_ONE 'After(Ioc)Init'\"");
iocshCmdDebug("afterIocRunning \"epicsEnvSet TEST_VAR_SPACES 'After Ioc Init'\"");
iocshCmdDebug("afterIocRunning \"date\"");
// Verify error handling and robustness
iocshCmdDebug("afterIocRunning \"nonexistentCommand arg1 arg2\"");
iocshCmdDebug("afterIocRunning \"\""); // empty string
iocshCmdDebug("afterIocRunning \" \""); // only spaces
printf("===================\n");
epicsThreadSleep(1.0);
printf("Test whether the variables remain unchanged after 'afterIocRunning' and before 'iocInit'.\n");
testOkEnv("TEST_VAR", "BeforeIocInit");
testOkEnv("TEST_VAR_ONE", "Before(Ioc)Init");
testOkEnv("TEST_VAR_SPACES", "Before Ioc Init");
// Simulate iocInit
printf("===================\n"
"iocInit: Simulation\n"
"===================\n");
initHookAnnounce(initHookAfterIocRunning);
epicsThreadSleep(1.0);
// Verify the results
printf("Test whether the variables are correctly initialized after 'iocInit'.\n");
testOkEnv("TEST_VAR", "AfterIocInit");
testOkEnv("TEST_VAR_ONE", "After(Ioc)Init");
testOkEnv("TEST_VAR_SPACES", "After Ioc Init");
testPass("Command 'date' executed");
testPass("Invalid command did not crash IOC");
testPass("Empty afterIocRunning commands do not cause failure");
return testDone();
}

View File

@@ -1,72 +0,0 @@
/*************************************************************************\
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <epicsThread.h>
#include <epicsUnitTest.h>
#include <initHooks.h>
#include <iocsh.h>
#include <libComRegister.h>
#include <stdlib.h>
#include <string.h>
#include <testMain.h>
void testOkEnv(const char *varName, const char *varValue)
{
const char *val = getenv(varName);
testOk(val && strcmp(val, varValue) == 0,
"%s=%s", varName, val ? val : "(null)");
}
MAIN(atInitTest)
{
testPlan(12);
libComRegister();
// Reset environment variables
iocshCmd("epicsEnvSet \"ATINIT_TEST_VAR\" \"BeforeIocInit\"");
iocshCmd("epicsEnvSet \"ATINIT_TEST_VAR_ONE\" \"BeforeIocInit\"");
iocshCmd("epicsEnvSet 'ATINIT_TEST_VAR_SPACES' 'Before Ioc Init'");
printf("Test whether the variables are initialized correctly.\n");
testOkEnv("ATINIT_TEST_VAR", "BeforeIocInit");
testOkEnv("ATINIT_TEST_VAR_ONE", "BeforeIocInit");
testOkEnv("ATINIT_TEST_VAR_SPACES", "Before Ioc Init");
// Basic test
iocshCmd("atInit \"epicsEnvSet ATINIT_TEST_VAR AfterIocInit\"");
iocshCmd("atInit \"epicsEnvSet ATINIT_TEST_VAR_ONE AfterIocInit\"");
iocshCmd("atInit \"epicsEnvSet ATINIT_TEST_VAR_SPACES 'After Ioc Init'\"");
iocshCmd("atInit \"date\"");
epicsThreadSleep(1.0);
// Verify error handling and robustness
iocshCmd("atInit \"nonexistentCommand arg1 arg2\"");
iocshCmd("atInit \"\""); // empty string
iocshCmd("atInit \" \""); // only spaces
printf("Test whether the variables remain unchanged after 'atInit' and before 'iocInit'.\n");
testOkEnv("ATINIT_TEST_VAR", "BeforeIocInit");
testOkEnv("ATINIT_TEST_VAR_ONE", "BeforeIocInit");
testOkEnv("ATINIT_TEST_VAR_SPACES", "Before Ioc Init");
// Simulate iocInit
initHookAnnounce(initHookAfterIocRunning);
printf("=== iocInit Simulation ===\n");
epicsThreadSleep(1.0);
// Verify the results
printf("Test whether the variables are correctly initialized after 'iocInit'.\n");
testOkEnv("ATINIT_TEST_VAR", "AfterIocInit");
testOkEnv("ATINIT_TEST_VAR_ONE", "AfterIocInit");
testOkEnv("ATINIT_TEST_VAR_SPACES", "After Ioc Init");
testPass("Command 'date' executed");
testPass("Invalid command did not crash IOC");
testPass("Empty atInit commands do not cause failure");
return testDone();
}