Overview
I am tasked with designing a system that serves as an Interface between a User and one or more microcontrollers in different Variants.
As an example, our Microcontrollers Type 1 are milk frothers. They heat milk to a preset temperature, also they spin a little circular spring to make the milk foamy with varying speed. The speed is set by the user. Microcontroller variant A expects the parameter in terms of motor RPM as a 16-bit uint, variant B in terms of angular velocity as double precision float. Both report back the current milk temperature while frothing to my system. Sometimes I want to forward this info to another Device, say an LCD.
The Users requests from my System to frothe the milk at a certain speed, then expects a response back with some infos once everything is done. In practise, they send jsons to request a specific function with a given set of parameters, and expect a response json with another given set of parameters afterwards.
My Architecture
I decided to split the task into a layered architecture:
Starting from the right, here's what the individual layers do:
The Driver layer provides the low level drivers to interface the Microcontrollers and are vendor-supplied.
The VirtualTarget layer models the actual microcontroller variant and translates the requests made by the preceding layers to what the microcontroller actually expects. In our example, we would have three different implementations for this: Microcontroller B and the two variants of Type A.
The TargetAtomicFunction layer breaks down the user requests into the smallest individual functions the microcontrollers provide, e.g. MilkHeater, SpringPropeller and TemperatureReader. This layer does not know about any variants.
The UserFunction models the User story as described above and breaks it up into a sequence of TargetAtomicFunction blocks, e.g. MilkFrother
The UserInterface translates the json requests to binary data and forwards it to the appropriate UserFunction.
My Problem
I am not sure how to handle request/response parameters. The request parameters need to propagate the entire system and are mostly needed at the VirtualTarget Layer (but not all!). This is also where most response parameter values are generated (but again not all!) and they need to propagate back again.
But at the same time there is no reason for the UserFunction and TargetAtomicFunction layers to know whether the speed parameter has RPM or angular velocity as its unit.
What I came up with is a Request/Response parameter buffer that is accessed by all layers. When a new request arrives at the UserInterface level, the buffer is created with all request and response parameter fields. Then, each layer can read the request parameters it needs to operate, and post all response parameters it delivers. Then after everything is done and the response is returned, the Buffer is emptied. A warning is created if a parameter is read/posted that does not exist, or if some parameters have never been read or posted when the buffer is flushed.
As the actual parameters are known at compile time, I feel like a parameter mismatch would be reasonably easy detectable during integration testing. Yet it feels wrong to have such a component that needs to be known by the entire system.
Constraints
There are some constraints that might be important here:
- I have little control over the user interface (json <-> C struct) and the request/response parameters & their datatypes
- the System runs on a rather obscure embedded system and I have limited influence on the toolchain. Whatever solution I pursue, it will have to be implemented in plain C. No databases, no xml/json parsing,...
- I need to compile a system configuration for Variant A and another for Variant B, it is not possible to unify them into one single configuration. Reasons are out of scope of this question (and my influence).
- The user knows about the Variants! If he uses Variant A, he will pass the speed as RPM uint, for Variant B he passes a double with angular velocity.
- No concurrency. There won't be another user request as long as the first one has not finished/timed out.
My Question
Given the application as described, is my proposed solution sound? Or are there better ways to handle the parameters propagating across layers?