112 lines
4.8 KiB
C++
112 lines
4.8 KiB
C++
#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_;
|
|
};
|
|
}
|