blob: e89ef5acc27a841fae5cd8ae9c917879cc1e1bba [file] [log] [blame]
#include "modbus/modbus.hpp"
#include "modbus/modbus_commands.hpp"
#include "modbus/modbus_message.hpp"
#include <fcntl.h>
#include <sdbusplus/async.hpp>
#include <iostream>
#include <string>
namespace phosphor::modbus::test
{
class MessageIntf : public phosphor::modbus::rtu::Message
{
friend class TestServer;
};
class TestServer
{
public:
explicit TestServer(sdbusplus::async::context& ctx, int fd);
private:
auto processRequests() -> sdbusplus::async::task<void>;
void processMessage(MessageIntf& request, size_t requestSize,
MessageIntf& response);
void processReadHoldingRegisters(MessageIntf& request, size_t requestSize,
MessageIntf& response);
sdbusplus::async::context& ctx;
int fd;
sdbusplus::async::fdio fdioInstance;
};
TestServer::TestServer(sdbusplus::async::context& ctx, int fd) :
ctx(ctx), fd(fd), fdioInstance(ctx, fd)
{
ctx.spawn(processRequests());
}
auto TestServer::processRequests() -> sdbusplus::async::task<void>
{
while (!ctx.stop_requested())
{
MessageIntf request;
co_await fdioInstance.next();
auto ret = read(fd, request.raw.data(), request.raw.size());
// Request message need to be at least 4 bytes long - address(1),
// function code(1), ..., CRC(2)
if (ret < 4)
{
std::cerr << "Invalid Server message size:" << ret << ", drop it"
<< std::endl;
continue;
}
MessageIntf response;
processMessage(request, ret, response);
ret = write(fd, response.raw.data(), response.len);
if (ret < 0)
{
std::cerr << "Failed to send response" << std::endl;
}
}
}
void TestServer::processMessage(MessageIntf& request, size_t requestSize,
MessageIntf& response)
{
constexpr uint8_t readHoldingRegistersFunctionCode = 0x3;
switch (request.functionCode)
{
case readHoldingRegistersFunctionCode:
processReadHoldingRegisters(request, requestSize, response);
break;
default:
std::cerr << "Server received unknown request" << std::endl;
break;
}
}
void TestServer::processReadHoldingRegisters(
MessageIntf& request, size_t requestSize, MessageIntf& response)
{
uint16_t registerOffset = request.raw[2] << 8 | request.raw[3];
uint16_t registerCount = request.raw[4] << 8 | request.raw[5];
std::cout << "Received readHoldingRegisters request with size:"
<< requestSize << ", registerOffset:" << registerOffset
<< ", registerCount:" << registerCount << std::endl;
response << request.raw[0] << request.raw[1] << uint8_t(2 * registerCount);
for (int i = 0; i < registerCount; i++)
{
constexpr uint16_t readHoldingRegisterResponse = 0x4142;
response << uint16_t(readHoldingRegisterResponse);
}
response.appendCRC();
}
} // namespace phosphor::modbus::test
int main(int argc, char* argv[])
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " <integer_value>" << std::endl;
return 1;
}
int interfaceValue = 1;
try
{
interfaceValue = std::stoi(argv[1]);
}
catch (const std::invalid_argument& e)
{
std::cerr << "Invalid argument: " << e.what() << std::endl;
return 1;
}
catch (const std::out_of_range& e)
{
std::cerr << "Argument out of range: " << e.what() << std::endl;
return 1;
}
using TestServerIntf = phosphor::modbus::test::TestServer;
std::string devicePathStr =
std::string("/dev/ttyV") + std::to_string(interfaceValue);
const char* serverDevicePath = devicePathStr.c_str();
std::cout << "Starting at device path" << serverDevicePath << std::endl;
constexpr auto path = "/xyz/openbmc_project";
constexpr auto serviceName = "xyz.openbmc_project.ModbusRTUTestServer";
sdbusplus::async::context ctx;
auto fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fdServer == -1)
{
std::cerr << "Failed to open serial port " << serverDevicePath
<< " with error: " << strerror(errno) << std::endl;
return 1;
}
std::cout << "Creating Modbus RTU test server at " << path << std::endl;
TestServerIntf server{ctx, fdServer};
ctx.request_name(serviceName);
ctx.run();
return 0;
}