1*7184805aSJagpal Singh Gill #include "common/events.hpp"
2e92aba45SJagpal Singh Gill #include "device/device_factory.hpp"
3e92aba45SJagpal Singh Gill #include "modbus_server_tester.hpp"
4e92aba45SJagpal Singh Gill #include "port/base_port.hpp"
5e92aba45SJagpal Singh Gill
6e92aba45SJagpal Singh Gill #include <fcntl.h>
7e92aba45SJagpal Singh Gill
8e92aba45SJagpal Singh Gill #include <xyz/openbmc_project/Sensor/Value/client.hpp>
9e92aba45SJagpal Singh Gill
10e92aba45SJagpal Singh Gill #include <cmath>
11e92aba45SJagpal Singh Gill #include <string>
12e92aba45SJagpal Singh Gill
13e92aba45SJagpal Singh Gill #include <gtest/gtest.h>
14e92aba45SJagpal Singh Gill
15e92aba45SJagpal Singh Gill using namespace std::literals;
16e92aba45SJagpal Singh Gill using namespace testing;
17e92aba45SJagpal Singh Gill using SensorValueIntf =
18e92aba45SJagpal Singh Gill sdbusplus::client::xyz::openbmc_project::sensor::Value<>;
19e92aba45SJagpal Singh Gill
20e92aba45SJagpal Singh Gill namespace TestIntf = phosphor::modbus::test;
21e92aba45SJagpal Singh Gill namespace ModbusIntf = phosphor::modbus::rtu;
22e92aba45SJagpal Singh Gill namespace PortIntf = phosphor::modbus::rtu::port;
23e92aba45SJagpal Singh Gill namespace PortConfigIntf = PortIntf::config;
24e92aba45SJagpal Singh Gill namespace DeviceIntf = phosphor::modbus::rtu::device;
25e92aba45SJagpal Singh Gill namespace DeviceConfigIntf = DeviceIntf::config;
26*7184805aSJagpal Singh Gill namespace EventIntf = phosphor::modbus::events;
27e92aba45SJagpal Singh Gill
28e92aba45SJagpal Singh Gill class MockPort : public PortIntf::BasePort
29e92aba45SJagpal Singh Gill {
30e92aba45SJagpal Singh Gill public:
MockPort(sdbusplus::async::context & ctx,const PortConfigIntf::Config & config,const std::string & devicePath)31e92aba45SJagpal Singh Gill MockPort(sdbusplus::async::context& ctx,
32e92aba45SJagpal Singh Gill const PortConfigIntf::Config& config,
33e92aba45SJagpal Singh Gill const std::string& devicePath) : BasePort(ctx, config, devicePath)
34e92aba45SJagpal Singh Gill {}
35e92aba45SJagpal Singh Gill };
36e92aba45SJagpal Singh Gill
37e92aba45SJagpal Singh Gill class SensorsTest : public ::testing::Test
38e92aba45SJagpal Singh Gill {
39e92aba45SJagpal Singh Gill public:
40e92aba45SJagpal Singh Gill PortConfigIntf::Config portConfig;
41e92aba45SJagpal Singh Gill static constexpr const char* clientDevicePath = "/tmp/ttySensorsTestPort0";
42e92aba45SJagpal Singh Gill static constexpr const char* serverDevicePath = "/tmp/ttySensorsTestPort1";
43e92aba45SJagpal Singh Gill static constexpr auto portName = "TestPort0";
44e92aba45SJagpal Singh Gill static constexpr auto baudRate = 115200;
45e92aba45SJagpal Singh Gill static constexpr const auto strBaudeRate = "b115200";
46e92aba45SJagpal Singh Gill std::string deviceName;
47e92aba45SJagpal Singh Gill std::string fullSensorName;
48e92aba45SJagpal Singh Gill std::string objectPath;
49e92aba45SJagpal Singh Gill static constexpr auto serviceName =
50e92aba45SJagpal Singh Gill "xyz.openbmc_project.TestModbusRTUSensors";
51e92aba45SJagpal Singh Gill static constexpr auto sensorName = "OutletTemperature";
52e92aba45SJagpal Singh Gill int socat_pid = -1;
53e92aba45SJagpal Singh Gill sdbusplus::async::context ctx;
54e92aba45SJagpal Singh Gill int fdClient = -1;
55e92aba45SJagpal Singh Gill std::unique_ptr<TestIntf::ServerTester> serverTester;
56e92aba45SJagpal Singh Gill int fdServer = -1;
57e92aba45SJagpal Singh Gill
SensorsTest()58e92aba45SJagpal Singh Gill SensorsTest()
59e92aba45SJagpal Singh Gill {
60e92aba45SJagpal Singh Gill portConfig.name = portName;
61e92aba45SJagpal Singh Gill portConfig.portMode = PortConfigIntf::PortMode::rs485;
62e92aba45SJagpal Singh Gill portConfig.baudRate = baudRate;
63e92aba45SJagpal Singh Gill portConfig.rtsDelay = 1;
64e92aba45SJagpal Singh Gill
65e92aba45SJagpal Singh Gill deviceName = std::format("ResorviorPumpUnit_{}_{}",
66e92aba45SJagpal Singh Gill TestIntf::testDeviceAddress, portName);
67e92aba45SJagpal Singh Gill
68e92aba45SJagpal Singh Gill fullSensorName = std::format("{}_{}", deviceName, sensorName);
69e92aba45SJagpal Singh Gill
70e92aba45SJagpal Singh Gill objectPath = std::format(
71e92aba45SJagpal Singh Gill "{}/{}/{}", SensorValueIntf::namespace_path::value,
72e92aba45SJagpal Singh Gill SensorValueIntf::namespace_path::temperature, fullSensorName);
73e92aba45SJagpal Singh Gill
74e92aba45SJagpal Singh Gill std::string socatCmd = std::format(
75e92aba45SJagpal Singh Gill "socat -x -v -d -d pty,link={},rawer,echo=0,parenb,{} pty,link={},rawer,echo=0,parenb,{} & echo $!",
76e92aba45SJagpal Singh Gill serverDevicePath, strBaudeRate, clientDevicePath, strBaudeRate);
77e92aba45SJagpal Singh Gill
78e92aba45SJagpal Singh Gill // Start socat in the background and capture its PID
79e92aba45SJagpal Singh Gill FILE* fp = popen(socatCmd.c_str(), "r");
80e92aba45SJagpal Singh Gill EXPECT_NE(fp, nullptr) << "Failed to start socat: " << strerror(errno);
81e92aba45SJagpal Singh Gill EXPECT_GT(fscanf(fp, "%d", &socat_pid), 0);
82e92aba45SJagpal Singh Gill pclose(fp);
83e92aba45SJagpal Singh Gill
84e92aba45SJagpal Singh Gill // Wait for socat to start up
85e92aba45SJagpal Singh Gill sleep(1);
86e92aba45SJagpal Singh Gill
87e92aba45SJagpal Singh Gill fdClient = open(clientDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
88e92aba45SJagpal Singh Gill EXPECT_NE(fdClient, -1)
89e92aba45SJagpal Singh Gill << "Failed to open serial port " << clientDevicePath
90e92aba45SJagpal Singh Gill << " with error: " << strerror(errno);
91e92aba45SJagpal Singh Gill
92e92aba45SJagpal Singh Gill fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
93e92aba45SJagpal Singh Gill EXPECT_NE(fdServer, -1)
94e92aba45SJagpal Singh Gill << "Failed to open serial port " << serverDevicePath
95e92aba45SJagpal Singh Gill << " with error: " << strerror(errno);
96e92aba45SJagpal Singh Gill
97e92aba45SJagpal Singh Gill ctx.request_name(serviceName);
98e92aba45SJagpal Singh Gill
99e92aba45SJagpal Singh Gill serverTester = std::make_unique<TestIntf::ServerTester>(ctx, fdServer);
100e92aba45SJagpal Singh Gill }
101e92aba45SJagpal Singh Gill
~SensorsTest()102e92aba45SJagpal Singh Gill ~SensorsTest() noexcept override
103e92aba45SJagpal Singh Gill {
104e92aba45SJagpal Singh Gill if (fdClient != -1)
105e92aba45SJagpal Singh Gill {
106e92aba45SJagpal Singh Gill close(fdClient);
107e92aba45SJagpal Singh Gill fdClient = -1;
108e92aba45SJagpal Singh Gill }
109e92aba45SJagpal Singh Gill if (fdServer != -1)
110e92aba45SJagpal Singh Gill {
111e92aba45SJagpal Singh Gill close(fdServer);
112e92aba45SJagpal Singh Gill fdServer = -1;
113e92aba45SJagpal Singh Gill }
114e92aba45SJagpal Singh Gill kill(socat_pid, SIGTERM);
115e92aba45SJagpal Singh Gill }
116e92aba45SJagpal Singh Gill
testSensorCreation(std::string objectPath,DeviceConfigIntf::SensorRegister sensorRegister,double expectedValue)117e92aba45SJagpal Singh Gill auto testSensorCreation(std::string objectPath,
118e92aba45SJagpal Singh Gill DeviceConfigIntf::SensorRegister sensorRegister,
119e92aba45SJagpal Singh Gill double expectedValue)
120e92aba45SJagpal Singh Gill -> sdbusplus::async::task<void>
121e92aba45SJagpal Singh Gill {
122e92aba45SJagpal Singh Gill DeviceConfigIntf::DeviceFactoryConfig deviceFactoryConfig = {
123e92aba45SJagpal Singh Gill {
124e92aba45SJagpal Singh Gill .address = TestIntf::testDeviceAddress,
125e92aba45SJagpal Singh Gill .parity = ModbusIntf::Parity::none,
126e92aba45SJagpal Singh Gill .baudRate = baudRate,
127e92aba45SJagpal Singh Gill .name = deviceName,
128e92aba45SJagpal Singh Gill .portName = portConfig.name,
129e92aba45SJagpal Singh Gill .inventoryPath = sdbusplus::message::object_path(
130e92aba45SJagpal Singh Gill "xyz/openbmc_project/Inventory/ResorviorPumpUnit"),
131e92aba45SJagpal Singh Gill .sensorRegisters = {sensorRegister},
132e92aba45SJagpal Singh Gill .statusRegisters = {},
133e92aba45SJagpal Singh Gill .firmwareRegisters = {},
134e92aba45SJagpal Singh Gill },
135e92aba45SJagpal Singh Gill DeviceConfigIntf::DeviceType::reservoirPumpUnit,
136e92aba45SJagpal Singh Gill DeviceConfigIntf::DeviceModel::RDF040DSS5193E0,
137e92aba45SJagpal Singh Gill };
138e92aba45SJagpal Singh Gill
139*7184805aSJagpal Singh Gill EventIntf::Events events{ctx};
140*7184805aSJagpal Singh Gill
141*7184805aSJagpal Singh Gill MockPort mockPort(ctx, portConfig, clientDevicePath);
142e92aba45SJagpal Singh Gill
143e92aba45SJagpal Singh Gill auto device = DeviceIntf::DeviceFactory::create(
144*7184805aSJagpal Singh Gill ctx, deviceFactoryConfig, mockPort, events);
145e92aba45SJagpal Singh Gill
146e92aba45SJagpal Singh Gill co_await device->readSensorRegisters();
147e92aba45SJagpal Singh Gill
148e92aba45SJagpal Singh Gill auto properties = co_await SensorValueIntf(ctx)
149e92aba45SJagpal Singh Gill .service(serviceName)
150e92aba45SJagpal Singh Gill .path(objectPath)
151e92aba45SJagpal Singh Gill .properties();
152e92aba45SJagpal Singh Gill
153e92aba45SJagpal Singh Gill EXPECT_EQ(properties.value, expectedValue) << "Sensor value mismatch";
154e92aba45SJagpal Singh Gill EXPECT_EQ(properties.unit, sensorRegister.unit)
155e92aba45SJagpal Singh Gill << "Sensor unit mismatch";
156e92aba45SJagpal Singh Gill EXPECT_TRUE(std::isnan(properties.min_value)) << "Min value mismatch";
157e92aba45SJagpal Singh Gill EXPECT_TRUE(std::isnan(properties.max_value)) << "Max value mismatch";
158e92aba45SJagpal Singh Gill
159e92aba45SJagpal Singh Gill co_return;
160e92aba45SJagpal Singh Gill }
161e92aba45SJagpal Singh Gill
SetUp()162e92aba45SJagpal Singh Gill void SetUp() override
163e92aba45SJagpal Singh Gill {
164e92aba45SJagpal Singh Gill // Process request for sensor poll
165e92aba45SJagpal Singh Gill ctx.spawn(serverTester->processRequests());
166e92aba45SJagpal Singh Gill }
167e92aba45SJagpal Singh Gill };
168e92aba45SJagpal Singh Gill
TEST_F(SensorsTest,TestSensorValueUnsigned)169e92aba45SJagpal Singh Gill TEST_F(SensorsTest, TestSensorValueUnsigned)
170e92aba45SJagpal Singh Gill {
171e92aba45SJagpal Singh Gill const DeviceConfigIntf::SensorRegister sensorRegister = {
172e92aba45SJagpal Singh Gill .name = sensorName,
173e92aba45SJagpal Singh Gill .pathSuffix = SensorValueIntf::namespace_path::temperature,
174e92aba45SJagpal Singh Gill .unit = SensorValueIntf::Unit::DegreesC,
175e92aba45SJagpal Singh Gill .offset = TestIntf::testReadHoldingRegisterTempUnsignedOffset,
176e92aba45SJagpal Singh Gill .size = TestIntf::testReadHoldingRegisterTempCount,
177e92aba45SJagpal Singh Gill .format = DeviceConfigIntf::SensorFormat::floatingPoint,
178e92aba45SJagpal Singh Gill };
179e92aba45SJagpal Singh Gill
180e92aba45SJagpal Singh Gill ctx.spawn(
181e92aba45SJagpal Singh Gill testSensorCreation(objectPath, sensorRegister,
182e92aba45SJagpal Singh Gill TestIntf::testReadHoldingRegisterTempUnsigned[0]));
183e92aba45SJagpal Singh Gill
184e92aba45SJagpal Singh Gill ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
185e92aba45SJagpal Singh Gill sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
186e92aba45SJagpal Singh Gill
187e92aba45SJagpal Singh Gill ctx.run();
188e92aba45SJagpal Singh Gill }
189e92aba45SJagpal Singh Gill
TEST_F(SensorsTest,TestSensorValueSigned)190e92aba45SJagpal Singh Gill TEST_F(SensorsTest, TestSensorValueSigned)
191e92aba45SJagpal Singh Gill {
192e92aba45SJagpal Singh Gill const DeviceConfigIntf::SensorRegister sensorRegister = {
193e92aba45SJagpal Singh Gill .name = sensorName,
194e92aba45SJagpal Singh Gill .pathSuffix = SensorValueIntf::namespace_path::temperature,
195e92aba45SJagpal Singh Gill .unit = SensorValueIntf::Unit::DegreesC,
196e92aba45SJagpal Singh Gill .offset = TestIntf::testReadHoldingRegisterTempSignedOffset,
197e92aba45SJagpal Singh Gill .size = TestIntf::testReadHoldingRegisterTempCount,
198e92aba45SJagpal Singh Gill .isSigned = true,
199e92aba45SJagpal Singh Gill .format = DeviceConfigIntf::SensorFormat::floatingPoint,
200e92aba45SJagpal Singh Gill };
201e92aba45SJagpal Singh Gill
202e92aba45SJagpal Singh Gill // Convert expected hex value to a signed 16-bit integer for comparison
203e92aba45SJagpal Singh Gill const int16_t expectedSigned =
204e92aba45SJagpal Singh Gill static_cast<int16_t>(TestIntf::testReadHoldingRegisterTempSigned[0]);
205e92aba45SJagpal Singh Gill
206e92aba45SJagpal Singh Gill ctx.spawn(testSensorCreation(objectPath, sensorRegister, expectedSigned));
207e92aba45SJagpal Singh Gill
208e92aba45SJagpal Singh Gill ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
209e92aba45SJagpal Singh Gill sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
210e92aba45SJagpal Singh Gill
211e92aba45SJagpal Singh Gill ctx.run();
212e92aba45SJagpal Singh Gill }
213e92aba45SJagpal Singh Gill
applyValueSettings(double value,double shift,double scale,uint8_t precision)214e92aba45SJagpal Singh Gill static auto applyValueSettings(double value, double shift, double scale,
215e92aba45SJagpal Singh Gill uint8_t precision)
216e92aba45SJagpal Singh Gill {
217e92aba45SJagpal Singh Gill return (shift + (scale * (value / (1ULL << precision))));
218e92aba45SJagpal Singh Gill }
219e92aba45SJagpal Singh Gill
TEST_F(SensorsTest,TestSensorValueWithSettings)220e92aba45SJagpal Singh Gill TEST_F(SensorsTest, TestSensorValueWithSettings)
221e92aba45SJagpal Singh Gill {
222e92aba45SJagpal Singh Gill const DeviceConfigIntf::SensorRegister sensorRegister = {
223e92aba45SJagpal Singh Gill .name = sensorName,
224e92aba45SJagpal Singh Gill .pathSuffix = SensorValueIntf::namespace_path::temperature,
225e92aba45SJagpal Singh Gill .unit = SensorValueIntf::Unit::DegreesC,
226e92aba45SJagpal Singh Gill .offset = TestIntf::testReadHoldingRegisterTempUnsignedOffset,
227e92aba45SJagpal Singh Gill .size = TestIntf::testReadHoldingRegisterTempCount,
228e92aba45SJagpal Singh Gill .precision = 2,
229e92aba45SJagpal Singh Gill .scale = 0.1,
230e92aba45SJagpal Singh Gill .shift = 50,
231e92aba45SJagpal Singh Gill .format = DeviceConfigIntf::SensorFormat::floatingPoint,
232e92aba45SJagpal Singh Gill };
233e92aba45SJagpal Singh Gill
234e92aba45SJagpal Singh Gill ctx.spawn(testSensorCreation(
235e92aba45SJagpal Singh Gill objectPath, sensorRegister,
236e92aba45SJagpal Singh Gill applyValueSettings(TestIntf::testReadHoldingRegisterTempUnsigned[0],
237e92aba45SJagpal Singh Gill sensorRegister.shift, sensorRegister.scale,
238e92aba45SJagpal Singh Gill sensorRegister.precision)));
239e92aba45SJagpal Singh Gill
240e92aba45SJagpal Singh Gill ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
241e92aba45SJagpal Singh Gill sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
242e92aba45SJagpal Singh Gill
243e92aba45SJagpal Singh Gill ctx.run();
244e92aba45SJagpal Singh Gill }
245