- Replace two duplicate enums with single EntityStatusEnum (same index/code)
- Rename DB columns: state_flag→entity_state_flag, state_ext→entity_state_ext
- Add stateDescription field to DeviceStateDTO/DriverStateDTO for diagnostics
- Add description field to DeviceHealthState/DriverHealthState
- UPSERT SQL now writes stateDescription into entity_state_ext on heartbeat
- Remove EntityStateStatus interface (no longer needed with single enum)
- Update MQ layer, heartbeat services, status queries, gRPC, and all tests
Replace hand-written all-assignment constructors with Lombok
@RequiredArgsConstructor across controllers, services, config classes,
event listeners, and tool classes. No behavioral change.
Last batch of the cross-module refactor away from @Resource field
injection.
Production code:
- dc3-driver/* (8 driver modules): listening-virtual (4 files: Netty
handlers + driver custom impl), virtual, mqtt (2), coap (2),
modbus-tcp, plcs7, opc-da, opc-ua. Fields become private final;
jakarta.annotation.Resource imports are removed.
- NettyTcpServer/NettyUdpServer pass null to NettyTcpServerHandler /
NettyUdpServerHandler when adding the manually-created handler to the
Netty pipeline. Those handler instances are pure stubs that delegate
to the Spring-managed instance via the existing static self-reference,
so a null collaborator field on the stub is functionally equivalent to
the prior no-op default-constructor instance.
Test adaptations:
- 26 tests across dc3-common-mqtt, dc3-common-data, dc3-common-auth,
dc3-common-manager, dc3-common-facade-{local-manager,local-auth,
local-data}, and dc3-driver/* swap the no-args + reflection setup for
a direct constructor call that passes the Lombok-generated parameter
list. Reflection setters and the `injectField` helper are removed
where they only existed for refactor-affected fields; reflection
helpers that touch unrelated state (static factories, private cache
maps used as test seams) are kept.
- 3 dc3-common-driver tests (DeviceCommandReceiverTest,
MetadataReceiverTest, DriverRegisterServiceImplTest) get the same
treatment. MetadataReceiverTest also gains a DriverClient mock to
match the now-five-arg constructor.
- VirtualDriverCustomServiceImplTest and SessionServiceImplTest drop
their reflection-only @BeforeEach blocks: with final fields, Mockito
@InjectMocks now uses constructor injection automatically and matches
collaborators by type.
- PointServiceImplTest gains a missing DriverService @Mock so that
Mockito constructor injection can populate every parameter.
Six public/private static final fields used camelCase, in violation
of the standard Java naming convention for class-level constants:
MqttScheduleJob.messageLock -> MESSAGE_LOCK
MqttScheduleJob.messageCount -> MESSAGE_COUNT
MqttScheduleJob.messageSpeed -> MESSAGE_SPEED
MqttScheduleJob.mqttMessages -> MQTT_MESSAGES
NettyTcpServer.deviceChannelMap -> DEVICE_CHANNEL_MAP
RepositoryStrategyFactory.repositoryServiceMap -> REPOSITORY_SERVICE_MAP
The receiveBatch() method parameter mqttMessages is intentionally
kept as camelCase; only the static field is renamed.
Refresh module READMEs, AGENTS.md, CONTRIBUTING.md, and dc3/doc/
guides to reflect the unified Dockerfile, consolidated compose stacks,
and updated build instructions.
Replace 14 individual Dockerfiles with a single unified Dockerfile at
the repo root using multi-stage BuildKit targets. Maven build runs
inside the builder stage so no host JDK is required.
Update @since from 2022.1.0 to 2016.10.1 and @version from 2026.5.5
to 2026.5.17 across all Java source files to reflect the actual
project start date.
Modbus TCP, OPC DA, and OPC UA all wrapped low-level protocol errors
with constructors that only carried e.getMessage() and dropped the
cause — so the connection / read / write traces ended at the SDK
boundary and operators saw the symptom (a misleading top-level message
without the protocol stack). Switch every connection / read / write
catch site to the placeholder-aware exception constructors so the
driver name, device id, host/port/slave/offset context, and the
underlying Throwable all survive on the cause chain. This pairs with
the SDK-side change that stopped catch-all-wrapping write failures
inside DriverWriteServiceImpl — the receiver now sees the actual
protocol exception type with full stack rather than a generic
ServiceException.
Driver SPI used the cryptic RValue/WValue names for read/write payload
envelopes. Rename them to ReadPointValue/WritePointValue so the public
contract reads naturally, add a sibling CalculatedPointValue for values
that need their derived/scaled form to flow alongside the raw sample,
and pull the value-type conversion utility into a dedicated
support/TypedValueConverter helper instead of leaving it inline.
DriverCustomService.read/write signatures and the seven driver
implementations (modbus-tcp, mqtt, opc-da, opc-ua, plcs7, virtual,
listening-virtual) move to the new types in lockstep so every consumer
of the SDK keeps compiling. Tests are renamed and resequenced to match,
and the driver authoring guide reflects the new identifiers.
PlcS7DriverCustomServiceImplTest (12 tests):
- schedule sends ONLINE / is silent under empty device set
- device update / delete events invalidate the cached connector wrapper
- device add and point events do NOT touch the connection map
- read with primed connector dispenses through serializer and stringifies result
- read swallows serializer failures and returns null (per existing contract)
- write with primed connector stores integer through serializer and returns true
- write returns false when serializer.store throws downstream
- PlcS7PointVariable maps every supported S7 type code (bool/byte/int/dint/word/dword/real/date/time/datetime) and falls back to STRING for unknown codes
- initial is idempotent
The private static MyS7Connector wrapper and connectMap are populated via
reflection so that read/write paths can run without invoking the static
S7ConnectorFactory chain (which depends on a real PLC endpoint).
OpcUaDriverCustomServiceImplTest (9 tests):
- initial allocates an empty connection map and is idempotent
- schedule sends ONLINE for every attached device with 25s TTL
- schedule is silent when no devices are registered
- device update / delete events invalidate the cached OpcUaClient
- device add and point events do NOT touch the connection map
- factory failure (UaException) surfaces as ConnectorException, cache stays empty
- connector is cached across subsequent invocations (factory called once per device)
OpcDaDriverCustomServiceImplTest (17 tests):
- initial allocates an empty connection map
- schedule sends ONLINE / is silent under empty device set
- device update / delete events invalidate the cached Server
- device add and point events do NOT touch the connection map
- getItem reuses existing group when found, adds a new group otherwise
- readItem dispatches by JIVariant.VT_I2/I4/I8/R4/R8/BOOL/BSTR with correct conversion
- readItem falls back to toString() for unknown variant types
Static OpcUaClient.create(...) is mocked via Mockito.mockStatic for the OPC UA
connector tests. OPC DA's vendored DCOM stack uses concrete-class mocking only;
no real DCOM endpoint is required.
MqttDriverCustomServiceImplTest (7 tests):
- initial is no-op and does not touch collaborators
- schedule sends ONLINE for every attached device with 25s TTL
- device and point metadata events are observed without side effects
- read returns null because mqtt is passive
- write publishes with configured commandQos when present
- write falls back to default-QoS overload when commandQos is missing
- write falls back to default-QoS overload when commandQos has wrong type
MqttReceiveServiceImplTest (3 tests):
- single receive parses payload, stamps createTime and forwards via sender
- batch receive forwards point values in order with createTime stamped
- single receive tolerates null header (logs null topic/qos and proceeds)
ModbusTcpDriverCustomServiceImplTest (16 tests):
- schedule sends ONLINE for every attached device with 25s TTL
- device update / delete events invalidate the cached ModbusMaster
- point events do not touch the per-device connection cache
- first read creates and inits the connection, subsequent reads reuse it
- connection init failure surfaces as ConnectorException with original cause
- read dispatches by function code 1=coil, 2=input status, 3=holding, 4=input register
- read with unsupported function code returns "0" without contacting master
- read transport / error response failures surface as ReadPointException
- write coil returns true when response is not an exception, false otherwise
- write coil transport failure surfaces as WritePointException
- write holding register succeeds when setValue does not throw
- write holding register transport failure surfaces as WritePointException
- write with unsupported function code returns false and skips master entirely
Static ModbusFactory field is swapped via reflection per test and restored after.
VirtualDriverCustomServiceImplTest (9 tests):
- initial is no-op and does not touch collaborators
- schedule sends ONLINE for every attached device with 25s TTL
- schedule is silent when no devices are registered
- device and point metadata events are observed without side effects
- read returns constant string for STRING points
- read returns parseable boolean for BOOLEAN points
- read returns numeric value in [0, 100) for other point types
- write returns false unconditionally
ListeningVirtualDriverCustomServiceImplTest (7 tests):
- initial dispatches both TCP and UDP listeners through the thread pool
- schedule sends ONLINE for every attached device with 25s TTL
- device and point metadata events are observed without side effects
- read returns null because the listener is passive
- write flushes value through the channel when device channel is registered
- write is a no-op (returns true) when no channel is mapped for the device
16 GREEN.
Rename the generic DriverCustomServiceImpl in each driver module to a
protocol-specific name (e.g., ModbusTcpDriverCustomServiceImpl) so
that static analysis tools can scan every driver module without
filtering duplicate fully-qualified class names. Update
DRIVER-AUTHORING.md to reflect the new convention.
A prior docs-simplification commit (4a17fcd7f) merged two adjacent class
Javadoc blocks and inadvertently dropped the leading 'abstract' modifier
that lived between them, leaving 18 abstract-by-design classes declared
as concrete. The compiler then rejected each one with "is not abstract
and does not override abstract method ...".
Restoring 'abstract' on BaseLocator, ModbusMaster/SlaveSet, ModbusMessage
and its Request/Response subclasses, BaseMessageParser/RequestHandler,
SerialMaster/Message/Slave, IpMessage, AsciiMessage, ProgressiveTask,
and ModbusValue makes dc3-driver-modbus-tcp compile again.
- Remove netty-all and netty-resolver-dns-native-macos from listening-virtual
pom.xml (already provided transitively via grpc-netty)
- Add field comments to NettyTcpServerHandler, NettyUdpServerHandler, and
PlcS7PointVariable
Remove dangling Javadoc comments between annotations and fields across
all driver modules. Strip redundant inline block comments from method
bodies that duplicate method-level Javadoc. Improve class-level Javadoc
and simplify verbose private method documentation. Convert traditional
switch to enhanced switch expression in Modbus TCP driver.
- Bump all module versions from 2025.11.1 to 2026.4.18.3
- Remove orphaned dc3-common-influx dependency and property from root pom.xml
- Standardize dependency comment style in dc3-common/pom.xml
- Merge gRPC dependency groups into single section in dc3-common-api
- Restructure Spring Integration MQTT and Eclipse Paho groups in dc3-common-mqtt
- Fix incorrect comment 'DC3 Common Exception' to 'DC3 Common Related' in dc3-common-postgres
- Add missing blank line before </dependencies> in dc3-common-driver
- Remove stray blank line in root pom.xml dependencyManagement