mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[modbus] Turn the ModbusDevice alias into a working compatibility shim (#17854)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
eaa79b3696
commit
0833e91fb5
@@ -252,10 +252,27 @@ class ModbusClientDevice {
|
||||
uint8_t address_{0};
|
||||
};
|
||||
|
||||
// This is for compatibility with external components using the former class name
|
||||
// Remove before 2026.12.0
|
||||
using ModbusDevice ESPDEPRECATED("Use ModbusClientDevice instead. Removed in 2026.12.0",
|
||||
"2026.6.0") = ModbusClientDevice;
|
||||
// Compatibility shim for external components written against the pre-2026.8 API, which subclassed
|
||||
// ModbusDevice and overrode on_modbus_data()/on_modbus_error(). The name is free (nothing in-tree
|
||||
// uses it), so instead of a plain alias it adapts the new span-based hooks back to the old
|
||||
// signatures: on_modbus_data() receives the response payload as an owning vector (the heap copy
|
||||
// exists only on this deprecated path) and on_modbus_error() the function code and exception code.
|
||||
// Remove before 2027.2.0 (window restarted when the plain alias became a behavior shim in 2026.8.0)
|
||||
class ESPDEPRECATED("Subclass ModbusClientDevice and override on_response()/on_error() instead. Removed in 2027.2.0",
|
||||
"2026.8.0") ModbusDevice : public ModbusClientDevice {
|
||||
public:
|
||||
using ModbusClientDevice::ModbusClientDevice;
|
||||
virtual void on_modbus_data(const std::vector<uint8_t> &data) {}
|
||||
virtual void on_modbus_error(uint8_t function_code, uint8_t exception_code) {}
|
||||
|
||||
void on_response(std::span<const uint8_t> request_pdu, std::span<const uint8_t> response_pdu) override {
|
||||
auto payload = helpers::server_pdu_payload(response_pdu);
|
||||
this->on_modbus_data(std::vector<uint8_t>(payload.begin(), payload.end()));
|
||||
}
|
||||
void on_error(std::span<const uint8_t> request_pdu, ExceptionCode exception_code) override {
|
||||
this->on_modbus_error(request_pdu.empty() ? 0 : request_pdu[0], static_cast<uint8_t>(exception_code));
|
||||
}
|
||||
};
|
||||
|
||||
// Register values exchanged with server handlers, in host byte order. Sized at the larger of the two protocol
|
||||
// maxima (read = 125 / 0x7D, write = 123 / 0x7B); the per-direction count limit is enforced by the hub, not by
|
||||
|
||||
@@ -220,4 +220,56 @@ TEST(ModbusClientHub, OversizedPduIsRefusedWithNotSent) {
|
||||
EXPECT_TRUE(hub.tx_buffer_empty());
|
||||
}
|
||||
|
||||
// --- ModbusDevice compatibility shim ------------------------------------------------------------
|
||||
// External components written against the pre-2026.8 API subclass ModbusDevice and override the
|
||||
// old callbacks; the shim adapts the span-based hooks back to those signatures.
|
||||
namespace {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
class LegacyApiDevice : public ModbusDevice {
|
||||
public:
|
||||
LegacyApiDevice(ModbusClientHub *hub, uint8_t address) : ModbusDevice(hub, address) {}
|
||||
void on_modbus_data(const std::vector<uint8_t> &data) override { this->last_data_ = data; }
|
||||
void on_modbus_error(uint8_t function_code, uint8_t exception_code) override {
|
||||
this->last_error_fc_ = function_code;
|
||||
this->last_error_code_ = exception_code;
|
||||
}
|
||||
std::vector<uint8_t> last_data_;
|
||||
int last_error_fc_{-1};
|
||||
int last_error_code_{-1};
|
||||
};
|
||||
#pragma GCC diagnostic pop
|
||||
} // namespace
|
||||
|
||||
TEST(ModbusDeviceShim, LegacyCallbacksReceiveTheOldShapes) {
|
||||
NoResponseProbeHub hub;
|
||||
LegacyApiDevice device(&hub, 0x02);
|
||||
|
||||
// Read response: on_modbus_data() historically received the payload after the function code and
|
||||
// the byte-count byte, as an owning vector.
|
||||
const uint8_t read_req[] = {0x03, 0x00, 0x10, 0x00, 0x02};
|
||||
device.send_pdu(read_req);
|
||||
hub.force_send_front();
|
||||
const uint8_t response[] = {0x03, 0x04, 0x00, 0x2A, 0x01, 0x00};
|
||||
hub.receive_frame_for_test(0x02, response);
|
||||
const std::vector<uint8_t> expected{0x00, 0x2A, 0x01, 0x00};
|
||||
EXPECT_EQ(device.last_data_, expected);
|
||||
|
||||
// Write echo: no byte-count byte, so the payload is everything after the function code.
|
||||
const uint8_t write_req[] = {0x06, 0x00, 0x10, 0x00, 0x2A};
|
||||
device.send_pdu(write_req);
|
||||
hub.force_send_front();
|
||||
hub.receive_frame_for_test(0x02, write_req); // single-write responses echo the request
|
||||
const std::vector<uint8_t> expected_echo{0x00, 0x10, 0x00, 0x2A};
|
||||
EXPECT_EQ(device.last_data_, expected_echo);
|
||||
|
||||
// Exception response: on_modbus_error() received the masked function code and the exception code.
|
||||
device.send_pdu(read_req);
|
||||
hub.force_send_front();
|
||||
const uint8_t error[] = {0x83, 0x02};
|
||||
hub.receive_frame_for_test(0x02, error);
|
||||
EXPECT_EQ(device.last_error_fc_, 0x03);
|
||||
EXPECT_EQ(device.last_error_code_, 0x02);
|
||||
}
|
||||
|
||||
} // namespace esphome::modbus::testing
|
||||
|
||||
Reference in New Issue
Block a user