2026-03-11 23:40:06 +08:00

166 lines
5.4 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.

#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. 轮询读取拍照请求寄存器默认地址0值=1时触发相机1值=2时触发相机2
* 2. 拍照执行后,写 0 到拍照请求寄存器
* 3. 拍照完成输出坐标数据到坐标数据起始地址默认地址2
* 4. 坐标数据输出完成,写 1 到数据完成寄存器默认地址1
*
* 线程模型:
* - 单一轮询线程:连接检测、自动重连、寄存器读取
* - 回调在轮询线程执行,调用方需注意线程安全
*
* 锁策略:
* - m_mutex: 保护 m_plcClient
* - m_callbackMutex: 保护回调函数指针
* - 回调通知时先复制回调再释放锁,避免死锁
*/
class PLCModbusClient
{
public:
// PLC 寄存器地址默认值
static constexpr int DEFAULT_ADDR_PHOTO_REQUEST = 0; // 拍照请求1=相机1, 2=相机2
static constexpr int DEFAULT_ADDR_DATA_COMPLETE = 1; // 数据完成标志
static constexpr int DEFAULT_ADDR_COORD_DATA_START = 2; // 坐标数据起始
// 数据字节序枚举
enum ByteOrder {
BIG_ENDIAN_ORDER = 0, // 大端序 (ABCD) - 高字节在前
LITTLE_ENDIAN_ORDER = 1 // 小端序 (DCBA) - 低字节在前
};
struct RegisterConfig {
int addrPhotoRequest = 0; // 拍照请求1=相机1, 2=相机2
int addrDataComplete = 1; // 数据完成标志
int addrCoordDataStart = 2; // 坐标数据起始
int byteOrder = BIG_ENDIAN_ORDER; // 数据字节序,默认大端序
};
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 NotifyDataComplete();
bool ClearPhotoRequest();
// ========== 轮询控制 ==========
void PausePhotoRequestPolling(); // 暂停读取拍照请求(检测期间调用)
void ResumePhotoRequestPolling(); // 恢复读取拍照请求(检测完成后调用)
bool IsPhotoRequestPollingPaused() const;
// ========== 状态查询 ==========
bool IsPLCConnected() const;
private:
// 轮询线程
void pollThreadFunc();
// 连接管理
bool checkConnection();
bool tryConnectPLC();
void disconnectPLC(); // 主动断开连接(用于触发重连)
// 寄存器操作
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