xref: /openbmc/phosphor-modbus/tests/test_sensors.cpp (revision 7184805ae4ede906935133e3e0f8ee2468bc781b)
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