autoSaveRestore -- save_restore.c v4.0; dbrestore.c v4.0 This software automatically saves the values of EPICS process variables (PVs), to files on a server, and can automatically restore those values when the VME crate is rebooted. The original author is Bob Dalesio; I made some improvements; Frank Lenkszus made some more improvements, which I folded into the version I've been maintaining. A bunch of people contributed to getting the software running on PPC hardware, including Ron Sluiter, Andrew Johnson, and Pete Jemian (APS), Markus Janousch and David Maden (SLS), and I'm not sure who else. autoSaveRestore is a collection of files, and a rather ungainly name. In this documentation, I'll use 'save_restore' for short. 'save_restore' is one part of autoSaveRestore; it is the task that actually does the parameter saving. 'reboot_restore' is the name of the function that restores parameter values during reboot. v4.0 highlights --------------- Status PV's have been reworked. Now you don't have to modify either the database or the medm-display file to use your own save-set names. v3.5 highlights --------------- Beginning with version 3.5, arrays can be saved and restored much as scalar PV's. Arrays are read and written using run-time database access, which implies that they can only be written during pass 1 of the restore, and that you cannot save the value of a non-local array PV. (Scalar PV's are read using channel access and normally are written using static database access.) Beginning with v3.5, scalar PV's which have type DBF_NOACCESS in the .dbd file, and are set to some other DBF type during record initialization, can be restored in pass 1. (Some fields of the genSub record behave in this way, and previous versions of save_restore could not restore them.) Prior to v3.5, PV's were listed 'backwards' in the save file -- i.e., the first PV in the request file became the last PV in the save file. Now, both files have the same ordering. v3.3 highlights --------------- In versions earlier than 3.3, save_restore's job was simply to save parameters through a reboot. The file server to which save files were written was assumed competent to protect those files, and when the server said a save file was safe, save_restore believed it. Beginning with 3.3, the file server is viewed as the enemy: save_restore expects it to lie about file status, to suddenly stop recognizing previously valid file handles, and to return error codes that don't make sense. Save_restore can now defend its NFS mount against a server reboot, defend parameter values against a server power failure, and attempts to defend against a user messing around with the directory into which save files are being written. Save_restore now allows you to change, at run time, the directory and/or the server to which save files are written. Here's an example: # The normal save_restore directory setup, in st.cmd: save_restoreSet_NFSHost("oxygen", "164.54.52.4") set_savefile_path(startup, "autosave") ... iocBoot() ... (end of st.cmd) ... # Type the following into the ioc's console save_restoreSet_NFSHost("wheaties", "164.54.53.101") # You'll get an immediate error if the old save file path doesn't # exist, or has adverse permissions, on the new server. set_savefile_path("/local/autosave", "") # save_restore will immediately attempt to mount the new file path The above code assumes the ioc has permission to mount file systems on the host ('wheaties', in this case) and that the ioc has read/write/execute permission to the directory in which it is suppoed to write .sav files (in this case, '/local/autosave'). Neither of these conditions are likely to be met by default; you must take action to arrange them. Save_restore cannot defend against adverse file permissions, or against the save directory (NFS mount point) disappearing altogether. Contents ======== save_restore.c -- saves PV values in files on a file server (or whatever lame piece of crap happens to be lying around) according to preset rules. dbrestore.c -- restore PV values at boot time, using dbStaticLib initHooks.c -- call restore routines at the correct time during boot. fGetDateStr.c -- Frank Lenkszus' date-string routines save_restore.h -- headers fGetDateStr.h auto_settings.req -- Sample request files auto_positions.req reqInclude.tar -- collection of save_restore-request files with parameterized PV names (e.g., "$(P)$(T).PREC") intended to be included in auto_settings.req. This collection was copied from *App/Db directories in the synApps_4.4 CVS repository on 8/27/03. save_restoreStatus*.adl -- MEDM displays of save_restore status. SR_X_Status.adl save_restoreStatus.db -- database containing records save_restore uses to report status. SR_array_test.vdb -- Test database for array save_restore. Differences from previous versions ================================== v4.0 ---- Status PV's have been reworked. Now you don't have to modify either the database or the medm-display file to use your own save-set names. v3.5 ---- Sometime in the past, I made fdbrestore() and fdbrestoreX() unavailable to console users by making them macros. This is fixed. save_restore v3.3; dbrestore v3.3: ---------------------------------- Several variables and user-callable functions have been renamed, as their meanings have changed. In preparation for 3.14, most user-settable variables can now be set by a function call. Here's a list of variables and functions added, deleted, or changed: OLD NEW ---------------------------------------------------------------------- sr_save_incomplete_sets_ok -> save_restoreIncompleteSetsOk sr_restore_incomplete_sets_ok -> save_restoreIncompleteSetsOk -> save_restoreSet_IncompleteSetsOk(value) reboot_restoreDebug -> save_restoreDebug -> save_restoreSet_Debug(value) reboot_restoreDatedBu -> save_restoreDatedBackupFiles -> save_restoreSet_DatedBackupFiles(value) -> save_restoreNumSeqFiles -> save_restoreSet_NumSeqFiles(value) -> save_restoreSeqPeriodInSeconds -> save_restoreSet_SeqPeriodInSeconds(value) NOTE: if save_restoreNumSeqFiles > 0, redundant .sav files will be written every save_restoreSeqPeriodInSeconds seconds -> dbrestoreShow() fdblist() -> save_restoreShow(verbose) NOTE: save_restoreShow() calls dbrestoreShow() save_restore_test_fopen -> NOTE: test is no longer done create_periodic_set(file,period) -> create_periodic_set(file,period,macro) create_triggered_set(file,trigPV) -> create_triggered_set(file,trigPV,macro) create_monitor_set(file,period) -> create_monitor_set(file,period,macro) create_manual_set(file) -> create_manual_set(file,macro) reload_periodic_set(file,period) -> reload_periodic_set(file,period,macro) reload_triggered_set(file,trigPV) -> reload_triggered_set(file,trigPV,macro) reload_monitor_set(file,period) -> reload_monitor_set(file,period,macro) reload_manual_set(file) -> reload_manual_set(file,macro) NOTE: macro string is optional and supplements any macro strings defined in request files. You can only reload one set at a time; you must wait for a reload to complete before starting another one. ************************************************************************* NOTE: IN VERSIONS EARLIER THAN 2.7, Time PERIODS WERE SPECIFIED USING FLOATING POINT NUMBERS. THEY ARE NOW SPECIFIED AS INTEGERS (IN SECONDS). ************************************************************************* set_savefile_path(path) -> set_savefile_path(path,pathsub) set_requestfile_path(path) -> set_requestfile_path(path,pathsub) NOTE: if pathsub is specified, it is appended to path. The purpose of this change was to make it easier to use variabled defined in cdCommands as path elements. Example: set_requestfile_path(std,"stdApp/Db") -> save_restoreSet_status_prefix(value) NOTE: save_restore expects PV's with which it can display its status. Status PV's are request-file specific; the supplied database contains PV's for auto_positions.req and auto_settings.req only. If you use other request files, you can add your own PV's. It's OK if the PV's don't exist, but you will get informational messages when their absence is noticed. -> save_restoreSet_NFSHost(host,IPaddress) -> save_restoreRemountThreshold NOTE: If save_restore is managing its own NFS mount (i.e., if calls to save_restoreSet_NFSHost() and set_savefile_path() succeeded) it will dismount and remount after save_restoreRemountThreshold consecutive I/O errors (e.g. to fix a stale file handle). ---------------------------------------------------------------------- Save_restore now maintains status PV's for each PV list, and rolled-up status PV's for overall save status and reboot status. fdblist() renamed as save_restoreShow(), now enhanced with more status information, including rolled-up reboot status. Save_restore can now maintain up to ten "sequence files" (named *.savX, where X is a digit in [0..(save_restoreNumSeqFiles-1)]), which are copies of the most recent .sav or .savB file, copied at user-specified intervals. If no valid .sav or .savB file exists, save_restore will write sequence files directly from its PV list, as though it were writing a .sav file. At boot time, if .sav and .savB are both corrupt or missing, save_restore will restore from the most recent sequence file. Save_restore no longer tests fopen() by writing a temporary file, before saving. Save_restore will manage its own NFS mount, if the host name and address has been specified with a call to save_restoreSet_NFSHost(), and the save-file path (used as the mount point) has been set by a call to set_savefile_path(). When more than 'save_restoreRemountThreshold' consecutive, unexpected I/O errors (the default number is ten) have occurred, save_restore will attempt to remount the file system. Previously, save_restore saved floats and doubles using an algorithm that was prone to failure, especially for PV's from another ioc. (Thanks to Mark Rivers for finding and fixing this bug.) Now use errlogPrintf() for all messages except those from callback routines, where logMsg() is used. All CA monitors are now set up by the save_restore task. Previously, some were setup by whatever task called create_XXX_set(). Previously, if a save-file write failed, it would not be retried until the normal conditions (e.g., a PV's value changed) were again met. Now, if save_restore is managing its own NFS mount, and enough I/O errors have accumulated to cause an NFS remount, all save sets in failure are written immediately after the remount succeeds. Some ca_pend timeouts adjusted or limited. Previous timeouts were inappropriate if many thousands of PV's were specified in a request file. Save files now contain the time at which they were written, in the format of fGetDateStr() -- yymmdd-hhmmss. Dated backup files now have an underscore between the name and the date. Previously, if the second fopen() call in myFileCopy() failed, the function would return without closing the first file. Previously, save_restore would overwrite corrupted files in the normal course of operation. Now, whenever a corrupted file is encountered, a copy is made. If the copy is made at restore time, the copy is named, e.g., auto_settings.sav_RBAD_030818-134850; if at save time, it's named, e.g., auto_settings.sav_SBAD_030818-134850. save_restore no longer uses the vxWorks 'copy' command for anything, because it writes files with inconvenient permissions. save_restore v2.9a; dbrestore v2.7: ---------------------------------- Previously, create_data_set() would return without releasing a semaphore (causing the save_restore task to hang) if called with save_method==TRIGGERED, but no trigger channel was specified. Previously, fdbrestore() would return without releasing a semaphore (causing the save_restore task to hang) if called with the name of a save set currently being maintained, and the save list contained unconnected PV's, and the variable 'sr_restore_incomplete_sets_ok' was false. Added macro-string argument to create_xxx_set() Added argument 'verbose' to fdblist() Added second argument, 'subpath', to set_XXXfile_path(). This allows the caller to pass the path as two args to be concatenated, making it easier to build the path string using a variable set in the cdCommands file. Use logMsg() instead of epicsPrintf() for several informational messages. The variables chlist and reqFilePathList are now private. Increased ca_pend_io search timeout in connect_list() from 2 to 5 seconds. Decreased fetch timeout from num_channels/10 to 10 seconds. Decreased ca_pend_io clear-channel timeout from num_channels/10 to 10 seconds in remove_data_set(). Changed call to macParseDefns() to specify the macro-substitution handle. save_restore v2.7; dbrestore v2.7: --------------------------------- *********************************************************** ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION *********************************************************** This version is incompatible in one important respect with versions of save_restore.c numbered lower than 2.6: The functions create_periodic_set() create_monitor_set() reload_periodic_set() reload_monitor_set() now take 'int' arguments to specify time periods, instead of 'double' arguments. This change was required for compatibility with PPC processors. In Frank Lenkszus' version, the calls set_pass_restoreFile() were required to specify files to be restored. In this version, if no restore files have been specified, initHooks specifies the default files auto_positions.sav and auto_settings.sav for you, for backward compatibility with my previous version. If you really don't want to restore any files, you now must either not load initHooks, or replace it with your own version. In Frank Lenkszus' version, set_requestfile_path() could specify only a single directory. Now that include files are a possibility, you can specify several request-file directories by calling set_requestfile_path() several times. Backup files made as part of a restore operation (i.e., files ending in ".bu", or "YYMMDD-HHMMSS") are no longer created using the VxWorks "copy" command. How to use this software ======================== This software can be used in many different ways. I'll describe what you have to do to use it as it's commonly used at APS beamlines to save PV values periodically, and restore them on reboot. *) (required) Build save_restore.o, dbrestore.o, initHooks.o, and fGetDateStr.o. Don't use an old copy of initHooks.o. Although initHooks.c hasn't changed since save_restore v2.6, it uses a data structure from save_restore.h, which has changed, and if initHooks doesn't get recompiled, it will corrupt that data structure. *) (required) Create "request" files (e.g., auto_settings.req, auto_positions.req) specifying the PVs whose values you want to save and restore. The save files corresponding to these request files will have the ".req" suffix replaced by ".sav". Request files can include other request files (nested includes are allowed) and macro substitution can be performed on the included files (using William Lupton's macro library), with the following syntax: file e.g., file motor_settings.req P=xxx:,M=m1 I've tried to defend against forseeable variations in syntax, so that include lines with embedded whitespace and/or quotes, macro strings without commas, empty macro strings, and lines with trailing comments will be parsed as one would want. Generally, quotes are ignored, whitespace implies a comma but otherwise is ignored, and everything after the second sequence of non-whitespace characters (i.e., after the file name) and before the (optional) comment character '#' is taken as the macro-substitution string. Macro substitution is performed on the entire line, so it's possible to parameterize names of included files, as well as PV names. It is also possible to define a macro that replaces its target with nothing. *) (optional, recommended) Specify one or more directories to be searched for request files, using one or more invocations of the function set_requestfile_path() *) (optional, recommended) Specify the NFS host from which save files will be read at restore time, and to which they will be written at save time, by calling the function save_restoreSet_NFSHost() *) (strongly recommended) Use NFS, preferably as described above, or by including an nfsMount() command in your startup script. Save_restore is only tested with NFS, though it has in the past and may still work with vxWorks' netDrv (ftp or rsh). *) (optional, recommended) Specify the directory in which you want save files to be written, by calling the function set_savefile_path() in your startup script, before the create_xxx_set() commands, to specify the path to the directory. If you are using NFS (strongly recommended), ensure that the path does not contain symbolic links. In my experience, VxWorks cannot write through a symbolic link. (I don't understand all the ins and outs of this limitation. It may be that symbolic links are OK if they contain absolute path names.) *) (required) Give the IOC write permission to the directory in which the save files are to be written. If you forget this step, save_restore may be able to write save files, but the files will be corrupted because save_restore will not be permitted to change their lengths. Save_restore attempts to detect this condition, but cannot work around it if the file length must increase. *) (optional, recommended) Specify which save files are to be restored before record initialization (pass 0) and which are to be restored after record initialization (pass 1), using the commands set_pass0_restoreFile() set_pass1_restoreFile() Place these commands in the startup file before iocInit. If you don't call either of these functions (or if your calls fail completely to specify any restore files) the supplied initHooks routine will attempt to restore the file "auto_positions.sav" before record init, and the file "auto_settings.sav" both before and after record init. Notes: Link fields cannot be restored (by dbStatic calls) after record initialization. If you want save/restore to work for link fields you must specify them in a pass-0 file. It is not an error to specify link fields in a pass-1 file, but this software will not try to restore them. Device support code for the motor record uses the value of the field DVAL, restored during pass 0, only if the value read from the hardware is zero. If the value from hardware is nonzero, it is used instead of the restored value. *) (required) Load a copy of initHooks that calls reboot_restore() to restore saved parameter values. The copy of initHooks included in this distribution is recommended. *) (optional, recommended) Tell save_restore to writed dated backup files. At boot time, the restore software writes a backup copy of the ".sav" file from which it restored PV's. This file can either be named xxx.sav.bu, and be rewritten every reboot, or named xxx.sav_YYMMDD-HHMMSS, where "YY..." is a date. Dated backups are not overwritten. If you want dated backup files (recommended), put either of the following two lines to your st.cmd file before the call to iocInit(): save_restoreDatedBackupFiles=1 OR save_restoreSet_DatedBackupFiles(1) Note: If a save file is restored in both pass 0 and pass 1, the boot-backup file will be written only during pass 0. *) (required) Invoke the "save" part of this software as part of the EPICS startup sequence, by calling create_XXX_set() -- e.g., adding lines of the form create_monitor_set("auto_positions.req", 5, "P=xxx:") create_monitor_set("auto_settings.req", 30, "P=xxx:") to your EPICS startup file after iocInit. You can call the files anything you want, but note that the file names "auto_positions" and "auto_settings" receive special treatment: they are default restore-file names, and the included database and medm-display files assume these file names. The third argument to create_monitor_set() is a macro-substitution string, as described above in the discussion of request files. If supplied, this macro-substitution string supplements any macro strings supplied in include-file directives of request files read for this save set. For each "create_monitor_set(.req,