xref: /openbmc/phosphor-modbus/rtu/device/base_device.cpp (revision e92aba4516471f5a01d4ab1f93eb9919ec05c21f)
1 #include "base_device.hpp"
2 
3 #include <phosphor-logging/lg2.hpp>
4 
5 #include <numeric>
6 
7 namespace phosphor::modbus::rtu::device
8 {
9 
10 PHOSPHOR_LOG2_USING;
11 
12 BaseDevice::BaseDevice(sdbusplus::async::context& ctx,
13                        const config::Config& config, PortIntf& serialPort) :
14     ctx(ctx), config(config), serialPort(serialPort)
15 {
16     createSensors();
17 
18     info("Successfully created device {NAME}", "NAME", config.name);
19 }
20 
21 static auto getObjectPath(const std::string& sensorType,
22                           const std::string& sensorName)
23     -> sdbusplus::message::object_path
24 {
25     return sdbusplus::message::object_path(
26         std::string(SensorValueIntf::namespace_path::value) + "/" + sensorType +
27         "/" + sensorName);
28 }
29 
30 auto BaseDevice::createSensors() -> void
31 {
32     for (const auto& sensorRegister : config.sensorRegisters)
33     {
34         SensorValueIntf::properties_t initProperties = {
35             std::numeric_limits<double>::quiet_NaN(),
36             std::numeric_limits<double>::quiet_NaN(),
37             std::numeric_limits<double>::quiet_NaN(), sensorRegister.unit};
38 
39         auto sensorPath = getObjectPath(
40             sensorRegister.pathSuffix, config.name + "_" + sensorRegister.name);
41 
42         auto sensor = std::make_unique<SensorValueIntf>(
43             ctx, sensorPath.str.c_str(), initProperties);
44 
45         sensor->emit_added();
46 
47         sensors.emplace(sensorRegister.name, std::move(sensor));
48     }
49 
50     return;
51 }
52 
53 static auto getRawIntegerFromRegister(const std::vector<uint16_t>& reg,
54                                       bool sign) -> int64_t
55 {
56     if (reg.empty())
57     {
58         return 0;
59     }
60 
61     uint64_t accumulator = 0;
62     for (auto val : reg)
63     {
64         accumulator = (accumulator << 16) | val;
65     }
66 
67     int64_t result = 0;
68 
69     if (sign)
70     {
71         if (reg.size() == 1)
72         {
73             result = static_cast<int16_t>(accumulator);
74         }
75         else if (reg.size() == 2)
76         {
77             result = static_cast<int32_t>(accumulator);
78         }
79         else
80         {
81             result = static_cast<int64_t>(accumulator);
82         }
83     }
84     else
85     {
86         if (reg.size() == 1)
87         {
88             result = static_cast<uint16_t>(accumulator);
89         }
90         else if (reg.size() == 2)
91         {
92             result = static_cast<uint32_t>(accumulator);
93         }
94         else
95         {
96             result = static_cast<int64_t>(accumulator);
97         }
98     }
99 
100     return result;
101 }
102 
103 auto BaseDevice::readSensorRegisters() -> sdbusplus::async::task<void>
104 {
105     while (!ctx.stop_requested())
106     {
107         for (const auto& sensorRegister : config.sensorRegisters)
108         {
109             auto sensor = sensors.find(sensorRegister.name);
110             if (sensor == sensors.end())
111             {
112                 error("Sensor not found for {NAME}", "NAME",
113                       sensorRegister.name);
114                 continue;
115             }
116 
117             if (sensorRegister.size > 4)
118             {
119                 error("Unsupported size for register {NAME}", "NAME",
120                       sensorRegister.name);
121                 continue;
122             }
123 
124             auto registers = std::vector<uint16_t>(sensorRegister.size);
125             auto ret = co_await serialPort.readHoldingRegisters(
126                 config.address, sensorRegister.offset, config.baudRate,
127                 config.parity, registers);
128             if (!ret)
129             {
130                 error(
131                     "Failed to read holding registers {NAME} for {DEVICE_ADDRESS}",
132                     "NAME", sensorRegister.name, "DEVICE_ADDRESS",
133                     config.address);
134                 continue;
135             }
136 
137             double regVal = static_cast<double>(
138                 getRawIntegerFromRegister(registers, sensorRegister.isSigned));
139             if (sensorRegister.format == config::SensorFormat::floatingPoint)
140             {
141                 regVal = sensorRegister.shift +
142                          (sensorRegister.scale *
143                           (regVal / (1ULL << sensorRegister.precision)));
144             }
145 
146             sensor->second->value(regVal);
147         }
148 
149         constexpr auto pollInterval = 3;
150         co_await sdbusplus::async::sleep_for(
151             ctx, std::chrono::seconds(pollInterval));
152         debug("Polling sensors for {NAME} in {INTERVAL} seconds", "NAME",
153               config.name, "INTERVAL", pollInterval);
154     }
155 
156     co_return;
157 }
158 
159 } // namespace phosphor::modbus::rtu::device
160