Introduction
All devices are state machines, they have certain states like idle, error, running, and you can send commans to them which may or may not alter the state, events can happen, which are like spontane commands, which can also alter the state or not.
Just to say that this adds quite a bit of complexity since certain commands are only allowed in certain states, and some commands are syncrhone, some not. With synchrone it is meant: we can only continue with the next command if the prior command executed successfully.
Examples of state machines in CTrack are all devices and all proxies. The engine is a state machine for the UI. All devices are state machines, and of all the measurement devices, laser trackers are the most complex machines because :
- they also have substates
- a combintation of asynchrone and synchrone commands
State machines can be implemented in multiple ways. C++17 provides a cleaver scheme using variants, where each state, event, command is a structure with its own data, but by combining them all into a single variant, we can enforce compiler time checks, which makes it safer than using enum where new states may be forgotten.
Another aspect is galvanic isolation: we always have a client like the CTrack UI and a worker like the engine, or in a similar way the engine acts as a client to the worker device proxies, and that is also why we have a unified proxy approach for both the engine and the device proxies. The communication between both client and server is done through a JSON based message queue.
In order to get a full understanding of the complexity and how variants can be used in this we made a test project that shows the state machine of a laser tracker system.
Laser Tracker Threaded HSM
This project can be found on GitHub
https://github.com/lucwens/StateMachine
Read the excellent Readme.md. This also contains a prompt section so that the contents of the project can be used as a template.
Adoption for CTrack
We need to adopt this scheme in CTrack for