State management
User state commands reduced to Init() and Off() which request (re)initialisation and turning the device off, respectively. All transitions are managed by the device.
Merge request reports
Activity
The user cannot modify the state, as the state represents the conditions of the device, not what the user wants those conditions to be. The On state is a direct consequence of succesful initialisation. The Fault state is a direct consequence of encountering an unrecoverable error.
If the device does not perform state transitions, the state is not an actual state... it'd misrepresent what the state actually is, and would run into issues for state transitions which the device feels forced to (f.e. Fault).
Init() allows the user to reinitialise when the device is stuck in Fault or Off. The Init() call is thus effectively an "ON" command. Off() allows the user to turn the device off, which should be possible at any time. So both these commands effectively force a state transition, but only because the device can always respond directly and accordingly.
Code updated. Added STANDBY to mean "initialised, configure me further if needed before going ON". We now can trigger most states "manually", except STANDBY. INIT->STANDBY happens only if the initialisation succeeds. It creates a malfunctioning device if we allow a human to force STANDBY even if initialisation is incomplete. We can however go to FAULT manually, simulating a malfunctioning device.
The implemented behaviour is described in the comments near the top of RCUSCC.py.
Looks now very good. One last tiny piece is missing: how can we start the code, i.e. run it and avoid that the device runs away with its configuration (good or bad)?
This touches on the problem that we or a master device need to see that all devices arrive at a checkpoint (a state) when commanded to perform the transition. We cannot see that for the device code not running -> device code running -> init -> standby sequence because every device would now init itself as soon as its code runs. This can create some really nasty side effects when we start-up 20 devices. All of them immediately going to init. Could result in unavailable devices because dev A depends on B but dev B is not ready yet.
Bottom line: rather not have automatic init after power up, i.e. executing the code. Running the code means the device is powered up. Or whatever one calls it, the device exists in the Tango context as device. One can get a reference to it and ask the state() command for the current state and receives OFF. Then it awaits the user or a master device to tell it to init. Results in STANDBY. Then the user or some master device checks that everything is in standby. Then the user or a master device tells the devices in the correct sequence (if necessary) to transition to ON.
Good point. Given the master device:
Our RCUSCC device indeed now inits on boot. Your concerns are valid. So the question is, does our master device control the booting of the RCUSCC device? I suppose it could, but it feels nicer to have the RCUSCC device always available even regardless of the master device. That will allow us to reason about it and refer to it, etc.
So I'll indeed make the INIT explicit. The init_device function thus only performs very basic initialisation of the python class (possibly, nothing). And we get an Init() command, obviously.
That's a very good idea! Having the devices "there", i.e. they are present, we can browse them in the Jive GUI (administrative GUI for Tango) and see them being alive. The master device is also always just there. Like daemons in a Unix system. Computer boots, init starts the daemons. Our LCU2 boots, magically the Tango Device Servers (remember that a device runs inside a Device Server) get started and Bob's your uncle.
We can connect to the master device, tell it to init, it then in turn will init the devices that itself needs to function.
99.99999% of the devices will be able to init quickly which means that master can wait for them to go to standby and check on 'em. Also most if not all of the devices can be init'ed in parallel since they go to stand-by which is per definition their state where everything is ready to operate but doesn't work on anything. Not data flows, only MPs and CPs are read and archived, no "activity".
OK. So we have now:
- Device Server is started in an automatic way. (Need to figure out how to do it the Tango way. -> Task for somebody!)
- Device class is instantiated by Device Server in an automatic way. (Handled by Tango, nothing to do for us.)
- Device stops automatically after it has performed device_init(). There it sets its state to Tango's "OFF" state and waits for a command to perform an allowed transition. (We have to set the state to OFF ourselves. Not setting a state means the device is in state "UNKNOWN".)
- Allowed transitions from OFF:
- STANDBY
- FAULT (could that be possible, think of Tango crapping out?)
- Device receives "perform init" command:
- On success Device's state will be Tango's "STANDBY".
- On failure Device's state will be Tango's "FAULT".
- Device receives "let's rock" command:
- If there's still something to do between INIT and ON, the Device will do it.
- On success the Device's state will be Tango's "ON".
- On failure the Device's state will be Tango's "FAULT".
- In "ON" allowed states will be
- "OFF"
- "FAULT"
- In "OFF": rinse and repeat.
- In "FAULT":
- Transition to "OFF"
- Also transition to STANDBY via "perform init"?
- What about clearing errors that need clearing?
added 1 commit
- 1691f08a - Boot into OFF state, requiring explicit INIT (f.e. after other devices have been instantiated).
mentioned in commit f66598cb