mod behavior;
pub mod config;
mod connection_core;
pub mod context;
pub mod control;
pub mod core;
pub mod device;
pub mod error;
pub mod fault_injection;
pub mod handler;
pub mod profile;
pub mod register;
pub mod registers;
pub mod rtu;
pub mod runtime;
#[cfg(feature = "experimental-scaling")]
pub mod scalability;
mod semantic;
pub mod server;
pub mod service;
pub mod simulator;
pub mod tcp;
#[cfg(feature = "testing")]
pub mod testing;
mod transport_runtime;
pub mod types;
pub mod unit;
pub use context::{
AddressSpace, BroadcastPolicy, DenseRegisterStore, DeviceContext, ServerContext,
};
pub use control::{
BehaviorSetPort, FaultPresetPort, ModbusControlSession, PointCatalogPort, PointCatalogQuery,
PointDescriptor, PointTarget, RegisterControlPort, ResponseProfilePort, SessionControlPort,
SessionMetadataPort, SessionSnapshot, SessionStatus, TraceEntry, TraceOperation, TracePort,
TraceStatus,
};
pub use core::{FunctionCode, RequestPdu, ResponsePdu, SemanticRequest, SemanticResponse};
pub use config::{ModbusDeviceConfig, ModbusServerConfig};
pub use device::ModbusDevice;
pub use error::{ModbusError, ModbusResult};
pub use profile::{
DatastoreKind, GeneratedProfilePreset, PointProfile, SimulatorProfile, UnitProfile,
};
pub use register::{RegisterStore, RegisterType};
pub use server::ModbusTcpServer;
pub use service::{
ExtensionContext, ExtensionHandler, ExtensionMetadata, ExtensionRegistry, ExtensionRequest,
ServiceOutcome,
};
pub use simulator::{
schema_summary, ActionBindingDefinition, ActionBindingSummary, ActionDefinition, ActionTrigger,
BehaviorBindingSummary, BehaviorCondition, BehaviorConditionOperator, BehaviorDefinition,
BehaviorSetDefinition, BehaviorTarget, BehaviorTrigger, CompiledModbusSession,
CompiledPointMetadata, CompiledTransportKind, DatastoreAddressRange, DatastoreDefinition,
DatastoreInitialization, DatastorePolicySummary, DatastoreRepeatPolicy, DatastoreSelector,
DatastoreTypedBlock, DeviceBundleDefinition, GeneratedPresetDefinition,
MalformedResponseDefinition, ModbusConfigSummary, ModbusSchemaSummary,
ModbusServiceLaunchConfig, ModbusSimulatorConfig, ModbusTransportLaunch,
PartialResponseDefinition, PointActionBinding, ResponseProfileDefinition, SchemaSection,
SessionControlConfig, SessionDefinition, SessionResetPolicy, SessionSummary,
SessionTraceConfig, SimulatorDefaults, SplitResponseDefinition, TransportDefinition,
UnitDefinition,
};
pub use tcp::ModbusTcpServerV2;
pub use registers::{
AddressRange,
CallbackManager,
CallbackPriority,
DefaultValue,
InitializationMode,
ReadCallback,
ReadCallbackFn,
RegisterRangeConfig,
RegisterStoreConfig,
RegisterValue,
SparseRegisterStore,
WriteCallback,
WriteCallbackFn,
};
pub use rtu::{
ModbusRtuServer, RtuCodec, RtuFrame, RtuServerConfig, RtuTiming, SerialConfig, VirtualSerial,
VirtualSerialConfig,
};
pub use types::{RegisterConverter, RegisterDataType, TypedValue, WordOrder};
pub use unit::{BroadcastMode, MultiUnitManager, UnitConfig, UnitInfo, UnitManagerConfig};
#[cfg(feature = "testing")]
pub use testing::{
AllocationTracker,
ConnectionSimulator,
LoadConfig,
LoadGenerator,
LoadPattern,
MemoryProfiler,
MemoryReport,
MemorySnapshot,
PerformanceConfig,
PerformanceTarget,
PerformanceValidator,
TestMetrics,
TestReport,
TestSummary,
ValidationResult,
};
#[cfg(feature = "faults")]
pub use fault_injection::{
CrcCorruptionFault, DelayedResponseFault, ExceptionInjectionFault, ExtraDataFault, FaultAction,
FaultConfig, FaultInjectionConfig, FaultPipeline, FaultStats, FaultStatsSnapshot, FaultTarget,
FaultType, ModbusFault, ModbusFaultContext, NoResponseFault, PartialFrameFault, TransportKind,
TruncatedResponseFault, WrongFunctionCodeFault, WrongTransactionIdFault, WrongUnitIdFault,
};
#[cfg(feature = "faults")]
pub use fault_injection::connection_disruption::{
ConnectionDisruptionConfig, ConnectionDisruptionState, DisruptionAction,
};
#[cfg(feature = "faults")]
pub use fault_injection::rtu_timing::{
BusCollisionConfig, ByteJitterConfig, CollisionMode, GapPosition, InterCharGapConfig,
RtuTimingFaultConfig, TimingPlan, TimingSegment,
};
pub use runtime::{descriptor, driver};
pub type Config = tcp::ServerConfigV2;
pub type Profile = SimulatorProfile;
pub type Device = ModbusDevice;
pub type Server = ModbusTcpServerV2;
pub type Error = ModbusError;
pub type Result<T> = ModbusResult<T>;
pub type Stats = tcp::ServerMetrics;
#[derive(Debug, Clone, Default)]
pub struct Builder {
config: Config,
profile: Option<Profile>,
}
impl Builder {
pub fn new() -> Self {
Self::default()
}
pub fn config(mut self, config: Config) -> Self {
self.config = config;
self
}
pub fn profile(mut self, profile: Profile) -> Self {
self.profile = Some(profile);
self
}
pub fn generated_profile(mut self, devices: usize, points_per_device: usize) -> Self {
self.profile = Some(Profile::generated(devices, points_per_device));
self
}
pub fn build(self) -> Result<Server> {
let server = Server::new(self.config);
if let Some(profile) = self.profile {
server.set_broadcast_enabled(profile.broadcast_enabled);
for unit in profile.units {
server.add_device(Device::from_profile(&unit)?);
}
}
Ok(server)
}
}
#[derive(Debug, Clone, Default)]
pub struct Factory;
impl Factory {
pub fn server(config: Config) -> Server {
Server::new(config)
}
pub fn device(config: ModbusDeviceConfig) -> Device {
Device::new(config)
}
}
#[cfg(test)]
mod architecture_tests {
use mabi_core::device::Device as _;
use mabi_core::types::{DataType, ModbusRegisterType};
use super::{Builder, Config, DatastoreKind, PointProfile, Profile, UnitProfile};
#[test]
fn builder_populates_server_from_profile() {
let profile = Profile::new()
.with_unit(
UnitProfile::new(7, "Pump-A")
.with_datastore(DatastoreKind::dense_from_counts(16, 16, 16, 16))
.with_point(PointProfile::new(
"holding_temp",
"Holding Temperature",
ModbusRegisterType::HoldingRegister,
0,
DataType::UInt16,
))
.with_point(PointProfile::new(
"coil_enable",
"Enable",
ModbusRegisterType::Coil,
1,
DataType::Bool,
)),
)
.with_unit(
UnitProfile::new(9, "Pump-B")
.with_datastore(DatastoreKind::dense_from_counts(16, 16, 16, 16)),
);
let server = Builder::new()
.config(Config::default())
.profile(profile)
.build()
.unwrap();
let mut ids = server.device_ids();
ids.sort_unstable();
assert_eq!(ids, vec![7, 9]);
assert_eq!(server.device(7).unwrap().info().point_count, 2);
assert_eq!(server.device(7).unwrap().context().name(), "Pump-A");
}
#[test]
fn generated_profile_builder_preserves_legacy_device_counts() {
let server = Builder::new().generated_profile(2, 8).build().unwrap();
let mut ids = server.device_ids();
ids.sort_unstable();
assert_eq!(ids, vec![1, 2]);
assert_eq!(server.device(1).unwrap().info().point_count, 8);
assert_eq!(server.device(2).unwrap().info().point_count, 8);
}
}