xref: /openbmc/phosphor-modbus/mocked_test_device/mock_modbus_server.cpp (revision 2168bbd050af3c68d820f96054a939dda02688c6)
1 #include "modbus/modbus.hpp"
2 #include "modbus/modbus_commands.hpp"
3 #include "modbus/modbus_message.hpp"
4 
5 #include <fcntl.h>
6 
7 #include <sdbusplus/async.hpp>
8 
9 #include <iostream>
10 #include <string>
11 
12 namespace phosphor::modbus::test
13 {
14 
15 class MessageIntf : public phosphor::modbus::rtu::Message
16 {
17     friend class TestServer;
18 };
19 
20 class TestServer
21 {
22   public:
23     explicit TestServer(sdbusplus::async::context& ctx, int fd);
24 
25   private:
26     auto processRequests() -> sdbusplus::async::task<void>;
27     void processMessage(MessageIntf& request, size_t requestSize,
28                         MessageIntf& response);
29 
30     void processReadHoldingRegisters(MessageIntf& request, size_t requestSize,
31                                      MessageIntf& response);
32 
33     sdbusplus::async::context& ctx;
34     int fd;
35     sdbusplus::async::fdio fdioInstance;
36 };
37 
TestServer(sdbusplus::async::context & ctx,int fd)38 TestServer::TestServer(sdbusplus::async::context& ctx, int fd) :
39     ctx(ctx), fd(fd), fdioInstance(ctx, fd)
40 {
41     ctx.spawn(processRequests());
42 }
43 
processRequests()44 auto TestServer::processRequests() -> sdbusplus::async::task<void>
45 {
46     while (!ctx.stop_requested())
47     {
48         MessageIntf request;
49         co_await fdioInstance.next();
50         auto ret = read(fd, request.raw.data(), request.raw.size());
51         // Request message need to be at least 4 bytes long - address(1),
52         // function code(1), ..., CRC(2)
53         if (ret < 4)
54         {
55             std::cerr << "Invalid Server message size:" << ret << ", drop it"
56                       << std::endl;
57             continue;
58         }
59 
60         MessageIntf response;
61         processMessage(request, ret, response);
62 
63         ret = write(fd, response.raw.data(), response.len);
64         if (ret < 0)
65         {
66             std::cerr << "Failed to send response" << std::endl;
67         }
68     }
69 }
70 
processMessage(MessageIntf & request,size_t requestSize,MessageIntf & response)71 void TestServer::processMessage(MessageIntf& request, size_t requestSize,
72                                 MessageIntf& response)
73 {
74     constexpr uint8_t readHoldingRegistersFunctionCode = 0x3;
75 
76     switch (request.functionCode)
77     {
78         case readHoldingRegistersFunctionCode:
79             processReadHoldingRegisters(request, requestSize, response);
80             break;
81         default:
82             std::cerr << "Server received unknown request" << std::endl;
83             break;
84     }
85 }
86 
processReadHoldingRegisters(MessageIntf & request,size_t requestSize,MessageIntf & response)87 void TestServer::processReadHoldingRegisters(
88     MessageIntf& request, size_t requestSize, MessageIntf& response)
89 {
90     uint16_t registerOffset = request.raw[2] << 8 | request.raw[3];
91     uint16_t registerCount = request.raw[4] << 8 | request.raw[5];
92 
93     std::cout << "Received readHoldingRegisters request with size:"
94               << requestSize << ", registerOffset:" << registerOffset
95               << ", registerCount:" << registerCount << std::endl;
96 
97     response << request.raw[0] << request.raw[1] << uint8_t(2 * registerCount);
98     for (int i = 0; i < registerCount; i++)
99     {
100         constexpr uint16_t readHoldingRegisterResponse = 0x4142;
101         response << uint16_t(readHoldingRegisterResponse);
102     }
103     response.appendCRC();
104 }
105 
106 } // namespace phosphor::modbus::test
107 
main(int argc,char * argv[])108 int main(int argc, char* argv[])
109 {
110     if (argc < 2)
111     {
112         std::cerr << "Usage: " << argv[0] << " <integer_value>" << std::endl;
113         return 1;
114     }
115 
116     int interfaceValue = 1;
117     try
118     {
119         interfaceValue = std::stoi(argv[1]);
120     }
121     catch (const std::invalid_argument& e)
122     {
123         std::cerr << "Invalid argument: " << e.what() << std::endl;
124         return 1;
125     }
126     catch (const std::out_of_range& e)
127     {
128         std::cerr << "Argument out of range: " << e.what() << std::endl;
129         return 1;
130     }
131 
132     using TestServerIntf = phosphor::modbus::test::TestServer;
133     std::string devicePathStr =
134         std::string("/dev/ttyV") + std::to_string(interfaceValue);
135     const char* serverDevicePath = devicePathStr.c_str();
136     std::cout << "Starting at device path" << serverDevicePath << std::endl;
137     constexpr auto path = "/xyz/openbmc_project";
138     constexpr auto serviceName = "xyz.openbmc_project.ModbusRTUTestServer";
139     sdbusplus::async::context ctx;
140 
141     auto fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
142     if (fdServer == -1)
143     {
144         std::cerr << "Failed to open serial port " << serverDevicePath
145                   << " with error: " << strerror(errno) << std::endl;
146         return 1;
147     }
148 
149     std::cout << "Creating Modbus RTU test server at " << path << std::endl;
150     TestServerIntf server{ctx, fdServer};
151 
152     ctx.request_name(serviceName);
153 
154     ctx.run();
155     return 0;
156 }
157