I declare my data structures in YAML. From this I generate code for memory allocation; data validation; serialization and de-serialisation routines.
I also declare the topology of the data flow graph in a separate YAML file, decorating each edge with the data type.
Finally, I have a separate YAML file that specifies which computers/processes each node will run on.
Nodes communicate by passing messages. For nodes in the same process, this is simply shared memory (zero overhead). For nodes in different processes that share a memory bus, this is a shared memory queue. For nodes on different machines, the data goes over the network.
Nodes can be python, C or C++.
Very nice. Very declarative. The bare minimum of duplication and boilerplate. Changing from MIL to SIL to HIL is (mostly) just a configuration change. Logging, measurement and replay is built into the framework, as is support for parameter tuning.
Within a single process, the model of computation is synchronous data flow. Across processes, it is asynchronous data flow, but because we have queues with blocking reads and nonblocking writes, we still keep the ability to replay data through the system in a deterministic manner. (Modulo nodes exceeding their time budget).