******************************************************* Mid.CBF FPGA Host Server (FHS) Device Simulation Design ******************************************************* How to simulate an FHS device ----------------------------- FHS Tango devices can be simulated using the `ska_mid_cbf_fhs_common.testing.simulation module`. This module provides several key classes, including; * `FhsSimMode`: simulation mix-in for FHS device classes - supplies the `simOverrides` attribute to the device interface * `SimModeCMBase`: base for simulating FHS component manager (CM) classes * `FhsObsSimMode` and `SimModeObsCMBase`: extensions of the above classes for simulation of observing devices Any FHS device needing simulation can be turned into a standalone simulator with the following changes: * Create a sim CM for the device that replicates the actual CM's interface. The FHS sim CM inherits from either `SimModeCMBase` or `SimModeObsCMBase`. Three basic things must be implemented: * Default attribute return values, stored in `attribute_overrides`. * Attribute getters so that `attribute_overrides` can be read by the device class. * Attribute setters for any write attributes, so the device class can set `attribute_overrides`. * Default command return values, stored in `command_overrides`. * Create an FHS simulator device class, that inherits from both the actual FHS device class and one of either `FhsSimMode` or `FhsObsSimMode`. * This will need a `main()` method to run the simulator, which will furthermore require an entrypoint script in `pyproject.toml`. * This process startup command will lastly need to be added to the chart values such that it is called instead of the genuine device startup when the value `standaloneSimulator` is set to `true`. **For a fleshed-out example see the simulation of VCCAllBandsController:** `ska-mid-cbf-fhs-vcc `_ *Example test code: overriding On command result code* .. code-block:: python # Get a proxy to the simulated device. simulator = tango.DeviceProxy("") # Commands are set by default to return OK. (result_code, message) = simulator.On() assert (result_code, message) == (ResultCode.OK, "On completed OK") # Override the result code value by writing the simOverrides attribute. # Only behaviours included in the JSON configuration will override the default, # so here the returned message will stay the same. simulator.simOverrides = json.dumps( { "commands": { "On": { "result_code": "FAILED" } } } ) (result_code, message) = simulator.On() assert (result_code, message) == (ResultCode.FAILED, "On completed OK") Class breakdown --------------- * FHS simulator device base mix-in (`FhsSimMode`) - inherits from ska-tango-base `tango.Device` and implements patterns from the ska-tango-base class `TestModeOverrideMixin` * Adds the `simOverrides` attribute: write a JSON-formatted string containing overrides under 2 keys, "attributes" and "commands", which update the `attribute_overrides` and `command_overrides` dicts respectively * "attributes": keys are attribute names, values are override values for those attributes * Initialized with default values * Updated with values from `simOverrides.write`; any change in attribute values will push a new change event for those attributes * Attribute write methods also update the override values; clients should be able to write new values to the simulator and expect them to be stored if allowed * "commands": keys are command names, values are dicts containing the following: * For LRCs: command callback update values, including result code, message, component state updates * For FastCommands: return value tuple * start/stop_communicating: callback update values to edit adminMode pseudo-command behaviour * FHS simulator component manager class (SimModeCMBase) - inherits from the ska-tango-base class `TaskExecutorComponentManager` * Provides 3 methods for simulating LRC behaviour * sim_command: behaves as typical LRC target method, simply submits private _sim_command method to the task executor queue; the only difference is that it takes in the command name as sole argument, passing it to the private method as well, for logging and task_callback use * is_sim_command_allowed: returns True and thus executes the _sim_command private method if "allowed" is set to True or None in command overrides; if set to False, simulate command rejection. Used by both LRCs and FastCommandSim. * _sim_command: private LRC execution method (following the pattern established for all other Mid.CBF devices), follows command_overrides to indicate behaviour of command * start/stop_communicating: `adminMode.write` is a pseudo-command, so we can alter the behaviour of these component manager methods as well Example sequence: using simOverrides ===================================== .. uml:: ./diagrams/simulator_test.puml