GrabBag/Module/ModbusTCPServer/Src/ModbusTCPServer.cpp
杰仔 ae2f795d2c 初步的页面和流程
TestDATA的增加
ARM版本初步编译(代码通过,缺少opencv)
2025-06-17 00:37:05 +08:00

513 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ModbusTCPServer.h"
#include <iostream>
#include <cstring>
#include <stdexcept>
#include "VrError.h"
#include "VrLog.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <errno.h>
#endif
ModbusTCPServer::ModbusTCPServer()
: m_modbusCtx(nullptr)
, m_mapping(nullptr)
, m_isRunning(false)
, m_shouldStop(false)
, m_serverSocket(-1)
, m_port(502)
, m_maxConnections(10)
{
}
ModbusTCPServer::~ModbusTCPServer()
{
stop();
}
int ModbusTCPServer::start(int port, int maxConnections)
{
if (m_isRunning.load()) {
setLastError("服务器已在运行");
return SUCCESS;
}
m_port = port;
m_maxConnections = maxConnections;
// 创建modbus TCP context
m_modbusCtx = modbus_new_tcp("0.0.0.0", port);
if (m_modbusCtx == nullptr) {
setLastError("创建Modbus TCP上下文失败");
return ERR_CODE(NET_ERR_CREAT_INIT);
}
// 创建数据映射 - 分配足够的空间
// 线圈: 0-9999, 离散输入: 0-9999, 保持寄存器: 0-9999, 输入寄存器: 0-9999
m_mapping = modbus_mapping_new(10000, 10000, 10000, 10000);
if (m_mapping == nullptr) {
setLastError("创建数据映射失败");
modbus_free(m_modbusCtx);
m_modbusCtx = nullptr;
return ERR_CODE(NET_ERR_CONFIG);
}
// 监听连接
m_serverSocket = modbus_tcp_listen(m_modbusCtx, maxConnections);
if (m_serverSocket == -1) {
setLastError(std::string("监听失败: ") + modbus_strerror(errno));
modbus_mapping_free(m_mapping);
m_mapping = nullptr;
modbus_free(m_modbusCtx);
m_modbusCtx = nullptr;
return ERR_CODE(NET_ERR_CREAT_LISTEN);
}
m_isRunning.store(true);
m_shouldStop.store(false);
// 启动服务器线程
m_serverThread = std::make_unique<std::thread>(&ModbusTCPServer::serverLoop, this);
// 初始状态:服务器启动但没有客户端连接
if (m_connectionStatusCallback) {
m_connectionStatusCallback(false);
}
return SUCCESS;
}
void ModbusTCPServer::stop()
{
if (!m_isRunning.load()) {
return;
}
m_shouldStop.store(true);
// 关闭服务器socket以中断accept调用
if (m_serverSocket != -1) {
#ifdef _WIN32
closesocket(m_serverSocket);
#else
close(m_serverSocket);
#endif
m_serverSocket = -1;
}
// 等待服务器线程退出
if (m_serverThread && m_serverThread->joinable()) {
m_serverThread->join();
}
// 清理所有客户端连接
{
std::lock_guard<std::mutex> lock(m_clientsMutex);
m_clients.clear();
}
// 清理资源
if (m_mapping) {
modbus_mapping_free(m_mapping);
m_mapping = nullptr;
}
if (m_modbusCtx) {
modbus_free(m_modbusCtx);
m_modbusCtx = nullptr;
}
m_isRunning.store(false);
}
void ModbusTCPServer::serverLoop()
{
fd_set readfds;
int maxfd = m_serverSocket;
while (!m_shouldStop.load()) {
FD_ZERO(&readfds);
FD_SET(m_serverSocket, &readfds);
maxfd = m_serverSocket;
// 添加所有客户端socket到监听集合
{
std::lock_guard<std::mutex> lock(m_clientsMutex);
for (const auto& pair : m_clients) {
int clientSocket = pair.first;
FD_SET(clientSocket, &readfds);
if (clientSocket > maxfd) {
maxfd = clientSocket;
}
}
}
// 设置超时时间1秒避免阻塞太久
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int selectResult = select(maxfd + 1, &readfds, nullptr, nullptr, &timeout);
if (selectResult == -1) {
if (!m_shouldStop.load()) {
LOG_WARNING("select error: %s\n", strerror(errno));
}
break;
}
if (selectResult == 0) {
// 超时,继续循环
continue;
}
// 检查服务器socket是否有新连接
if (FD_ISSET(m_serverSocket, &readfds)) {
handleNewConnection();
}
// 检查客户端socket是否有数据
std::vector<int> socketsToRemove;
{
std::lock_guard<std::mutex> lock(m_clientsMutex);
for (const auto& pair : m_clients) {
int clientSocket = pair.first;
if (FD_ISSET(clientSocket, &readfds)) {
try {
handleClientData(pair.second);
} catch (...) {
// 客户端处理出错,标记删除
socketsToRemove.push_back(clientSocket);
}
}
}
}
// 删除有问题的客户端连接
for (int socket : socketsToRemove) {
removeClient(socket);
}
}
}
void ModbusTCPServer::handleNewConnection()
{
int clientSocket = modbus_tcp_accept(m_modbusCtx, &m_serverSocket);
if (clientSocket == -1) {
if (!m_shouldStop.load()) {
LOG_ERRO("Accept connection failed: %s\n", modbus_strerror(errno));
}
return;
}
// 创建新的modbus context用于此客户端
modbus_t* clientCtx = modbus_new_tcp("0.0.0.0", m_port);
if (clientCtx == nullptr) {
LOG_ERRO("Create Modbus context for client failed\n");
#ifdef _WIN32
closesocket(clientSocket);
#else
close(clientSocket);
#endif
return;
}
// 设置socket为非阻塞模式
modbus_set_socket(clientCtx, clientSocket);
// 创建客户端连接对象
auto client = std::make_shared<ClientConnection>(clientSocket, clientCtx);
// 添加到客户端列表
{
std::lock_guard<std::mutex> lock(m_clientsMutex);
m_clients[clientSocket] = client;
}
LOG_INFO("New client connected: socket=%d\n", clientSocket);
// 触发连接状态回调
if (m_connectionStatusCallback) {
m_connectionStatusCallback(true);
}
}
void ModbusTCPServer::handleClientData(std::shared_ptr<ClientConnection> client)
{
uint8_t buffer[MODBUS_TCP_MAX_ADU_LENGTH];
// 接收数据
int bytesReceived = recv(client->socket, reinterpret_cast<char*>(buffer), sizeof(buffer), 0);
if (bytesReceived <= 0) {
// 连接断开或错误
if (bytesReceived == 0) {
LOG_VERBOSE("Client disconnected: socket=%d\n", client->socket);
} else {
LOG_ERRO("Receive data error: %s\n", strerror(errno));
}
throw std::runtime_error("客户端连接错误");
}
// 处理Modbus请求
processModbusRequest(client, buffer, bytesReceived);
}
void ModbusTCPServer::removeClient(int socket)
{
std::lock_guard<std::mutex> lock(m_clientsMutex);
auto it = m_clients.find(socket);
if (it != m_clients.end()) {
LOG_INFO("Remove client connection: socket=%d\n", socket);
m_clients.erase(it);
// 如果没有客户端连接了,触发断开回调
if (m_clients.empty() && m_connectionStatusCallback) {
m_connectionStatusCallback(false);
}
}
}
void ModbusTCPServer::processModbusRequest(std::shared_ptr<ClientConnection> client, const uint8_t* query, int queryLength)
{
// 处理请求并发送响应
std::lock_guard<std::mutex> lock(m_dataMutex);
int responseRc = modbus_reply(client->modbusCtx, query, queryLength, m_mapping);
if (responseRc == -1) {
LOG_WARNING("Send response failed: %s\n", modbus_strerror(errno));
throw std::runtime_error("发送响应失败");
}
// 解析ModbusTCP ADU - 最小长度检查
if (queryLength < 8) {
LOG_DEBUG("Query too short: %d bytes\n", queryLength);
return;
}
// ModbusTCP ADU格式: [Transaction ID(2)] [Protocol ID(2)] [Length(2)] [Unit ID(1)] [Function Code(1)] [Data...]
uint16_t transactionId = (query[0] << 8) | query[1]; // Transaction ID
uint16_t protocolId = (query[2] << 8) | query[3]; // Protocol ID (should be 0 for ModbusTCP)
uint16_t length = (query[4] << 8) | query[5]; // Length field
uint8_t unitId = query[6]; // Unit ID
uint8_t function = query[7]; // Function Code
// 验证协议ID
if (protocolId != 0) {
LOG_DEBUG("Invalid protocol ID: %d\n", protocolId);
return;
}
// 验证长度字段
if (length != (queryLength - 6)) {
LOG_DEBUG("Length mismatch: expected %d, got %d\n", length, queryLength - 6);
return;
}
LOG_DEBUG("ModbusTCP request - TransID:%d, UnitID:%d, Function:0x%02X\n",
transactionId, unitId, function);
// 根据功能码解析数据
switch (function) {
case MODBUS_FC_WRITE_SINGLE_COIL:
{
if (queryLength >= 12) {
uint16_t address = (query[8] << 8) | query[9];
uint16_t value = (query[10] << 8) | query[11];
LOG_DEBUG("Write single coil - Address:%d, Value:0x%x\n", address, value);
if (m_writeCoilsCallback) {
uint8_t coilValue = (value == 0xFF00) ? 1 : 0;
ErrorCode result = m_writeCoilsCallback(unitId, address, 1, &coilValue);
if (result != ErrorCode::SUCCESS) {
LOG_DEBUG("Write coil callback returned error: %d\n", (int)result);
}
}
}
}
break;
case MODBUS_FC_WRITE_MULTIPLE_COILS:
{
if (queryLength >= 13) {
uint16_t address = (query[8] << 8) | query[9];
uint16_t quantity = (query[10] << 8) | query[11];
uint8_t byteCount = query[12];
if (queryLength >= (13 + byteCount)) {
LOG_DEBUG("Write multiple coils - Address:%d, Quantity:%d, Bytes:%d\n",
address, quantity, byteCount);
if (m_writeCoilsCallback) {
const uint8_t* values = &query[13];
ErrorCode result = m_writeCoilsCallback(unitId, address, quantity, values);
if (result != ErrorCode::SUCCESS) {
LOG_DEBUG("Write coils callback returned error: %d\n", (int)result);
}
}
}
}
}
break;
case MODBUS_FC_WRITE_SINGLE_REGISTER:
{
if (queryLength >= 12) {
uint16_t address = (query[8] << 8) | query[9];
uint16_t value = (query[10] << 8) | query[11];
LOG_DEBUG("Write single register - Address:%d, Value:%d\n", address, value);
if (m_writeRegistersCallback) {
ErrorCode result = m_writeRegistersCallback(unitId, address, 1, &value);
if (result != ErrorCode::SUCCESS) {
LOG_DEBUG("Write register callback returned error: %d\n", (int)result);
}
}
}
}
break;
case MODBUS_FC_WRITE_MULTIPLE_REGISTERS:
{
if (queryLength >= 13) {
uint16_t address = (query[8] << 8) | query[9];
uint16_t quantity = (query[10] << 8) | query[11];
uint8_t byteCount = query[12];
if (queryLength >= (13 + byteCount) && byteCount == (quantity * 2)) {
LOG_DEBUG("Write multiple registers - Address:%d, Quantity:%d, Bytes:%d\n",
address, quantity, byteCount);
if (m_writeRegistersCallback) {
// 解析寄存器值(注意字节序)
std::vector<uint16_t> values;
values.reserve(quantity);
for (int i = 0; i < quantity; i++) {
uint16_t value = (query[13 + i*2] << 8) | query[13 + i*2 + 1];
values.push_back(value);
}
ErrorCode result = m_writeRegistersCallback(unitId, address, quantity, values.data());
if (result != ErrorCode::SUCCESS) {
LOG_DEBUG("Write registers callback returned error: %d\n", (int)result);
}
}
}
}
}
break;
default:
LOG_DEBUG("Unhandled function code: 0x%02X\n", function);
break;
}
}
void ModbusTCPServer::updateCoil(uint16_t address, bool value)
{
std::lock_guard<std::mutex> lock(m_dataMutex);
if (m_mapping && address < m_mapping->nb_bits) {
m_mapping->tab_bits[address] = value ? 1 : 0;
}
}
void ModbusTCPServer::updateCoils(uint16_t startAddress, const std::vector<bool>& values)
{
std::lock_guard<std::mutex> lock(m_dataMutex);
if (!m_mapping) return;
for (size_t i = 0; i < values.size() && (startAddress + i) < m_mapping->nb_bits; ++i) {
m_mapping->tab_bits[startAddress + i] = values[i] ? 1 : 0;
}
}
void ModbusTCPServer::updateDiscreteInput(uint16_t address, bool value)
{
std::lock_guard<std::mutex> lock(m_dataMutex);
if (m_mapping && address < m_mapping->nb_input_bits) {
m_mapping->tab_input_bits[address] = value ? 1 : 0;
}
}
void ModbusTCPServer::updateDiscreteInputs(uint16_t startAddress, const std::vector<bool>& values)
{
std::lock_guard<std::mutex> lock(m_dataMutex);
if (!m_mapping) return;
for (size_t i = 0; i < values.size() && (startAddress + i) < m_mapping->nb_input_bits; ++i) {
m_mapping->tab_input_bits[startAddress + i] = values[i] ? 1 : 0;
}
}
void ModbusTCPServer::updateHoldingRegister(uint16_t address, uint16_t value)
{
std::lock_guard<std::mutex> lock(m_dataMutex);
if (m_mapping && address < m_mapping->nb_registers) {
m_mapping->tab_registers[address] = value;
}
}
void ModbusTCPServer::updateHoldingRegisters(uint16_t startAddress, const std::vector<uint16_t>& values)
{
std::lock_guard<std::mutex> lock(m_dataMutex);
if (!m_mapping) return;
for (size_t i = 0; i < values.size() && (startAddress + i) < m_mapping->nb_registers; ++i) {
m_mapping->tab_registers[startAddress + i] = values[i];
}
}
void ModbusTCPServer::updateInputRegister(uint16_t address, uint16_t value)
{
std::lock_guard<std::mutex> lock(m_dataMutex);
if (m_mapping && address < m_mapping->nb_input_registers) {
m_mapping->tab_input_registers[address] = value;
}
}
void ModbusTCPServer::updateInputRegisters(uint16_t startAddress, const std::vector<uint16_t>& values)
{
std::lock_guard<std::mutex> lock(m_dataMutex);
if (!m_mapping) return;
for (size_t i = 0; i < values.size() && (startAddress + i) < m_mapping->nb_input_registers; ++i) {
m_mapping->tab_input_registers[startAddress + i] = values[i];
}
}
std::string ModbusTCPServer::getLastError() const
{
std::lock_guard<std::mutex> lock(m_errorMutex);
return m_lastError;
}
void ModbusTCPServer::setLastError(const std::string& error)
{
std::lock_guard<std::mutex> lock(m_errorMutex);
m_lastError = error;
}
bool IYModbusTCPServer::CreateInstance(IYModbusTCPServer** ppModbusTCPServer)
{
if (ppModbusTCPServer == nullptr) {
return false;
}
*ppModbusTCPServer = new ModbusTCPServer();
return true;
}