Skip to content

Commit

Permalink
implement changes from review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
jsouter committed Aug 30, 2024
1 parent 46f95c5 commit 515979f
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 35 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Unreleased_
-----------

Added:
- `Add autosave support to all records and record fields <../../pull/163>`
- `Add autosave support to all records and record fields <../../pull/163>`_
- `Add int64In/Out record support <../../pull/161>`_
- `Enable setting alarm status of Out records <../../pull/157>`_
- `Adding the non_interactive_ioc function <../../pull/156>`_
Expand Down
10 changes: 5 additions & 5 deletions docs/examples/example_autosave_ioc.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
builder.aOut("AO", autosave=True)
builder.aIn("AI", autosave_fields=["PREC", "EGU"])
builder.boolIn("BO")
builder.WaveformIn("WAVEFORMOUT", [0, 0, 0, 0], autosave=True)
builder.WaveformIn("WAVEFORMIN", [0, 0, 0, 0], autosave=True)
with autosave.Autosave(True, ["LOPR", "HOPR"]):
builder.aOut("AUTOMATIC-AO", autosave_fields=["EGU"])
minutes = builder.longOut("MINUTESRUN", autosave=True)
seconds = builder.longOut("SECONDSRUN", autosave=True)

autosave.configure(
directory="/tmp/autosave-data",
name="MY-DEVICE-PREFIX",
save_period=20.0
save_period=5.0
)

builder.LoadDatabase()
Expand All @@ -26,8 +26,8 @@
# Start processes required to be run after iocInit
def update():
while True:
cothread.Sleep(60)
minutes.set(minutes.get() + 1)
cothread.Sleep(1)
seconds.set(seconds.get() + 1)

cothread.Spawn(update)

Expand Down
22 changes: 11 additions & 11 deletions docs/how-to/use-autosave-in-an-ioc.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Use `softioc.autosave` in an IOC
Use `autosave` in an IOC
================================

`../tutorials/creating-an-ioc` shows how to create a pythonSoftIOC.
Expand All @@ -13,13 +13,13 @@ Records are instantiated as normal and configured for automatic loading and
periodic saving to a backup file with the keyword arguments ``autosave`` and ``autosave_fields``.
Records with ``autosave=True`` (``False`` by default) have their
VAL fields backed up. Additional record fields in a list passed to ``autosave_fields`` will be backed
up, note that this applies even when ``autosave`` is ``False``.
up - note that this applies even when ``autosave`` is ``False``.

The field values get written into a yaml-formatted file containing key-value pairs,
by default the keys are the same as the full PV name, including any device name specified
The field values get written into a yaml-formatted file containing key-value pairs.
By default the keys are the same as the full PV name, including any device name specified
in :func:`~softioc.builder.SetDeviceName()`.

Autosave is disabled by default until :func:`~softioc.autosave.configure()` is called. The first two arguments,
Autosave is disabled until :func:`~softioc.autosave.configure()` is called. The first two arguments,
``directory`` and ``name`` are required. Backup files are periodically written into
``directory`` with the name ``<name>.softsav`` every ``save_period`` seconds,
set to 30.0 by default. The directory must exist, and should be configured with the appropriate
Expand All @@ -33,7 +33,7 @@ inside the context manager. If the PV already has ``autosave_fields`` set, the l
of fields get combined. All other module members are intended for internal use only.

In normal operation, loading from a backup is performed once during the
:func:`~softioc.builder.LoadDatabase()` call, periodic saving to the backup file begins when
:func:`~softioc.builder.LoadDatabase()` call and periodic saving to the backup file begins when
:func:`~softioc.softioc.iocInit()` is called, provided that any PVs are configured to be saved.
Currently, manual loading from a backup at runtime after ioc initialisation is not supported.
Saving only occurs when any of the saved field values have changed since the last save.
Expand All @@ -49,7 +49,7 @@ named ``<name>.softsav.bu``. To disable any autosaving, comment out the
:func:`~softioc.autosave.configure()` call or pass it the keyword argument
``enabled=False``.

The resulting backup file after running the IOC for a minute is the following:
The resulting backup file after running the example IOC for about 30 seconds is the following:

.. code-block::
Expand All @@ -60,12 +60,12 @@ The resulting backup file after running the IOC for a minute is the following:
MY-DEVICE-PREFIX:AUTOMATIC-AO.EGU: ''
MY-DEVICE-PREFIX:AUTOMATIC-AO.HOPR: '0'
MY-DEVICE-PREFIX:AUTOMATIC-AO.LOPR: '0'
MY-DEVICE-PREFIX:MINUTESRUN: 1
MY-DEVICE-PREFIX:WAVEFORMOUT: [0, 0, 0, 0] AI.EGU: ''
MY-DEVICE-PREFIX:SECONDSRUN: 29
MY-DEVICE-PREFIX:WAVEFORMIN: [0, 0, 0, 0]
If the IOC is stopped and restarted, the MINUTESRUN record will load its saved
value of 1 from the backup.
If the IOC is stopped and restarted, the SECONDSRUN record will load its saved
value of 29 from the backup.
All non-VAL fields are stored as strings. Waveform type records holding arrays
are cast into lists before saving.

