#pragma once #include "FluidSim/MachineSensors.hpp" #include "FluidSim/Ecu.hpp" #include #include #include #include #include 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 nodes; std::vector 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 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 logic_evaluation_order(const LogicGraph&,std::vector* cycle_path=nullptr); std::vector 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 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; 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 snapshot()const; const std::vector& commands()const; const std::vector& 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_; }; }