Skip to main content

rusty_modbus_sim/
scenario.rs

1//! Pre-built device profiles for common equipment types.
2
3use crate::config::{
4    CoilBlock, DeviceConfig, RegisterBlock, RegisterConfig, SimConfig, UpdateMode,
5};
6
7/// HVAC controller profile — typical BAS field device.
8///
9/// Holding registers: setpoints, PID parameters.
10/// Input registers: temperature, humidity, pressure sensors.
11/// Coils: fan on/off, damper open/close, valve enable.
12#[must_use]
13pub fn hvac_controller() -> SimConfig {
14    SimConfig {
15        device: DeviceConfig {
16            unit_id: 1,
17            vendor_name: String::from("Acme Controls"),
18            product_code: String::from("ACM-HVAC-100"),
19            revision: String::from("2.1.0"),
20            listen_addr: String::from("127.0.0.1:0"),
21        },
22        registers: RegisterConfig {
23            holding: vec![RegisterBlock {
24                address: 0,
25                count: 10,
26                initial: vec![
27                    720, // Setpoint: 72.0°F (×10)
28                    680, // Heating setpoint
29                    760, // Cooling setpoint
30                    50,  // Fan speed %
31                    100, // PID P gain (×10)
32                    20,  // PID I gain (×10)
33                    5,   // PID D gain (×10)
34                    0, 0, 0,
35                ],
36                mode: UpdateMode::Static,
37                min: 0,
38                max: 1000,
39            }],
40            input: vec![RegisterBlock {
41                address: 0,
42                count: 8,
43                initial: vec![721, 450, 1013, 0, 0, 0, 0, 0],
44                mode: UpdateMode::Random,
45                min: 600,
46                max: 800,
47            }],
48            coils: vec![CoilBlock {
49                address: 0,
50                count: 8,
51                initial: vec![true, false, true, false, false, false, false, false],
52            }],
53            discrete_inputs: vec![],
54        },
55        faults: vec![],
56    }
57}
58
59/// Power meter profile — energy monitoring device.
60///
61/// Input registers: voltage, current, power, energy counters.
62#[must_use]
63pub fn power_meter() -> SimConfig {
64    SimConfig {
65        device: DeviceConfig {
66            unit_id: 2,
67            vendor_name: String::from("PowerCo"),
68            product_code: String::from("PM-3000"),
69            revision: String::from("1.0.0"),
70            listen_addr: String::from("127.0.0.1:0"),
71        },
72        registers: RegisterConfig {
73            holding: vec![],
74            input: vec![RegisterBlock {
75                address: 0,
76                count: 10,
77                initial: vec![
78                    2400, // Voltage L1 (×10 = 240.0V)
79                    2390, // Voltage L2
80                    2410, // Voltage L3
81                    150,  // Current L1 (×10 = 15.0A)
82                    148,  // Current L2
83                    152,  // Current L3
84                    3600, // Active power (W)
85                    100,  // Power factor (×100 = 1.00)
86                    0, 0,
87                ],
88                mode: UpdateMode::Random,
89                min: 2300,
90                max: 2500,
91            }],
92            coils: vec![],
93            discrete_inputs: vec![],
94        },
95        faults: vec![],
96    }
97}
98
99/// Variable frequency drive profile.
100///
101/// Holding registers: speed setpoint, accel/decel times.
102/// Input registers: actual speed, motor current, bus voltage.
103#[must_use]
104pub fn vfd_drive() -> SimConfig {
105    SimConfig {
106        device: DeviceConfig {
107            unit_id: 3,
108            vendor_name: String::from("DriveTech"),
109            product_code: String::from("VFD-500"),
110            revision: String::from("3.2.1"),
111            listen_addr: String::from("127.0.0.1:0"),
112        },
113        registers: RegisterConfig {
114            holding: vec![RegisterBlock {
115                address: 0,
116                count: 6,
117                initial: vec![
118                    1500, // Speed setpoint (RPM)
119                    10,   // Accel time (seconds)
120                    10,   // Decel time (seconds)
121                    0,    // Run command (0=stop, 1=run)
122                    0, 0,
123                ],
124                mode: UpdateMode::Static,
125                min: 0,
126                max: 3600,
127            }],
128            input: vec![RegisterBlock {
129                address: 0,
130                count: 4,
131                initial: vec![0, 0, 480, 25],
132                mode: UpdateMode::Static,
133                min: 0,
134                max: 3600,
135            }],
136            coils: vec![CoilBlock {
137                address: 0,
138                count: 4,
139                initial: vec![false, false, false, false],
140            }],
141            discrete_inputs: vec![CoilBlock {
142                address: 0,
143                count: 4,
144                initial: vec![true, false, false, false],
145            }],
146        },
147        faults: vec![],
148    }
149}
150
151/// Generic I/O module — simple 16 coils + 16 registers.
152#[must_use]
153pub fn generic_io() -> SimConfig {
154    SimConfig {
155        device: DeviceConfig {
156            unit_id: 1,
157            vendor_name: String::from("Generic"),
158            product_code: String::from("IO-16"),
159            revision: String::from("1.0.0"),
160            listen_addr: String::from("127.0.0.1:0"),
161        },
162        registers: RegisterConfig {
163            holding: vec![RegisterBlock {
164                address: 0,
165                count: 16,
166                initial: vec![0; 16],
167                mode: UpdateMode::Static,
168                min: 0,
169                max: 65535,
170            }],
171            input: vec![RegisterBlock {
172                address: 0,
173                count: 16,
174                initial: vec![0; 16],
175                mode: UpdateMode::Static,
176                min: 0,
177                max: 65535,
178            }],
179            coils: vec![CoilBlock {
180                address: 0,
181                count: 16,
182                initial: vec![false; 16],
183            }],
184            discrete_inputs: vec![CoilBlock {
185                address: 0,
186                count: 16,
187                initial: vec![false; 16],
188            }],
189        },
190        faults: vec![],
191    }
192}