Expand Down
11 changes: 5 additions & 6 deletions docs/reference/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ and stderr streams, is sent directly to the terminal.

.. function:: configure(directory, name, save_period=30, timestamped_backups=True, enabled=True)

Used to set the location of backup files.
Used to configure settings for Autosave.
Backups are disabled by default unless this method is called. It must be
called prior to :func:`~softioc.builder.LoadDatabase`.

Expand All @@ -169,7 +169,7 @@ and stderr streams, is sent directly to the terminal.
~~~~~~
The file prefix used for naming the backup files. This is typically set to
be the same as the device prefix. The resulting file name will be
`name`.softsav. This argument is required.
``name``.softsav. This argument is required.

.. _save_period:

Expand All @@ -193,8 +193,7 @@ and stderr streams, is sent directly to the terminal.
`enabled`
~~~~~~~~~
A boolean that is `True` by default, if `False` then no loading will occur
at IOC startup, and the save thread will return without performing any
backups.
at IOC startup, and no values with be saved to any backup files.

.. seealso::
`softioc.builder` for how to designate a field for autosave.
Expand Down Expand Up @@ -334,7 +333,7 @@ and stderr streams, is sent directly to the terminal.
`autosave`
~~~~~~~~~~

Available on all record types, when set to `True` it marks the record
Available on all record types. When set to `True` it marks the record
value for automatic periodic backing up to a file. Set to `False` by
default. When the IOC is restarted and a backup file exists, the value is
loaded from this file when :func:`~softioc.builder.LoadDatabase` is called.
Expand All @@ -351,7 +350,7 @@ and stderr streams, is sent directly to the terminal.
~~~~~~~~~~~~~~~~~

A list of strings of record fields belonging to the PV (e.g. ["EGU", "PREC"])
to be saved to and loaded from a backup file, empty by default.
to be saved to and loaded from a backup file. Empty by default.


For all of these functions any EPICS database field can be assigned a value by
Expand Down
19 changes: 8 additions & 11 deletions softioc/autosave.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def configure(
Args:
directory: string or Path giving directory path where autosave backup
files are saved and loaded.
name: string name of the root used for naming backup files, this
name: string name of the root used for naming backup files. This
is usually the same as the device name.
save_period: time in seconds between backups. Backups are only performed
if PV values have changed.
Expand All @@ -58,7 +58,6 @@ def start_autosave_thread():
worker = threading.Thread(
target=Autosave._loop,
)
worker.daemon = True
worker.start()
atexit.register(_shutdown_autosave_thread, worker)

Expand All @@ -74,16 +73,17 @@ def add_pv_to_autosave(pv, name, save_val, save_fields):
Args:
pv: a PV object inheriting ProcessDeviceSupportCore
name: the key by which the PV value is saved to and loaded from a
backup, this is typically the same as the PV name.
backup. This is typically the same as the PV name.
save_val: a boolean that tracks whether to save the VAL field
in an autosave backup
save_fields: a list of string names of fields associated with the pv
to be saved to and loaded from a backup
"""
if save_val:
Autosave._pvs[name] = _AutosavePV(pv)
for field in save_fields:
Autosave._pvs[f"{name}.{field}"] = _AutosavePV(pv, field)
if save_fields:
for field in save_fields:
Autosave._pvs[f"{name}.{field}"] = _AutosavePV(pv, field)


def load_autosave():
Expand Down Expand Up @@ -126,12 +126,9 @@ def __enter__(self):
def __exit__(self, A, B, C):
if self._save_val or self._save_fields:
for key, pv in LookupRecordList():
try:
if key not in self._records_before_cm:
add_pv_to_autosave(
pv, key, self._save_val, self._save_fields)
except Exception:
traceback.print_exc()
if key not in self._records_before_cm:
add_pv_to_autosave(
pv, key, self._save_val, self._save_fields)
self._save_val = False
self._save_fields = []
self._records_before_cm = None
Expand Down
2 changes: 1 addition & 1 deletion softioc/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class ProcessDeviceSupportCore(DeviceSupportCore, RecordLookup):
# all record types can support autosave
def __init__(self, name, **kargs):
autosave_enabled = kargs.pop("autosave", False)
autosave_fields = kargs.pop("autosave_fields", [])
autosave_fields = kargs.pop("autosave_fields", None)
if autosave_enabled or autosave_fields:
autosave.add_pv_to_autosave(
self, name, autosave_enabled, autosave_fields)
Expand Down

0 comments on commit 515979f

Please sign in to comment.