2026-02-01 14:51:16 +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 依赖)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 协议流程:
|
|
|
|
|
|
* 1. 轮询读取 PLC D1000 寄存器,=1 时触发拍照
|
|
|
|
|
|
* 2. 拍照执行后,写 0 到 D1000
|
|
|
|
|
|
* 3. 拍照完成,输出坐标数据到 D2000 开始
|
|
|
|
|
|
* 4. 坐标数据输出完成,写 1 到 D1002
|
|
|
|
|
|
*
|
|
|
|
|
|
* 线程模型:
|
|
|
|
|
|
* - 单一轮询线程:连接检测、自动重连、寄存器读取
|
|
|
|
|
|
* - 回调在轮询线程执行,调用方需注意线程安全
|
|
|
|
|
|
*
|
|
|
|
|
|
* 锁策略:
|
|
|
|
|
|
* - m_mutex: 保护 m_plcClient
|
|
|
|
|
|
* - m_callbackMutex: 保护回调函数指针
|
|
|
|
|
|
* - 回调通知时先复制回调再释放锁,避免死锁
|
|
|
|
|
|
*/
|
|
|
|
|
|
class PLCModbusClient
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
// PLC 寄存器地址默认值(汇川 PLC D寄存器映射)
|
|
|
|
|
|
static constexpr int DEFAULT_ADDR_PHOTO_REQUEST = 1001; // D1000
|
|
|
|
|
|
static constexpr int DEFAULT_ADDR_DATA_COMPLETE = 1003; // D1002
|
|
|
|
|
|
static constexpr int DEFAULT_ADDR_COORD_DATA_START = 2001; // D2000
|
|
|
|
|
|
|
|
|
|
|
|
struct RegisterConfig {
|
|
|
|
|
|
int addrPhotoRequest = 1001; // D1000
|
|
|
|
|
|
int addrDataComplete = 1003; // D1002
|
|
|
|
|
|
int addrCoordDataStart = 2001; // D2000
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct CoordinateData {
|
|
|
|
|
|
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 = 12;
|
|
|
|
|
|
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 SendCoordinatesToPLC(const std::vector<CoordinateData>& coords);
|
|
|
|
|
|
bool NotifyDataComplete();
|
|
|
|
|
|
bool ClearPhotoRequest();
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 轮询控制 ==========
|
|
|
|
|
|
void PausePhotoRequestPolling(); // 暂停读取拍照请求(检测期间调用)
|
|
|
|
|
|
void ResumePhotoRequestPolling(); // 恢复读取拍照请求(检测完成后调用)
|
|
|
|
|
|
bool IsPhotoRequestPollingPaused() const;
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 状态查询 ==========
|
|
|
|
|
|
bool IsPLCConnected() const;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
// 轮询线程
|
|
|
|
|
|
void pollThreadFunc();
|
|
|
|
|
|
|
|
|
|
|
|
// 连接管理
|
|
|
|
|
|
bool checkConnection();
|
|
|
|
|
|
bool tryConnectPLC();
|
2026-02-02 23:24:24 +08:00
|
|
|
|
void disconnectPLC(); // 主动断开连接(用于触发重连)
|
2026-02-01 14:51:16 +08:00
|
|
|
|
|
|
|
|
|
|
// 寄存器操作
|
|
|
|
|
|
int readPhotoRequest();
|
|
|
|
|
|
void floatToRegisters(float value, uint16_t& high, uint16_t& low);
|
|
|
|
|
|
|
|
|
|
|
|
// 安全的回调通知(先复制回调,释放锁后再调用)
|
|
|
|
|
|
void notifyConnectionStateChanged(bool connected);
|
|
|
|
|
|
void notifyError(const std::string& errorMsg);
|
|
|
|
|
|
void notifyPhotoRequested(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_lastPhotoRequestState;
|
|
|
|
|
|
bool m_lastConnectedState;
|
|
|
|
|
|
|
|
|
|
|
|
// 线程控制
|
|
|
|
|
|
std::thread m_pollThread;
|
|
|
|
|
|
std::atomic<bool> m_pollRunning;
|
|
|
|
|
|
std::atomic<bool> m_photoRequestPaused; // 暂停拍照请求轮询标志
|
|
|
|
|
|
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
|