persistent module fixed
This commit is contained in:
@ -19,30 +19,67 @@
|
||||
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""Define base classes for real Modules implemented in the server"""
|
||||
"""Mixin for keeping parameters persistent
|
||||
|
||||
For hardware not keeping parameters persistent, we might want to store them in Frappy.
|
||||
|
||||
The following example will make 'param1' and 'param2' persistent, i.e. whenever
|
||||
one of the parameters is changed, either by a change command or by access from
|
||||
an other interface to the hardware, it is saved to a file, and reloaded after
|
||||
a power down / power up cycle. In order to make this work properly, there is a
|
||||
mechanism needed to detect power down (i.e. a reading a hardware parameter
|
||||
taking a special value on power up).
|
||||
|
||||
An additional use might be the example of a motor with cyclic reading of an
|
||||
encoder value, which looses the counts of how many turns already happened on
|
||||
power down.
|
||||
This can be solved by comparing the loaded encoder value self.encoder with a
|
||||
fresh value from the hardware and then adjusting the zero point accordingly.
|
||||
|
||||
|
||||
class MyClass(PersistentMixin, ...):
|
||||
param1 = PersistentParam(...)
|
||||
param2 = PersistentParam(...)
|
||||
encoder = PersistentParam(...)
|
||||
|
||||
...
|
||||
|
||||
def read_encoder(self):
|
||||
encoder = <get encoder from hardware>
|
||||
if <power down/power up cycle detected>:
|
||||
self.loadParameters()
|
||||
<fix encoder turns by comparing loaded self.encoder with encoder from hw>
|
||||
else:
|
||||
self.saveParameters()
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
from secop.errors import BadValueError, ConfigError, InternalError, \
|
||||
ProgrammingError, SECoPError, SilentError, secop_error
|
||||
from secop.lib import getGeneralConfig
|
||||
from secop.params import Parameter, Property, BoolType, Command
|
||||
from secop.modules import HasAccessibles
|
||||
|
||||
class PersistentMixin:
|
||||
|
||||
def __init__(*args, **kwds):
|
||||
class PersistentParam(Parameter):
|
||||
persistent = Property('persistence flag', BoolType(), default=True)
|
||||
|
||||
|
||||
class PersistentMixin(HasAccessibles):
|
||||
def __init__(self, *args, **kwds):
|
||||
super().__init__(*args, **kwds)
|
||||
# self.persistentFile = None
|
||||
# self.persistentData = {}
|
||||
self.writeDict.update(self.loadParameters())
|
||||
# write=False: write will happen later
|
||||
self.initData = {}
|
||||
for pname in self.parameters:
|
||||
pobj = self.parameters[pname]
|
||||
if not pobj.readonly and getattr(pobj, 'persistent', False):
|
||||
self.initData[pname] = pobj.value
|
||||
self.writeDict.update(self.loadParameters(write=False))
|
||||
print('initData', self.initData)
|
||||
|
||||
def writeInitParams(self, started_callback=None):
|
||||
super().writeInitParams()
|
||||
self.saveParameters()
|
||||
if started_callback:
|
||||
started_callback()
|
||||
|
||||
def loadParameters(self):
|
||||
def loadParameters(self, write=True):
|
||||
"""load persistent parameters
|
||||
|
||||
:return: persistent parameters which have to be written
|
||||
@ -58,23 +95,34 @@ class PersistentMixin:
|
||||
except FileNotFoundError:
|
||||
self.persistentData = {}
|
||||
writeDict = {}
|
||||
for pname, pobj in self.parameters.items():
|
||||
if pobj.persistent and pname in self.persistentData:
|
||||
value = pobj.datatype.import_value(self.persistentData[pname])
|
||||
for pname in self.parameters:
|
||||
pobj = self.parameters[pname]
|
||||
if getattr(pobj, 'persistent', False) and pname in self.persistentData:
|
||||
try:
|
||||
value = pobj.datatype.import_value(self.persistentData[pname])
|
||||
pobj.value = value
|
||||
if not pobj.readonly:
|
||||
writeDict[pname] = value
|
||||
except Exception as e:
|
||||
self.log.warning('can not restore %r to %r (%r)' % (pname, value, e))
|
||||
if write:
|
||||
self.writeDict.update(writeDict)
|
||||
self.writeInitParams()
|
||||
return writeDict
|
||||
|
||||
def saveParameters(self):
|
||||
"""save persistent parameters
|
||||
|
||||
to be called regularely explicitly by the module
|
||||
- to be called regularely explicitly by the module
|
||||
- the caller has to make sure that this is not called after
|
||||
a power down of the connected hardware before loadParameters
|
||||
"""
|
||||
data = {k: v.export_value() for k, v in self.parameters.items() if v.persistent}
|
||||
if self.writeDict:
|
||||
# do not save before all values are written to the hw, as potentially
|
||||
# factory default values were read in the mean time
|
||||
return
|
||||
data = {k: v.export_value() for k, v in self.parameters.items()
|
||||
if getattr(v, 'persistent', False)}
|
||||
if data != self.persistentData:
|
||||
self.persistentData = data
|
||||
persistentdir = os.path.basename(self.persistentFile)
|
||||
@ -92,3 +140,8 @@ class PersistentMixin:
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
@Command()
|
||||
def factory_reset(self):
|
||||
self.writeDict.update(self.initData)
|
||||
self.writeInitParams()
|
||||
|
Reference in New Issue
Block a user