Add machine workshop, typed logic, mechanics and sensor-driven ECUs
This commit is contained in:
111
include/FluidSim/MachineLogic.hpp
Normal file
111
include/FluidSim/MachineLogic.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
#include "FluidSim/MachineSensors.hpp"
|
||||
#include "FluidSim/Ecu.hpp"
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace fluidsim::port {
|
||||
struct MachineDocument;
|
||||
enum class LogicNodeKind {
|
||||
BooleanConstant,Switch,Button,NumberConstant,Slider,NumberInput,
|
||||
And,Or,Not,Xor,Nand,Nor,Greater,Less,Equal,Window,Hysteresis,
|
||||
Add,Subtract,Multiply,Divide,Minimum,Maximum,Absolute,Clamp,GainOffset,MapRange,Select,BooleanToNumber,
|
||||
Sensor,Command,Lamp,Readout,BooleanDelay,NumberDelay,Ecu
|
||||
};
|
||||
inline constexpr int logic_node_kind_count=36;
|
||||
enum class LogicCommand {MotorEnable,MotorSpeed,MotorTorque,BrakeEnable,ThrottleOpening,Starter};
|
||||
inline constexpr int logic_command_count=6;
|
||||
struct LogicPosition {float x{},y{};bool operator==(const LogicPosition&)const=default;};
|
||||
struct LogicNode {
|
||||
std::uint32_t id{};
|
||||
LogicNodeKind kind{LogicNodeKind::Switch};
|
||||
std::string name;
|
||||
LogicPosition position;
|
||||
SignalQuantity quantity{SignalQuantity::Scalar},output_quantity{SignalQuantity::Scalar};
|
||||
PressureReference pressure_reference{PressureReference::None},output_pressure_reference{PressureReference::None};
|
||||
double value{},minimum{},maximum{1},low{.25},high{.75},tolerance{1e-6};
|
||||
double gain{1},offset{},false_value{},true_value{1},input_low{},input_high{1},output_low{},output_high{1};
|
||||
bool boolean{},initial{},below{},clamp{true};
|
||||
unsigned input_count{2};
|
||||
std::uint32_t sensor{},part{};
|
||||
MachineSensorKind sensor_kind{MachineSensorKind::Pressure}; // Schema survives an unresolved reference.
|
||||
SensorEcuConfig::Strategy ecu_strategy{SensorEcuConfig::Strategy::Ignition};
|
||||
LogicCommand command{LogicCommand::MotorEnable};
|
||||
std::string binding_label;
|
||||
bool operator==(const LogicNode&)const=default;
|
||||
};
|
||||
struct LogicEndpoint {
|
||||
std::uint32_t node{};
|
||||
std::string port;
|
||||
bool operator==(const LogicEndpoint&)const=default;
|
||||
};
|
||||
struct LogicLink {
|
||||
LogicEndpoint from,to;
|
||||
bool operator==(const LogicLink&)const=default;
|
||||
};
|
||||
struct LogicGraph {
|
||||
std::uint32_t next_id{1};
|
||||
std::vector<LogicNode> nodes;
|
||||
std::vector<LogicLink> links;
|
||||
LogicNode* find(std::uint32_t);
|
||||
const LogicNode* find(std::uint32_t)const;
|
||||
LogicNode& add(LogicNodeKind,LogicPosition={});
|
||||
void remove(std::uint32_t);
|
||||
void disconnect(const LogicEndpoint&);
|
||||
void connect(LogicEndpoint,LogicEndpoint); // Canonical output -> input; validates without mutation on failure.
|
||||
bool operator==(const LogicGraph&)const=default;
|
||||
};
|
||||
const char* logic_node_key(LogicNodeKind);
|
||||
const char* logic_node_name(LogicNodeKind);
|
||||
const char* logic_command_key(LogicCommand);
|
||||
const char* logic_command_name(LogicCommand);
|
||||
bool logic_memory_node(LogicNodeKind);
|
||||
std::vector<MachinePortDescriptor> logic_node_ports(const LogicNode&,const MachineDocument* context=nullptr);
|
||||
MachinePortDescriptor logic_port(const LogicGraph&,const LogicEndpoint&);
|
||||
PortCompatibility logic_connection_preview(const LogicGraph&,LogicEndpoint,LogicEndpoint);
|
||||
void validate_logic_graph(const LogicGraph&); // Drafts may have required unconnected inputs.
|
||||
std::vector<std::uint32_t> logic_evaluation_order(const LogicGraph&,std::vector<std::uint32_t>* cycle_path=nullptr);
|
||||
std::vector<std::string> logic_required_inputs(const LogicGraph&);
|
||||
bool logic_topology_equal(const LogicGraph&,const LogicGraph&);
|
||||
// Empty means a compatible physical binding. Missing targets remain unresolved.
|
||||
std::string logic_binding_problem(const MachineDocument&,const LogicNode&);
|
||||
const LogicNode* logic_command_owner(const MachineDocument&,std::uint32_t part,LogicCommand);
|
||||
void validate_machine_logic(const MachineDocument&);
|
||||
struct LogicNodeState {
|
||||
std::uint32_t id{};
|
||||
std::vector<SignalValue> values; // Same order as logic_node_ports, including inputs.
|
||||
std::string problem;
|
||||
};
|
||||
struct LogicCommandState {
|
||||
std::uint32_t node{},part{};
|
||||
LogicCommand command{};
|
||||
SignalValue value; // Invalid values require the declared inactive consumer fallback.
|
||||
};
|
||||
struct LogicEcuInputs {
|
||||
std::uint32_t node{},part{};
|
||||
SignalValue speed,angle,tps,map,iat;
|
||||
};
|
||||
class MachineLogicRuntime {
|
||||
public:
|
||||
using SensorReader=std::function<MachineSensorState(std::uint32_t)>;
|
||||
explicit MachineLogicRuntime(const MachineDocument&,SensorReader={});
|
||||
~MachineLogicRuntime();
|
||||
MachineLogicRuntime(const MachineLogicRuntime&)=delete;
|
||||
MachineLogicRuntime& operator=(const MachineLogicRuntime&)=delete;
|
||||
void evaluate(double time,double dt);
|
||||
SignalValue value(const LogicEndpoint&)const;
|
||||
std::vector<LogicNodeState> snapshot()const;
|
||||
const std::vector<LogicCommandState>& commands()const;
|
||||
const std::vector<LogicEcuInputs>& ecu_inputs()const;
|
||||
void set_manual(std::uint32_t,bool);
|
||||
void set_manual(std::uint32_t,double);
|
||||
void release_buttons();
|
||||
void apply_tuning(const MachineDocument&);
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user