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