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