mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-06-11 07:47:12 +02:00
feat: adding support for saving the status of a DataService class
By passing a the "filename" keyword to the DataService init function, one can save the state of the instance in this file. Quitting the service (Ctrl+C) will dump the state of the instance in the file with given filename. When starting the instance, it will load these values again.
This commit is contained in:
@ -180,18 +180,76 @@ class DataService(rpyc.Service, TaskManager):
|
|||||||
the "subject" and the callback functions as the "observers".
|
the "subject" and the callback functions as the "observers".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, filename: Optional[str] = None) -> None:
|
||||||
TaskManager.__init__(self)
|
TaskManager.__init__(self)
|
||||||
DataServiceSerializer.__init__(self, "serialized.json")
|
|
||||||
self.__root__: "DataService" = self
|
self.__root__: "DataService" = self
|
||||||
"""Keep track of the root object. This helps to filter the emission of
|
"""Keep track of the root object. This helps to filter the emission of
|
||||||
notifications. This overwrite the TaksManager's __root__ attribute."""
|
notifications. This overwrite the TaksManager's __root__ attribute."""
|
||||||
|
|
||||||
self._callbacks: set[Callable[[str, Any], None]] = set()
|
self._callbacks: set[Callable[[str, Any], None]] = set()
|
||||||
|
self._filename: Optional[str] = filename
|
||||||
|
|
||||||
self._register_callbacks()
|
self._register_callbacks()
|
||||||
self.__check_instance_classes()
|
self.__check_instance_classes()
|
||||||
self._initialised = True
|
self._initialised = True
|
||||||
|
self._load_values_from_json()
|
||||||
|
|
||||||
|
def _load_values_from_json(self) -> None:
|
||||||
|
if self._filename is not None:
|
||||||
|
# Check if the file specified by the filename exists
|
||||||
|
if os.path.exists(self._filename):
|
||||||
|
with open(self._filename, "r") as f:
|
||||||
|
# Load JSON data from file and update class attributes with these
|
||||||
|
# values
|
||||||
|
self.set_attributes_from_serialized_representation(
|
||||||
|
cast(dict[str, Any], json.load(f))
|
||||||
|
)
|
||||||
|
|
||||||
|
def write_to_file(self) -> None:
|
||||||
|
"""
|
||||||
|
Serialize the DataService instance and write it to a JSON file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename (str): The name of the file to write to.
|
||||||
|
"""
|
||||||
|
if self._filename is not None:
|
||||||
|
with open(self._filename, "w") as f:
|
||||||
|
json.dump(self.serialize(), f, indent=4)
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"Class {self.__class__.__name__} was not initialised with a filename. "
|
||||||
|
'Skipping "write_to_file"...'
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_attributes_from_serialized_representation(
|
||||||
|
self, serialized_representation: dict[str, Any]
|
||||||
|
) -> None:
|
||||||
|
# Traverse the serialized representation and set the attributes of the class
|
||||||
|
for path, value in generate_paths_and_values_from_serialized_DataService(
|
||||||
|
serialized_representation
|
||||||
|
).items():
|
||||||
|
# Split the path into elements
|
||||||
|
parent_path, attr_name = f"DataService.{path}".rsplit(".", 1)
|
||||||
|
|
||||||
|
if isinstance(value, list):
|
||||||
|
for index, item in enumerate(value):
|
||||||
|
update_DataService_by_path(
|
||||||
|
self,
|
||||||
|
{
|
||||||
|
"name": f"{attr_name}[{index}]",
|
||||||
|
"parent_path": parent_path,
|
||||||
|
"value": item,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
update_DataService_by_path(
|
||||||
|
self,
|
||||||
|
{
|
||||||
|
"name": attr_name,
|
||||||
|
"parent_path": parent_path,
|
||||||
|
"value": value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def __setattr__(self, __name: str, __value: Any) -> None:
|
def __setattr__(self, __name: str, __value: Any) -> None:
|
||||||
current_value = getattr(self, __name, None)
|
current_value = getattr(self, __name, None)
|
||||||
|
@ -173,6 +173,9 @@ class Server:
|
|||||||
async def shutdown(self) -> None:
|
async def shutdown(self) -> None:
|
||||||
logger.info("Shutting down")
|
logger.info("Shutting down")
|
||||||
|
|
||||||
|
logger.info(f"Saving data to {self._service._filename}.")
|
||||||
|
self._service.write_to_file()
|
||||||
|
|
||||||
await self.__cancel_servers()
|
await self.__cancel_servers()
|
||||||
await self.__cancel_tasks()
|
await self.__cancel_tasks()
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user