175 lines
5.8 KiB
C
Raw Permalink Normal View History

2026-08-04 14:23:07 +08:00
#ifndef PLC_MODBUS_CLIENT_H
#define PLC_MODBUS_CLIENT_H
#include <thread>
#include <mutex>
#include <functional>
#include <vector>
#include <atomic>
#include <string>
#include "IYModbusTCPClient.h"
/**
* @brief PLC Modbus 通信客户端(纯 std::thread 实现,无 Qt 依赖)
*
* 协议流程(0-based 地址):
* 1. 轮询读取地址0(触发寄存器),边沿 0→1 时触发工作
* 2. 触发后写 0 到地址0(清除触发)
* 3. 写 0 到地址1(工作状态=正在工作)
* 4. 工作完成,输出坐标数据到地址2开始
* 5. 写 1 到地址1(工作状态=完成正常),或写 2(错误)
*
* 线程模型:
* - 单一轮询线程:连接检测、自动重连、寄存器读取
* - 回调在轮询线程执行,调用方需注意线程安全
*
* 锁策略:
* - m_mutex: 保护 m_plcClient
* - m_callbackMutex: 保护回调函数指针
* - 回调通知时先复制回调再释放锁,避免死锁
*/
class PLCModbusClient
{
public:
// 寄存器地址默认值(0-based 协议)
static constexpr int DEFAULT_ADDR_TRIGGER = 0; // 触发寄存器
static constexpr int DEFAULT_ADDR_WORK_STATUS = 1; // 工作状态寄存器
static constexpr int DEFAULT_ADDR_COORD_DATA_START = 2; // 坐标数据起始
// 工作状态值枚举
enum WorkStatusValue {
WORK_STATUS_WORKING = 0, // 正在工作
WORK_STATUS_COMPLETE_OK = 1, // 完成正常
WORK_STATUS_ERROR = 2 // 错误
};
// Modbus 浮点寄存器字顺序枚举(寄存器内部始终为大端)
enum ByteOrder {
BIG_ENDIAN_ORDER = 0, // 标准字顺序 (ABCD),高位字在低地址
LITTLE_ENDIAN_ORDER = 1 // 字交换 (CDAB),低位字在低地址
};
struct RegisterConfig {
int addrTrigger = 0; // 触发寄存器地址
int addrWorkStatus = 1; // 工作状态寄存器地址
int addrCoordDataStart = 2; // 坐标数据起始地址
int byteOrder = BIG_ENDIAN_ORDER; // 浮点寄存器字顺序,默认 ABCD
};
struct CoordinateData {
uint16_t workpieceType = 0;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float roll = 0.0f;
float pitch = 0.0f;
float yaw = 0.0f;
};
static constexpr int REGS_PER_POINT = 14; // workpieceType + reserved + 6 float values
static constexpr int MAX_POINTS = 10;
// 回调类型
using PhotoTriggerCallback = std::function<void(int cameraIndex)>;
using ConnectionStateCallback = std::function<void(bool connected)>;
using ErrorCallback = std::function<void(const std::string& errorMsg)>;
using ReconnectingCallback = std::function<void(const std::string& device, int attempt)>;
public:
PLCModbusClient();
~PLCModbusClient();
// 禁止拷贝和移动
PLCModbusClient(const PLCModbusClient&) = delete;
PLCModbusClient& operator=(const PLCModbusClient&) = delete;
PLCModbusClient(PLCModbusClient&&) = delete;
PLCModbusClient& operator=(PLCModbusClient&&) = delete;
// ========== 生命周期 ==========
bool Initialize(const std::string& plcIP, int plcPort = 502);
bool Initialize(const std::string& plcIP, int plcPort,
const RegisterConfig& regConfig);
void Shutdown();
void StartPolling(int intervalMs = 100);
void StopPolling();
// ========== 回调设置 ==========
void SetPhotoTriggerCallback(PhotoTriggerCallback callback);
void SetConnectionStateCallback(ConnectionStateCallback callback);
void SetErrorCallback(ErrorCallback callback);
void SetReconnectingCallback(ReconnectingCallback callback);
// ========== 配置 ==========
void SetReconnectInterval(int intervalMs);
void SetAutoReconnect(bool enable);
// ========== PLC 操作 ==========
bool SendCoordinateToPLC(const CoordinateData& coord, int pointIndex = 0);
bool WriteWorkStatus(WorkStatusValue status);
bool ClearTrigger();
// ========== 轮询控制 ==========
void PauseTriggerPolling(); // 暂停读取触发寄存器(检测期间调用)
void ResumeTriggerPolling(); // 恢复读取触发寄存器(检测完成后调用)
bool IsTriggerPollingPaused() const;
// ========== 状态查询 ==========
bool IsPLCConnected() const;
private:
// 轮询线程
void pollThreadFunc();
// 连接管理
bool checkConnection();
bool tryConnectPLC();
void disconnectPLC(); // 主动断开连接(用于触发重连)
// 寄存器操作
int readTriggerRegister();
void floatToRegisters(float value, uint16_t& high, uint16_t& low);
// 安全的回调通知(先复制回调,释放锁后再调用)
void notifyConnectionStateChanged(bool connected);
void notifyError(const std::string& errorMsg);
void notifyTriggerRequested(int cameraIndex);
void notifyReconnecting(const std::string& device, int attempt);
private:
// PLC 客户端
IYModbusTCPClient* m_plcClient;
// 回调函数
PhotoTriggerCallback m_photoCallback;
ConnectionStateCallback m_connectionStateCallback;
ErrorCallback m_errorCallback;
ReconnectingCallback m_reconnectingCallback;
// 连接配置
std::string m_plcIP;
int m_plcPort;
RegisterConfig m_registerConfig;
// 状态
bool m_lastTriggerState;
bool m_lastConnectedState;
// 线程控制
std::thread m_pollThread;
std::atomic<bool> m_pollRunning;
std::atomic<bool> m_triggerPollingPaused; // 暂停触发寄存器轮询标志
int m_pollIntervalMs;
// 重连控制
std::atomic<bool> m_shutdownRequested;
std::atomic<bool> m_autoReconnect;
int m_reconnectInterval;
int m_plcReconnectAttempts;
// 互斥锁
mutable std::mutex m_mutex;
std::mutex m_callbackMutex;
};
#endif // PLC_MODBUS_CLIENT_H