epics-modules / xxx

APS BCDA synApps module: xxx
http://epics-modules.github.io/xxx
Other
5 stars 6 forks source link

Modular Shell Commands #45

Closed keenanlang closed 3 years ago

keenanlang commented 3 years ago

Changes the xxx.sh startup script to dynamically generate its own list of commands.

IOC_COMMAND_DIR is available to be set to point to a folder where the commands reside. If it isn't set, the script uses the 'commands' folder in the same folder as xxx.sh. When the script is run, it sources each of the files that are contained in the command directory. The files define shell functions and then bind them to a string with the line:

register "COMMAND_NAME" shell_function

When you call xxx.sh now, it takes the first argument, tries to match it against a registered command name, then calls the shell_function for that name and passes that function all the additional command-line parameters (currently, only 'start' and 'usage' do anything with extra parameters though). Code is run in the same process as the xxx.sh script, so the commands have access to all the same environment variables.

As well, the usage command has been updated. The basic version runs through and builds a string list of all available commands and displays it to the user. If the script command is called with an additional parameter giving the name of a different command, it will attempt to find if there is a usage shell function for that command. The usage shell function is the same name as the actual function, just with "_usage" appended. For example, the start command has a usage function that displays the various inputs it can take in, the shell function for start is defined as start(), so the usage function is start_usage().

The usage script checks if that function exists, then calls it. If the command is a recognized command, but there is no usage function, it just displays that you call the command without any additional parameters. This allows commands that take in additional parameters to display information about how to use them, while keeping things simple for commands that are simple.

Finally, there is the ioc_cmd shell function added in for the commands to use. This allows the commands to call other commands without issues about which one gets loaded first. This is also the function that is used to find and run the commands based on the command line input. So far, only the restart command uses it.

Taken altogether, this is what a command file looks like:

register "medm" start_medm

start_medm() {
    ${IOC_STARTUP_DIR}/../../start_MEDM_xxx
}

Throwing this out there because I've had IOC's before I have put extra commands in. It's not a difficult process to add an additional case statement and call a function, but with the increasing complexity and size of the xxx.sh script, breaking these out into more manageable pieces and automating the addition may make it more approachable for those with less knowledge of the bones of the script.

Note: This does change the script to explicitly using bash, I don't think that's a dealbreaker, but it is something to note.

kmpeters commented 3 years ago

I haven't tested this yet, but I will soon.

My first thoughts:

  1. This would probably make it easier to update the script & commands for an IOC, since beamline-specific commands wouldn't be overwritten if all the new commands were copied over the old ones. Timestamps on files are likely to indicate which commands were modified.

  2. changePrefix would need to be updated at the same time to change the prefix in all of the command files.

keenanlang commented 3 years ago

changePrefix would need to be updated at the same time to change the prefix in all of the command files.

Since the shell script already has that information, I just replaced the use of it in start_{caqtdm/medm} with $(IOC_NAME) and replaced its use in comments with the generic term IOC.

kmpeters commented 3 years ago

@keenanlang I just started testing this. When I run the xxx.sh command without any arguments I get this message:

 not a recognized command

Any ideas why that might be happening?

kmpeters commented 3 years ago

Should the usage command be the default if none are specified?

kmpeters commented 3 years ago

I created an issue here:

https://github.com/epics-modules/xxx/issues/48