blob: d9a0567b511271fc15b82881df9ea94309f0eaa4 [file] [log] [blame]
Jagpal Singh Gill71848052025-10-16 00:28:58 -07001#include "common/events.hpp"
Jagpal Singh Gille92aba42025-10-16 00:00:13 -07002#include "device/device_factory.hpp"
Jagpal Singh Gille92aba42025-10-16 00:00:13 -07003#include "port/base_port.hpp"
Jagpal Singh Gill2fa10f42025-12-08 10:35:14 -08004#include "test_base.hpp"
Jagpal Singh Gille92aba42025-10-16 00:00:13 -07005
Jagpal Singh Gill1f1d0042025-11-17 13:15:33 -08006#include <xyz/openbmc_project/Association/Definitions/client.hpp>
Jagpal Singh Gille92aba42025-10-16 00:00:13 -07007#include <xyz/openbmc_project/Sensor/Value/client.hpp>
Jagpal Singh Gill1f1d0042025-11-17 13:15:33 -08008#include <xyz/openbmc_project/State/Decorator/Availability/client.hpp>
9#include <xyz/openbmc_project/State/Decorator/OperationalStatus/client.hpp>
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070010
11#include <cmath>
12#include <string>
13
14#include <gtest/gtest.h>
15
16using namespace std::literals;
17using namespace testing;
18using SensorValueIntf =
19 sdbusplus::client::xyz::openbmc_project::sensor::Value<>;
Jagpal Singh Gill1f1d0042025-11-17 13:15:33 -080020using OperationalStatusIntf = sdbusplus::client::xyz::openbmc_project::state::
21 decorator::OperationalStatus<>;
22using AvailabilityIntf =
23 sdbusplus::client::xyz::openbmc_project::state::decorator::Availability<>;
24using AssociationIntf =
25 sdbusplus::client::xyz::openbmc_project::association::Definitions<>;
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070026
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070027namespace ModbusIntf = phosphor::modbus::rtu;
28namespace PortIntf = phosphor::modbus::rtu::port;
29namespace PortConfigIntf = PortIntf::config;
30namespace DeviceIntf = phosphor::modbus::rtu::device;
31namespace DeviceConfigIntf = DeviceIntf::config;
Jagpal Singh Gill71848052025-10-16 00:28:58 -070032namespace EventIntf = phosphor::modbus::events;
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070033
34class MockPort : public PortIntf::BasePort
35{
36 public:
37 MockPort(sdbusplus::async::context& ctx,
38 const PortConfigIntf::Config& config,
39 const std::string& devicePath) : BasePort(ctx, config, devicePath)
40 {}
41};
42
Jagpal Singh Gill2fa10f42025-12-08 10:35:14 -080043class SensorsTest : public BaseTest
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070044{
45 public:
Jagpal Singh Gill2fa10f42025-12-08 10:35:14 -080046 static constexpr auto clientDevicePath = "/tmp/ttySensorsTestPort0";
47 static constexpr auto serverDevicePath = "/tmp/ttySensorsTestPort1";
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070048 static constexpr auto portName = "TestPort0";
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070049 static constexpr auto serviceName =
50 "xyz.openbmc_project.TestModbusRTUSensors";
51 static constexpr auto sensorName = "OutletTemperature";
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070052
Jagpal Singh Gill2fa10f42025-12-08 10:35:14 -080053 PortConfigIntf::Config portConfig;
54 std::string deviceName;
55 std::string fullSensorName;
56 std::string objectPath;
57
58 SensorsTest() : BaseTest(clientDevicePath, serverDevicePath, serviceName)
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070059 {
60 portConfig.name = portName;
61 portConfig.portMode = PortConfigIntf::PortMode::rs485;
62 portConfig.baudRate = baudRate;
63 portConfig.rtsDelay = 1;
64
65 deviceName = std::format("ResorviorPumpUnit_{}_{}",
66 TestIntf::testDeviceAddress, portName);
67
68 fullSensorName = std::format("{}_{}", deviceName, sensorName);
69
70 objectPath = std::format(
71 "{}/{}/{}", SensorValueIntf::namespace_path::value,
72 SensorValueIntf::namespace_path::temperature, fullSensorName);
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070073 }
74
Jagpal Singh Gill1f1d0042025-11-17 13:15:33 -080075 auto checkInventoryAssociations() -> sdbusplus::async::task<void>
76 {
77 constexpr auto numOfInventoryAssociations = 2;
78 auto associationProperties =
79 co_await AssociationIntf(ctx)
80 .service(serviceName)
81 .path(objectPath)
82 .properties();
83 EXPECT_EQ(associationProperties.associations.size(),
84 numOfInventoryAssociations);
85 }
86
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070087 auto testSensorCreation(std::string objectPath,
88 DeviceConfigIntf::SensorRegister sensorRegister,
89 double expectedValue)
90 -> sdbusplus::async::task<void>
91 {
92 DeviceConfigIntf::DeviceFactoryConfig deviceFactoryConfig = {
93 {
94 .address = TestIntf::testDeviceAddress,
95 .parity = ModbusIntf::Parity::none,
96 .baudRate = baudRate,
97 .name = deviceName,
98 .portName = portConfig.name,
99 .inventoryPath = sdbusplus::message::object_path(
100 "xyz/openbmc_project/Inventory/ResorviorPumpUnit"),
101 .sensorRegisters = {sensorRegister},
102 .statusRegisters = {},
103 .firmwareRegisters = {},
104 },
105 DeviceConfigIntf::DeviceType::reservoirPumpUnit,
106 DeviceConfigIntf::DeviceModel::RDF040DSS5193E0,
107 };
Jagpal Singh Gill71848052025-10-16 00:28:58 -0700108 EventIntf::Events events{ctx};
Jagpal Singh Gill71848052025-10-16 00:28:58 -0700109 MockPort mockPort(ctx, portConfig, clientDevicePath);
Jagpal Singh Gille92aba42025-10-16 00:00:13 -0700110 auto device = DeviceIntf::DeviceFactory::create(
Jagpal Singh Gill71848052025-10-16 00:28:58 -0700111 ctx, deviceFactoryConfig, mockPort, events);
Jagpal Singh Gille92aba42025-10-16 00:00:13 -0700112
113 co_await device->readSensorRegisters();
114
115 auto properties = co_await SensorValueIntf(ctx)
116 .service(serviceName)
117 .path(objectPath)
118 .properties();
Jagpal Singh Gille92aba42025-10-16 00:00:13 -0700119 EXPECT_EQ(properties.value, expectedValue) << "Sensor value mismatch";
120 EXPECT_EQ(properties.unit, sensorRegister.unit)
121 << "Sensor unit mismatch";
122 EXPECT_TRUE(std::isnan(properties.min_value)) << "Min value mismatch";
123 EXPECT_TRUE(std::isnan(properties.max_value)) << "Max value mismatch";
124
Jagpal Singh Gill1f1d0042025-11-17 13:15:33 -0800125 auto operationalProperties =
126 co_await OperationalStatusIntf(ctx)
127 .service(serviceName)
128 .path(objectPath)
129 .properties();
130 EXPECT_EQ(operationalProperties.functional, true)
131 << "Operational status mismatch";
132
133 auto availabilityProperties =
134 co_await AvailabilityIntf(ctx)
135 .service(serviceName)
136 .path(objectPath)
137 .properties();
138 EXPECT_EQ(availabilityProperties.available, true)
139 << "Availability mismatch";
140
141 co_await checkInventoryAssociations();
142
Jagpal Singh Gille92aba42025-10-16 00:00:13 -0700143 co_return;
144 }
Jagpal Singh Gille92aba42025-10-16 00:00:13 -0700145};
146
147TEST_F(SensorsTest, TestSensorValueUnsigned)
148{
149 const DeviceConfigIntf::SensorRegister sensorRegister = {
150 .name = sensorName,
151 .pathSuffix = SensorValueIntf::namespace_path::temperature,
152 .unit = SensorValueIntf::Unit::DegreesC,
153 .offset = TestIntf::testReadHoldingRegisterTempUnsignedOffset,
154 .size = TestIntf::testReadHoldingRegisterTempCount,
155 .format = DeviceConfigIntf::SensorFormat::floatingPoint,
156 };
157
158 ctx.spawn(
159 testSensorCreation(objectPath, sensorRegister,
160 TestIntf::testReadHoldingRegisterTempUnsigned[0]));
161
162 ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
163 sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
164
165 ctx.run();
166}
167
168TEST_F(SensorsTest, TestSensorValueSigned)
169{
170 const DeviceConfigIntf::SensorRegister sensorRegister = {
171 .name = sensorName,
172 .pathSuffix = SensorValueIntf::namespace_path::temperature,
173 .unit = SensorValueIntf::Unit::DegreesC,
174 .offset = TestIntf::testReadHoldingRegisterTempSignedOffset,
175 .size = TestIntf::testReadHoldingRegisterTempCount,
176 .isSigned = true,
177 .format = DeviceConfigIntf::SensorFormat::floatingPoint,
178 };
179
180 // Convert expected hex value to a signed 16-bit integer for comparison
181 const int16_t expectedSigned =
182 static_cast<int16_t>(TestIntf::testReadHoldingRegisterTempSigned[0]);
183
184 ctx.spawn(testSensorCreation(objectPath, sensorRegister, expectedSigned));
185
186 ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
187 sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
188
189 ctx.run();
190}
191
192static auto applyValueSettings(double value, double shift, double scale,
193 uint8_t precision)
194{
195 return (shift + (scale * (value / (1ULL << precision))));
196}
197
198TEST_F(SensorsTest, TestSensorValueWithSettings)
199{
200 const DeviceConfigIntf::SensorRegister sensorRegister = {
201 .name = sensorName,
202 .pathSuffix = SensorValueIntf::namespace_path::temperature,
203 .unit = SensorValueIntf::Unit::DegreesC,
204 .offset = TestIntf::testReadHoldingRegisterTempUnsignedOffset,
205 .size = TestIntf::testReadHoldingRegisterTempCount,
206 .precision = 2,
207 .scale = 0.1,
208 .shift = 50,
209 .format = DeviceConfigIntf::SensorFormat::floatingPoint,
210 };
211
212 ctx.spawn(testSensorCreation(
213 objectPath, sensorRegister,
214 applyValueSettings(TestIntf::testReadHoldingRegisterTempUnsigned[0],
215 sensorRegister.shift, sensorRegister.scale,
216 sensorRegister.precision)));
217
218 ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
219 sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
220
221 ctx.run();
222}