ics-simlab-config-gen-claude/models/ir_v1.py

116 lines
3.0 KiB
Python

from __future__ import annotations
from typing import Dict, List, Literal, Optional, Union
from pydantic import BaseModel, ConfigDict, Field
# -------------------------
# HIL blocks (v1.3)
# -------------------------
class TankLevelBlock(BaseModel):
model_config = ConfigDict(extra="forbid")
type: Literal["tank_level"] = "tank_level"
level_out: str
inlet_cmd: str
outlet_cmd: str
dt: float = 0.1
area: float = 1.0
max_level: float = 1.0
inflow_rate: float = 0.25
outflow_rate: float = 0.25
leak_rate: float = 0.0
initial_level: Optional[float] = None
class BottleLineBlock(BaseModel):
"""
Minimal bottle + conveyor dynamics (Strada A):
- bottle_at_filler_out = 1 when conveyor_cmd <= 0.5 else 0
- bottle_fill_level_out increases when at_filler==1
- bottle_fill_level_out decreases slowly when conveyor ON (new/empty bottle coming)
"""
model_config = ConfigDict(extra="forbid")
type: Literal["bottle_line"] = "bottle_line"
conveyor_cmd: str
bottle_at_filler_out: str
bottle_fill_level_out: str
dt: float = 0.1
fill_rate: float = 0.25 # per second
drain_rate: float = 0.40 # per second when conveyor ON (reset toward 0)
initial_fill: float = 0.0
HILBlock = Union[TankLevelBlock, BottleLineBlock]
class IRHIL(BaseModel):
model_config = ConfigDict(extra="forbid")
name: str
logic: str
outputs_init: Dict[str, float] = Field(default_factory=dict)
blocks: List[HILBlock] = Field(default_factory=list)
# -------------------------
# PLC rules (v1.2)
# -------------------------
class HysteresisFillRule(BaseModel):
model_config = ConfigDict(extra="forbid")
type: Literal["hysteresis_fill"] = "hysteresis_fill"
level_in: str
low: float = 0.2
high: float = 0.8
inlet_out: str
outlet_out: str
enable_input: Optional[str] = None
# Signal range for converting normalized thresholds to absolute values
# If signal_max=1000, then low=0.2 becomes 200, high=0.8 becomes 800
signal_max: float = 1.0 # Default 1.0 means thresholds are already absolute
class ThresholdOutputRule(BaseModel):
model_config = ConfigDict(extra="forbid")
type: Literal["threshold_output"] = "threshold_output"
input_id: str
threshold: float = 0.2
op: Literal["lt"] = "lt"
output_id: str
true_value: int = 1
false_value: int = 0
# Signal range for converting normalized threshold to absolute value
# If signal_max=200, then threshold=0.2 becomes 40
signal_max: float = 1.0 # Default 1.0 means threshold is already absolute
PLCRule = Union[HysteresisFillRule, ThresholdOutputRule]
class IRPLC(BaseModel):
model_config = ConfigDict(extra="forbid")
name: str
logic: str
rules: List[PLCRule] = Field(default_factory=list)
class IRSpec(BaseModel):
model_config = ConfigDict(extra="forbid")
version: Literal["ir_v1"] = "ir_v1"
plcs: List[IRPLC] = Field(default_factory=list)
hils: List[IRHIL] = Field(default_factory=list)