From 63686244746925d43248ceaf8d9e31f50df68a72 Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期一, 01 九月 2025 17:21:43 +0800
Subject: [PATCH] Merge branch 'clh'
---
SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.cpp | 1594 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,594 insertions(+), 0 deletions(-)
diff --git a/SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.cpp b/SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.cpp
new file mode 100644
index 0000000..b8e6a84
--- /dev/null
+++ b/SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.cpp
@@ -0,0 +1,1594 @@
+// PerformanceMelsec.cpp: implementation of the CPerformanceMelsec class.
+//
+//////////////////////////////////////////////////////////////////////
+#include "pch.h"
+#include "PerformanceMelsec.h"
+#include <windows.h>
+#include <iostream>
+#include <fstream>
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char* THIS_FILE = __FILE__;
+#define new DEBUG_NEW
+#endif
+
+#ifdef _DEBUG
+#define LOG_ERROR(msg) \
+std::cerr << "[ERROR] " << __FILE__ << ":" << __LINE__ << " (" << __FUNCTION__ << ") - " << msg << std::endl;
+#define LOG_DEBUG(msg) \
+std::cout << "[DEBUG] " << __FILE__ << ":" << __LINE__ << " (" << __FUNCTION__ << ") - " << msg << std::endl;
+#else
+#define LOG_ERROR(msg)
+#define LOG_DEBUG(msg)
+#endif
+
+// 初始化静态成员变量
+std::unordered_map<int, std::string> CPerformanceMelsec::m_mapError = {
+ // 板块SDK错误码
+ {0, "No error, communication successful."},
+ {1, "Driver not started. The driver is not running."},
+ {2, "Timeout error (board response error). Request not completed within timeout."},
+ {66, "Already OPEN error. The specified channel is OPEN."},
+ {68, "Path error. The specified path is invalid."},
+ {69, "Unsupported function execution error."},
+ {70, "Station number error. The specified station number is invalid."},
+ {71, "No received data error (during RECV function)."},
+ {77, "Memory allocation error / insufficient memory resources."},
+ {85, "SEND/RECV channel number error."},
+ {100, "Board H/W resource busy."},
+ {101, "Routing exception."},
+ {102, "Board driver I/F error: Failed to send request data to the board driver."},
+ {103, "Board driver I/F error: Failed to receive response data from the board driver."},
+ {130, "Initial software component No. Error."},
+ {131, "Capacity error."},
+ {133, "Parameter error."},
+ {16385, "Specified target station number does not exist."},
+ {16386, "Received a request that the target station cannot process."},
+ {16418, "Failed to create the event history file."},
+ {16420, "Failed to access the event history file."},
+ {16421, "Another board driver is using the event history file."},
+ {16432, "The specified soft component type does not exist."},
+ {16433, "Soft component specification error: Out of range or invalid start I/O or block number."},
+ {16512, "Request data exception: Invalid data or unsupported module."},
+ {16685, "File association error: Failed to create the event history file."},
+ {16837, "File association error: Event history file does not exist."},
+ {18944, "Link association error: Network does not exist, unsupported CPU, or incorrect network No./station number."},
+ {-1, "Invalid path. The specified function is not supported for this path."},
+ {-2, "Start component No. error. The specified component is out of range."},
+ {-3, "Capacity error. The capacity exceeds the component range."},
+ {-6, "Component type error. The specified type during write is invalid."},
+ {-8, "Channel No. error. The channel specified is invalid."},
+ {-12, "Target path error. The specified path points to an invalid target."},
+ {-13, "Write protection area error. The specified range is protected."},
+ {-16, "Target path conflict. The path conflicts with write protection settings."},
+ {-17, "Device not found or target not responding."},
+ {-18, "Invalid target. The device does not support the operation."},
+ {-19, "Invalid path operation. An unsupported path operation was executed."},
+ {-31, "DLL library call failed or path not initialized."},
+ {-32, "Resource timeout error. Communication timed out or exceeded resource limits."},
+ {-33, "Communication timeout error. The target is not responding or timed out."},
+ {-34, "Unsupported communication target error. The specified network No. or station No. points to an unsupported model."},
+ {-35, "Registry access error."},
+ {-36, "Registry access error."},
+ {-37, "Communication initialization error. The settings for initializing the communication path are invalid."},
+ {-42, "Key information error. Authentication failed."},
+ {-43, "Marking event error. TC waiting event write was executed on the CPU."},
+ {-61, "Marking event error. TC waiting event write was executed on the CPU."},
+ {-62, "Event waiting timeout. The specified external event waiting timed out."},
+ {-63, "Timeout value is out of range."},
+ {-64, "Timeout value is out of range."},
+ {-65, "Event waiting timeout. The specified external event waiting timed out."},
+ {-66, "Timeout-induced resource shortage."},
+ {-67, "Irrelevant file access execution error."},
+ {-69, "Operation executed, but the module does not support the function."},
+ {-70, "The target event processing module returned a rejection."},
+ {-71, "The remote station did not return data correctly."},
+ {-72, "Pointer error. The specified pointer value is invalid."},
+ {-73, "Specified address error."},
+ {-2174, "Buffer data queue exception occurred. Read/write exception to device."},
+ {-7656, "Buffer data queue exception. Read/write exception to the device."},
+ {-7672, "Buffer data queue exception. Read/write exception to the device."},
+ {-11683, "Buffer data transfer error."},
+ {-11717, "Network No. error."},
+ {-11746, "Station No. error."},
+ {-12128, "Buffer data send/response error."},
+ {-18560, "Module mode setting error."},
+ {-18572, "Communication method error."},
+ {-25056, "Processor error."},
+ {-26334, "Duplicate program call or illegal CPU operation."},
+ {-26336, "Routing request error to a station without routing function support."},
+ {-27902, "Event register timeout error."},
+ {-28079, "Communication No. read error."},
+ {-28080, "Communication No. incorrect error."},
+ {-28136, "Unsupported function in fast mode error."},
+ {-28139, "Link disconnection error."},
+ {-28140, "Incorrect mode setting error."},
+ {-28141, "System reboot error."},
+ {-28142, "Mode error."},
+ {-28143, "Hardware self-diagnosis error."},
+ {-28144, "Hardware self-diagnosis error."},
+ {-28150, "Data reception interruption at remote station error."},
+ {-28151, "Data reception interruption at remote station error."},
+ {-28153, "Data reception interruption at remote station error."},
+ {-28154, "Abnormal data reception error."},
+ {-28158, "Driver WDT error."},
+ {-28160, "Hardware resource error."},
+ {-28622, "Dedicated instruction channel in-use error."},
+ {-28634, "Hardware self-diagnosis error."},
+ {-28636, "Hardware self-diagnosis error."},
+
+ // 自定义错误码
+ {ERROR_CODE_UNKNOWN, "Error: Unknown error code."},
+ {ERROR_CODE_NOT_CONNECTED, "Error: Not connected to the device."},
+ {ERROR_CODE_INVALID_PARAM, "Error: Invalid parameter."},
+ {ERROR_CODE_INVALID_DATA, "Error: Invalid data provided."},
+ {ERROR_CODE_STATION_OUT_OF_RANGE, "Error: Station number is out of range."},
+ {ERROR_CODE_GROUP_OUT_OF_RANGE, "Error: Group number is out of range."},
+ {ERROR_CODE_NETWORK_OUT_OF_RANGE, "Error: Network number is out of range."}
+};
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+CPerformanceMelsec::CPerformanceMelsec(const BoardType enBoardType) {
+ m_nPath = 0;
+ m_enBoardType = enBoardType;
+ m_bConnected.store(false);
+}
+
+// 析构函数
+CPerformanceMelsec::~CPerformanceMelsec() {
+ Disconnect();
+}
+
+// 获取最近的错误信息
+std::string CPerformanceMelsec::GetLastError() const {
+ return m_strLastError;
+}
+
+// 保存错误信息
+bool CPerformanceMelsec::SaveErrorInfoToFile(const std::string& filename) {
+ // 打开文件
+ std::ofstream file(filename);
+ if (!file.is_open()) {
+ std::cerr << "Failed to open file for saving: " << filename << std::endl;
+ return false;
+ }
+
+ // 遍历静态成员变量 m_mapError 并将每个错误信息写入文件
+ for (const auto& entry : m_mapError) {
+ const int nCode = entry.first;
+ const std::string& strMessage = entry.second;
+ file << nCode << "|" << strMessage << "\n";
+ }
+ file.close();
+
+ return true;
+}
+
+// 加载错误信息
+bool CPerformanceMelsec::LoadErrorInfoFromFile(const std::string& filename) {
+ std::ifstream inFile(filename);
+ if (!inFile.is_open()) {
+ std::cerr << "Failed to open file for loading: " << filename << std::endl;
+ return false;
+ }
+
+ m_mapError.clear();
+ std::string line;
+ while (std::getline(inFile, line)) {
+ std::istringstream iss(line);
+ int nCode = 0;
+ std::string strToken;
+ std::string strMessage;
+
+ // 使用分隔符 "|" 解析每一行
+ if (std::getline(iss, strToken, '|')) {
+ nCode = std::stoi(strToken);
+ }
+
+ if (std::getline(iss, strToken)) {
+ strMessage = strToken;
+ }
+
+ if (!strMessage.empty()) {
+ m_mapError[nCode] = strMessage;
+ }
+ }
+
+ return true;
+}
+
+// 连接到PLC
+int CPerformanceMelsec::Connect(const short nChannel, const short nMode) {
+ std::lock_guard<std::mutex> lock(m_mtx);
+
+ if (m_bConnected.load()) {
+ return 0;
+ }
+
+ const BoardType enBoardType = FindBoardTypeByChannel(nChannel);
+ if (enBoardType == BoardType::UNKNOWN || enBoardType != m_enBoardType) {
+ UpdateLastError(ERROR_CODE_INVALID_PARAM);
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ // 连接PLC,显式类型转换以匹配 mdOpen 的签名
+ const short nRet = mdOpen(nChannel, nMode, &m_nPath);
+ if (nRet == 0) {
+ m_bConnected.store(true);
+ m_enBoardType = enBoardType;
+ }
+ else {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 断开连接
+int CPerformanceMelsec::Disconnect() {
+ std::lock_guard<std::mutex> lock(m_mtx);
+
+ short nRet = 0;
+ if (m_bConnected.load()) {
+ nRet = mdClose(m_nPath);
+ m_bConnected.store(false);
+ m_nPath = 0;
+ }
+
+ UpdateLastError(nRet);
+ LOG_DEBUG("Close connect.");
+
+ return nRet;
+}
+
+// 可编程控制器软元件信息表的初始化
+int CPerformanceMelsec::InitializeController() {
+ std::lock_guard<std::mutex> lock(m_mtx);
+
+ if (!m_bConnected.load()) {
+ UpdateLastError(ERROR_CODE_NOT_CONNECTED);
+ return ERROR_CODE_NOT_CONNECTED;
+ }
+
+ const short nRet = mdInit(m_nPath);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 获取版本信息
+int CPerformanceMelsec::GetBoardVersion(BoardVersion& version) {
+ if (!m_bConnected.load()) {
+ UpdateLastError(ERROR_CODE_NOT_CONNECTED);
+ return ERROR_CODE_NOT_CONNECTED;
+ }
+
+ // 获取版本信息
+ short buf[32] = { 0 };
+ const short nRet = mdBdVerRead(m_nPath, buf);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ return nRet;
+ }
+
+ // 填充版本信息到结构体
+ version.fixedValue[0] = static_cast<char>(buf[0] & 0xFF);
+ version.fixedValue[1] = static_cast<char>((buf[0] >> 8) & 0xFF);
+
+ version.checksum[0] = static_cast<char>(buf[1] & 0xFF);
+ version.checksum[1] = static_cast<char>((buf[1] >> 8) & 0xFF);
+
+ version.swVersion[0] = static_cast<char>(buf[2] & 0xFF);
+ version.swVersion[1] = static_cast<char>((buf[2] >> 8) & 0xFF);
+
+ std::memcpy(version.date, &buf[3], 6);
+
+ version.reserved = static_cast<uint32_t>(buf[6]) | (static_cast<uint32_t>(buf[7]) << 16);
+
+ std::memcpy(version.swModel, &buf[8], 16);
+ std::memcpy(version.hwModel, &buf[16], 16);
+
+ version.twoPortMemory[0] = static_cast<char>(buf[18] & 0xFF);
+ version.twoPortMemory[1] = static_cast<char>((buf[18] >> 8) & 0xFF);
+
+ version.twoPortAttribute[0] = static_cast<char>(buf[19] & 0xFF);
+ version.twoPortAttribute[1] = static_cast<char>((buf[19] >> 8) & 0xFF);
+
+ version.availableBias[0] = static_cast<char>(buf[20] & 0xFF);
+ version.availableBias[1] = static_cast<char>((buf[20] >> 8) & 0xFF);
+
+ std::memcpy(version.moduleType, &buf[21], 10);
+
+ return nRet;
+}
+
+// 读取目标站点CPU类型
+int CPerformanceMelsec::ReadCPUCode(const StationIdentifier& station, short& nCPUCode) {
+ // 验证站点参数和数据有效性
+ int nRet = ValidateStation(station);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 确保线程安全的最小锁定范围
+ {
+ nCPUCode = 0;
+ std::lock_guard<std::mutex> lock(m_mtx);
+ nRet = mdTypeRead(m_nPath, CombineStation(station), &nCPUCode);
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 板模式设置
+int CPerformanceMelsec::SetBoardMode(const short nMode) {
+ // 检查是否已经连接
+ if (!m_bConnected.load()) {
+ UpdateLastError(ERROR_CODE_NOT_CONNECTED);
+ return ERROR_CODE_NOT_CONNECTED;
+ }
+
+ // 确保线程安全的最小锁定范围
+ short nRet = 0;
+ {
+ std::lock_guard<std::mutex> lock(m_mtx);
+ nRet = mdBdModSet(m_nPath, nMode);
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 获取板模式
+int CPerformanceMelsec::GetBoardMode(short& nMode) {
+ // 检查是否已经连接
+ if (!m_bConnected.load()) {
+ UpdateLastError(ERROR_CODE_NOT_CONNECTED);
+ return ERROR_CODE_NOT_CONNECTED;
+ }
+
+ short nRet = 0;
+ {
+ nMode = 0;
+ std::lock_guard<std::mutex> lock(m_mtx);
+ nRet = mdBdModRead(m_nPath, &nMode);
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_DEBUG("Raw Mode: " << nMode);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return 0;
+}
+
+// 板复位
+int CPerformanceMelsec::BoardReset() {
+ std::lock_guard<std::mutex> lock(m_mtx);
+ if (!m_bConnected.load()) {
+ UpdateLastError(ERROR_CODE_NOT_CONNECTED);
+ return ERROR_CODE_NOT_CONNECTED;
+ }
+
+ const short nRet = mdBdRst(m_nPath);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 板LED读取
+int CPerformanceMelsec::ReadBoardLed(std::vector<short>& vecLedBuffer) {
+ std::lock_guard<std::mutex> lock(m_mtx);
+ if (!m_bConnected.load()) {
+ UpdateLastError(ERROR_CODE_NOT_CONNECTED);
+ return ERROR_CODE_NOT_CONNECTED;
+ }
+
+ // 清空 LED 缓冲区
+ vecLedBuffer.clear();
+ vecLedBuffer.resize(16, 0);
+
+ // 调用 SDK 函数读取 LED 数据
+ const short nRet = mdBdLedRead(m_nPath, vecLedBuffer.data());
+ if (nRet != 0) {
+ UpdateLastError(ERROR_CODE_NOT_CONNECTED);
+ LOG_ERROR("Error reading board LED, ErrorCode: " << nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 获取板状态
+int CPerformanceMelsec::GetBoardStatus(BoardStatus& status) {
+ std::lock_guard<std::mutex> lock(m_mtx);
+ if (!m_bConnected) {
+ UpdateLastError(ERROR_CODE_NOT_CONNECTED);
+ return ERROR_CODE_NOT_CONNECTED;
+ }
+
+ short buf[6] = { 0 };
+ const short nRet = mdBdSwRead(m_nPath, buf);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ // 将 buf 映射到结构体
+ status = BoardStatus::fromBuffer(buf);
+ return 0;
+}
+
+// 通用读数据
+int CPerformanceMelsec::ReadData(const StationIdentifier& station, const long nDevType, const long nDevNo, long nSize, std::vector<short>& vecData) {
+ // 验证站点参数和数据有效性
+ int nRet = ValidateStationAndSize(station, static_cast<short>(nSize));
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 初始化读取缓冲区
+ vecData.clear();
+ vecData.resize(nSize, 0);
+
+ // 确保线程安全的最小锁定范围
+ {
+ std::lock_guard<std::mutex> lock(m_mtx);
+ short* pData = vecData.data();
+ nSize *= sizeof(short);
+ nRet = mdReceiveEx(m_nPath, station.nNetNo, station.nStNo, nDevType, (long)(unsigned short)nDevNo, &nSize, pData);
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ if (nRet != 0) {
+ vecData.clear(); // 如果读取失败,清空缓冲区
+ }
+
+ return nRet;
+}
+
+// 读取位数据
+int CPerformanceMelsec::ReadBitData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const short nBitCount, BitContainer& vecData) {
+ // 验证站点参数和数据有效性
+ int nRet = ValidateStationAndSize(station, nBitCount);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ if (nDevNo % 8 != 0) {
+ nRet = -2;
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ const auto nSize = static_cast<short>((static_cast<int>(nBitCount) + 15) / 16); // 计算需要读取的字数量(向上取整)
+
+ std::vector<short> vecTempBuffer(nSize, 0);
+ nRet = ReadData(station, nDevType, nDevNo, nSize, vecTempBuffer);
+
+ if (nRet == 0) {
+ vecData.clear();
+
+ // 将字数据解析为位数据
+ for (short nIdx = 0; nIdx < nSize; ++nIdx) {
+ const short nCurrentValue = vecTempBuffer[nIdx];
+ // 遍历当前 short 中的每一位
+ for (int bitIdx = 0; bitIdx < 16; ++bitIdx) {
+ bool bBit = (nCurrentValue & (1 << bitIdx)) != 0;
+ vecData.push_back(bBit);
+ if (vecData.size() >= nBitCount) {
+ return nRet; // 如果已经读取完所需的位数,提前退出
+ }
+ }
+ }
+ }
+
+ return nRet;
+}
+
+// 读取字数据
+int CPerformanceMelsec::ReadWordData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const short nWordCount, WordContainer& vecData) {
+ // 验证站点参数和数据有效性
+ int nRet = ValidateStationAndSize(station, nWordCount);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ std::vector<short> vecTempBuffer(nWordCount, 0);
+ nRet = ReadData(station, nDevType, nDevNo, nWordCount, vecTempBuffer);
+
+ if (nRet == 0) {
+ vecData.clear();
+ vecData.assign(vecTempBuffer.begin(), vecTempBuffer.end());
+ }
+
+ return nRet;
+}
+
+// 读取双字数据
+int CPerformanceMelsec::ReadDWordData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const short nDWordCount, DWordContainer& vecData) {
+ // 验证站点参数和数据有效性
+ int nRet = ValidateStationAndSize(station, nDWordCount);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const auto nSize = static_cast<short>(nDWordCount * 2); // 每个双字占两个字(每个双字占 4 字节)
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ std::vector<short> vecTempBuffer(nSize, 0);
+ nRet = ReadData(station, nDevType, nDevNo, nSize, vecTempBuffer);
+
+ if (nRet == 0) {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ ConvertShortToUint32(vecTempBuffer, vecData);
+ }
+
+ return nRet;
+}
+
+// 通用写数据
+int CPerformanceMelsec::WriteData(const StationIdentifier& station, const long nDevType, const long nDevNo, long nSize, short* pData) {
+ // 验证站点参数
+ int nRet = ValidateStation(station);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 数据有效性
+ if (nSize < 0 || pData == nullptr) {
+ UpdateLastError(ERROR_CODE_INVALID_PARAM);
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ // 确保线程安全的最小锁定范围
+ {
+ std::lock_guard<std::mutex> lock(m_mtx);
+ nSize *= sizeof(short);
+ nRet = mdSendEx(m_nPath, station.nNetNo, station.nStNo, nDevType, nDevNo, &nSize, pData);
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 写位数据
+int CPerformanceMelsec::WriteBitData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const BitContainer& vecData) {
+ // 验证站点参数和数据有效性
+ int nRet = ValidateStationAndData(station, vecData);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ if (nDevNo % 8 != 0) {
+ nRet = -2;
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ const auto nSize = static_cast<short>((static_cast<int>(vecData.size()) + 15) / 16); // 计算需要写入的字数量(向上取整)
+
+ // 准备临时缓冲区来存储转换后的 16 位数据
+ std::vector<short> vecTempBuffer(nSize, 0);
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ // 将位数据按字打包到临时缓冲区
+ for (int i = 0; i < vecData.size(); ++i) {
+ if (vecData[i]) {
+ // 使用 & 0xFFFF 保证不会超过 16 位,防止溢出
+ vecTempBuffer[i / 16] |= static_cast<short>((1 << (i % 16)) & 0xFFFF);
+ }
+ }
+ }
+
+ return WriteData(station, nDevType, nDevNo, nSize, vecTempBuffer.data());
+}
+
+// 写字数据
+int CPerformanceMelsec::WriteWordData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const WordContainer& vecData) {
+ // 验证站点参数和数据有效性
+ const int nRet = ValidateStationAndData(station, vecData);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 计算需要写入的字节数(每个字占 2 字节)
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ const auto nSize = static_cast<short>(vecData.size());
+ const auto pData = const_cast<short*>(reinterpret_cast<const short*>(vecData.data()));
+
+ return WriteData(station, nDevType, nDevNo, nSize, pData);
+}
+
+// 写双字数据
+int CPerformanceMelsec::WriteDWordData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const DWordContainer& vecData) {
+ // 验证站点参数和数据有效性
+ const int nRet = ValidateStationAndData(station, vecData);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 计算需要写入的字节数(每个双字占 4 字节)
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ const auto nSize = static_cast<short>(vecData.size() * sizeof(short));
+ std::vector<short> vecBuffer(nSize, 0);
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ ConvertUint32ToShort(vecData, vecBuffer);
+ }
+
+ return WriteData(station, nDevType, nDevNo, nSize, vecBuffer.data());
+}
+
+// 扩展读数据
+long CPerformanceMelsec::ReadDataEx(const StationIdentifier& station, long nDevType, long nDevNo, long nSize, std::vector<char>& vecData) {
+ // 验证站点参数和读取大小是否有效
+ long nRet = ValidateStation(station);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ if (nSize < 0) {
+ UpdateLastError(ERROR_CODE_INVALID_PARAM);
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ nSize = nSize % 2 != 0 ? nSize + 1 : nSize;
+ std::vector<short> vecBuffer(nSize / 2, 0);
+
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ nRet = mdReceiveEx(m_nPath, station.nNetNo, station.nStNo, nDevType, nDevNo, &nSize, vecBuffer.data());
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+ else {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ vecData.resize(nSize);
+ ConvertShortToChar(vecBuffer, vecData);
+ }
+
+ return 0;
+}
+
+// 扩展读取位数据
+long CPerformanceMelsec::ReadBitDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nBitCount, BitContainer& vecData) {
+ long nRet = ValidateStationAndSize(station, static_cast<short>(nBitCount));
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const short nDevType = CalculateDeviceType(station, enDevType);
+
+ // === 自动对齐到起始字 ===
+ long nWordAlignedStartBit = nDevNo / 16 * 16;
+ long nBitOffset = nDevNo - nWordAlignedStartBit;
+ long nTotalBits = nBitOffset + nBitCount;
+ long nWordCount = (nTotalBits + 15) / 16;
+ long nByteSize = nWordCount * sizeof(short);
+
+ std::vector<char> vecRaw;
+ nRet = ReadDataEx(station, nDevType, nWordAlignedStartBit, nByteSize, vecRaw);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ vecData.clear();
+ for (long i = 0; i < nWordCount; ++i) {
+ short word = static_cast<unsigned char>(vecRaw[i * 2]) |
+ (static_cast<unsigned char>(vecRaw[i * 2 + 1]) << 8);
+
+ for (int j = 0; j < 16; ++j) {
+ long bitIndex = i * 16 + j;
+ if (bitIndex >= nBitOffset && vecData.size() < static_cast<size_t>(nBitCount)) {
+ vecData.push_back((word & (1 << j)) != 0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+// 扩展读取字数据
+long CPerformanceMelsec::ReadWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nWordCount, WordContainer& vecData) {
+ long nRet = ValidateStationAndSize(station, static_cast<short>(nWordCount));
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ const long nByteSize = nWordCount * sizeof(short);
+
+ std::vector<char> vecRaw;
+ nRet = ReadDataEx(station, nDevType, nDevNo, nByteSize, vecRaw);
+ if (nRet != 0) {
+ return nRet;
+ }
+
+ vecData.clear();
+ for (long i = 0; i < nWordCount; ++i) {
+ short value = static_cast<unsigned char>(vecRaw[i * 2]) |
+ (static_cast<unsigned char>(vecRaw[i * 2 + 1]) << 8);
+ vecData.push_back(value);
+ }
+
+ return 0;
+}
+
+// 扩展读取双字数据
+long CPerformanceMelsec::ReadDWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nDWordCount, DWordContainer& vecData) {
+ long nRet = ValidateStationAndSize(station, static_cast<short>(nDWordCount));
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ const long nByteSize = nDWordCount * sizeof(uint32_t);
+
+ std::vector<char> vecRaw;
+ nRet = ReadDataEx(station, nDevType, nDevNo, nByteSize, vecRaw);
+ if (nRet != 0) {
+ return nRet;
+ }
+
+ vecData.clear();
+ for (long i = 0; i < nDWordCount; ++i) {
+ uint32_t val = static_cast<unsigned char>(vecRaw[i * 4 + 0]) |
+ (static_cast<unsigned char>(vecRaw[i * 4 + 1]) << 8) |
+ (static_cast<unsigned char>(vecRaw[i * 4 + 2]) << 16) |
+ (static_cast<unsigned char>(vecRaw[i * 4 + 3]) << 24);
+ vecData.push_back(val);
+ }
+
+ return 0;
+}
+
+// 扩展写数据
+long CPerformanceMelsec::WriteDataEx(const StationIdentifier& station, long nDevType, long nDevNo, const std::vector<char>& vecData) {
+ // 验证站点参数和数据有效性
+ long nRet = ValidateStationAndData(station, vecData);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 将 vecData 转换为 short 类型的缓冲区
+ long nSize = static_cast<long>(vecData.size());
+ nSize = nSize % 2 != 0 ? nSize + 1 : nSize;
+ std::vector<short> vecBuffer(nSize / 2, 0);
+
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ ConvertCharToShort(vecData, vecBuffer);
+ nRet = mdSendEx(m_nPath, station.nNetNo, station.nStNo, nDevType, nDevNo, &nSize, vecBuffer.data());
+ }
+
+ // 错误处理和日志记录
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 扩展写位数据
+long CPerformanceMelsec::WriteBitDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const BitContainer& vecData) {
+ long nRet = ValidateStationAndData(station, vecData);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const short nDevType = CalculateDeviceType(station, enDevType);
+
+ // === 1. 自动对齐起始地址 ===
+ long nWordAlignedStartBit = nDevNo / 16 * 16;
+ long nBitOffset = nDevNo - nWordAlignedStartBit;
+ long nTotalBits = nBitOffset + static_cast<long>(vecData.size());
+ size_t nWordCount = (nTotalBits + 15) / 16;
+
+ // === 2. 先读取原始值以支持非对齐覆盖 ===
+ std::vector<char> vecRaw;
+ nRet = ReadDataEx(station, nDevType, nWordAlignedStartBit, static_cast<long>(nWordCount * sizeof(short)), vecRaw);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // === 3. 合并新数据 ===
+ std::vector<short> vecWordBuffer(nWordCount, 0);
+ for (size_t i = 0; i < nWordCount; ++i) {
+ vecWordBuffer[i] = static_cast<unsigned char>(vecRaw[i * 2]) | (static_cast<unsigned char>(vecRaw[i * 2 + 1]) << 8);
+ }
+
+ for (size_t i = 0; i < vecData.size(); ++i) {
+ size_t bitIndex = nBitOffset + i;
+ size_t wordIdx = bitIndex / 16;
+ size_t bitPos = bitIndex % 16;
+ if (vecData[i]) {
+ vecWordBuffer[wordIdx] |= (1 << bitPos);
+ }
+ else {
+ vecWordBuffer[wordIdx] &= ~(1 << bitPos);
+ }
+ }
+
+ // === 4. 转为字节流写入 ===
+ std::vector<char> vecByteBuffer(nWordCount * 2);
+ for (size_t i = 0; i < nWordCount; ++i) {
+ vecByteBuffer[i * 2] = static_cast<char>(vecWordBuffer[i] & 0xFF);
+ vecByteBuffer[i * 2 + 1] = static_cast<char>((vecWordBuffer[i] >> 8) & 0xFF);
+ }
+
+ return WriteDataEx(station, nDevType, nWordAlignedStartBit, vecByteBuffer);
+}
+
+// 扩展写字数据
+long CPerformanceMelsec::WriteWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const WordContainer& vecData) {
+ long nRet = ValidateStationAndData(station, vecData);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ std::vector<char> vecByteBuffer;
+ vecByteBuffer.resize(vecData.size() * sizeof(short));
+
+ for (size_t i = 0; i < vecData.size(); ++i) {
+ vecByteBuffer[i * 2] = static_cast<char>(vecData[i] & 0xFF);
+ vecByteBuffer[i * 2 + 1] = static_cast<char>((vecData[i] >> 8) & 0xFF);
+ }
+
+ return WriteDataEx(station, nDevType, nDevNo, vecByteBuffer);
+}
+
+// 扩展写双字数据
+long CPerformanceMelsec::WriteDWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const DWordContainer& vecData) {
+ long nRet = ValidateStationAndData(station, vecData);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ std::vector<char> vecByteBuffer;
+ vecByteBuffer.resize(vecData.size() * sizeof(uint32_t));
+
+ for (size_t i = 0; i < vecData.size(); ++i) {
+ vecByteBuffer[i * 4] = static_cast<char>(vecData[i] & 0xFF);
+ vecByteBuffer[i * 4 + 1] = static_cast<char>((vecData[i] >> 8) & 0xFF);
+ vecByteBuffer[i * 4 + 2] = static_cast<char>((vecData[i] >> 16) & 0xFF);
+ vecByteBuffer[i * 4 + 3] = static_cast<char>((vecData[i] >> 24) & 0xFF);
+ }
+
+ return WriteDataEx(station, nDevType, nDevNo, vecByteBuffer);
+}
+
+// 扩展软元件随机读取
+long CPerformanceMelsec::ReadRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, std::vector<char>& vecData) {
+ if (vecSoftElements.empty()) {
+ UpdateLastError(ERROR_INVALID_PARAMETER);
+ LOG_ERROR("Invalid parameters: soft elements are empty.");
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ // 准备 dev 数据
+ std::vector<short> devBuffer(vecSoftElements.size() * 3 + 1, 0); // 每个软元件需要 3 个 short,外加一个计数器
+ devBuffer[0] = static_cast<short>(vecSoftElements.size()); // 第一个元素是软元件数量
+ for (size_t i = 0; i < vecSoftElements.size(); ++i) {
+ const SoftElement& element = vecSoftElements[i];
+ devBuffer[i * 3 + 1] = element.nType; // 软元件类型
+ devBuffer[i * 3 + 2] = static_cast<short>(element.nStartNo); // 起始软元件编号
+ devBuffer[i * 3 + 3] = element.nElementCount; // 点数
+ }
+
+ // 计算读取数据所需缓冲区大小
+ long nBufferSize = 0;
+ for (const auto& element : vecSoftElements) {
+ nBufferSize += element.nElementCount * 2; // 每个点占用 2 个字节
+ }
+
+ // 锁保护及调用 mdRandREx
+ long nRet = 0;
+ std::vector<short> vecBuffer(nBufferSize / 2, 0);
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 确保线程安全
+ nRet = mdRandREx(m_nPath, station.nNetNo, station.nStNo, devBuffer.data(), vecBuffer.data(), nBufferSize);
+ }
+
+ // 错误处理和日志记录
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ return nRet;
+ }
+
+ // 将读取到的 short 数据转换为 char 数据
+ vecData.resize(nBufferSize);
+ for (size_t i = 0; i < vecBuffer.size(); ++i) {
+ vecData[i * 2] = static_cast<char>(vecBuffer[i] & 0xFF); // 低字节
+ vecData[i * 2 + 1] = static_cast<char>((vecBuffer[i] >> 8) & 0xFF); // 高字节
+ }
+
+ return nRet;
+}
+
+// 扩展软元件随机写入(支持多个软元件)
+long CPerformanceMelsec::WriteRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, const std::vector<char>& vecData) {
+ if (vecSoftElements.empty() || vecData.empty()) {
+ UpdateLastError(ERROR_INVALID_PARAMETER);
+ LOG_ERROR("Invalid parameters: soft elements or data is empty.");
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ // 准备 dev 数据
+ std::vector<long> devBuffer(vecSoftElements.size() * 3 + 1, 0); // 每个软元件需要 3 个 long,外加一个计数器
+ devBuffer[0] = static_cast<long>(vecSoftElements.size()); // 第一个元素是软元件数量
+ for (size_t i = 0; i < vecSoftElements.size(); ++i) {
+ const SoftElement& element = vecSoftElements[i];
+ devBuffer[i * 3 + 1] = static_cast<long>(element.nType); // 软元件类型
+ devBuffer[i * 3 + 2] = element.nStartNo; // 起始软元件编号(已经是 long 类型,无需转换)
+ devBuffer[i * 3 + 3] = static_cast<long>(element.nElementCount); // 点数
+ }
+
+ // 锁保护及调用 mdRandWEx
+ long nRet = 0;
+ std::vector<short> vecBuffer(vecData.size() / 2, 0);
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 确保线程安全
+ ConvertCharToShort(vecData, vecBuffer);
+ nRet = mdRandWEx(m_nPath, station.nNetNo, station.nStNo, devBuffer.data(), vecBuffer.data(), static_cast<long>(vecBuffer.size()));
+ }
+
+ // 错误处理和日志记录
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 远程设备站/远程站的缓冲存储器读取
+long CPerformanceMelsec::ReadRemoteBuffer(const StationIdentifier& station, long nOffset, long nSize, std::vector<char>& vecData) {
+ // 验证站点参数和数据有效性
+ int nRet = ValidateStation(station);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ if (nSize < 0) {
+ UpdateLastError(ERROR_CODE_INVALID_PARAM);
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ long nActualSize = (nSize + 1) / 2;
+ std::vector<short> vecBuffer(nActualSize, 0);
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ nRet = mdRemBufReadEx(m_nPath, station.nNetNo, station.nStNo, nOffset, &nActualSize, vecBuffer.data());
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet); // 更新错误码
+ LOG_ERROR(m_strLastError);
+ }
+ else {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ ConvertShortToChar(vecBuffer, vecData);
+ }
+
+ return nRet;
+}
+
+// 远程设备站/远程站的缓冲存储器写入
+long CPerformanceMelsec::WriteRemoteBuffer(const StationIdentifier& station, long nOffset, const std::vector<char>& vecData) {
+ // 验证站点参数和数据有效性
+ long nRet = ValidateStationAndData(station, vecData);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 将 vecData 转换为 short 类型的缓冲区
+ long nSize = static_cast<long>(vecData.size());
+ std::vector<short> vecBuffer((nSize + 1) / 2, 0);
+
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ ConvertCharToShort(vecData, vecBuffer);
+ nRet = mdRemBufWriteEx(m_nPath, station.nNetNo, station.nStNo, nOffset, &nSize, vecBuffer.data());
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 远程站的缓冲存储器读取 对象站IP地址指定
+long CPerformanceMelsec::ReadRemoteBufferByIp(const std::string& strIP, long nOffset, long nSize, std::vector<char>& vecData) {
+ uint32_t nAddress = 0;
+ if (nSize < 0 || !ConvertIpStringToUint32(strIP, nAddress)) {
+ UpdateLastError(ERROR_CODE_INVALID_PARAM);
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ // 将缓冲区大小调整为 nSize
+ vecData.resize(nSize, 0);
+ std::vector<short> vecBuffer((nSize + 1) / 2, 0); // 转换为 short 类型
+
+ // 调用底层 SDK
+ long nRet = 0;
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ nRet = mdRemBufReadIPEx(m_nPath, static_cast<long>(nAddress), nOffset, &nSize, vecBuffer.data());
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+ else {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
+ ConvertShortToChar(vecBuffer, vecData);
+ }
+
+ return nRet;
+}
+
+// 远程站的缓冲存储器写入 对象站IP地址指定
+long CPerformanceMelsec::WriteRemoteBufferByIp(const std::string& strIP, long nOffset, const std::vector<char>& vecData) {
+ uint32_t nAddress = 0;
+ if (vecData.empty() || !ConvertIpStringToUint32(strIP, nAddress)) {
+ UpdateLastError(ERROR_CODE_INVALID_PARAM);
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ // 转换 vecData 为 short 类型的缓冲区
+ long nSize = static_cast<long>(vecData.size());
+ std::vector<short> vecBuffer((nSize + 1) / 2, 0);
+
+ long nRet = 0;
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全
+ ConvertCharToShort(vecData, vecBuffer);
+ nRet = mdRemBufWriteIPEx(m_nPath, static_cast<long>(nAddress), nOffset, &nSize, vecBuffer.data());
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 设置(ON)对象站的指定位软元件
+int CPerformanceMelsec::SetBitDevice(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo) {
+ // 验证站点参数
+ int nRet = ValidateStation(station);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 确保线程安全的最小锁定范围
+ {
+ std::lock_guard<std::mutex> lock(m_mtx); // 线程安全
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ nRet = mdDevSet(m_nPath, CombineStation(station), nDevType, nDevNo);
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 复位(OFF)对象站的指定位软元件
+int CPerformanceMelsec::ResetBitDevice(const StationIdentifier& station, const DeviceType enDevType, const short enDevNo) {
+ // 验证站点参数
+ int nRet = ValidateStation(station);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 确保线程安全的最小锁定范围
+ {
+ std::lock_guard<std::mutex> lock(m_mtx);
+ const short nDevType = CalculateDeviceType(station, enDevType);
+ nRet = mdDevRst(m_nPath, CombineStation(station), nDevType, enDevNo);
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 扩展位软元件设置
+long CPerformanceMelsec::SetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo) {
+ std::lock_guard<std::mutex> lock(m_mtx);
+
+ // 检查参数有效性
+ long nRet = ValidateStation(station);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ long nDevType = static_cast<long>(enDevType);
+ nRet = mdDevSetEx(m_nPath, station.nNetNo, station.nStNo, nDevType, nDevNo);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 扩展位软元件复位
+long CPerformanceMelsec::ResetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo) {
+ std::lock_guard<std::mutex> lock(m_mtx);
+
+ // 检查参数有效性
+ long nRet = ValidateStation(station);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ long nDevType = static_cast<long>(enDevType);
+ nRet = mdDevRstEx(m_nPath, station.nNetNo, station.nStNo, nDevType, nDevNo);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 执行对象站的CPU
+int CPerformanceMelsec::ControlCPU(const StationIdentifier& station, ControlCode enControlCode) {
+ // 验证站点参数和数据有效性
+ int nRet = ValidateStation(station);
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ return nRet;
+ }
+
+ // 验证控制码是否合法
+ const auto nControlCode = static_cast<short>(enControlCode);
+ if (nControlCode < 0 || nControlCode > 2) {
+ UpdateLastError(ERROR_CODE_INVALID_PARAM); // 参数错误
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ // 确保线程安全的最小锁定范围
+ {
+ std::lock_guard<std::mutex> lock(m_mtx);
+ nRet = mdControl(m_nPath, CombineStation(station), nControlCode);
+ }
+
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+// 事件等待
+int CPerformanceMelsec::WaitForBoardEvent(std::vector<short> vecEventNumbers, const int nTimeoutMs, EventDetails& details) {
+ std::lock_guard<std::mutex> lock(m_mtx);
+
+ if (!m_bConnected.load()) {
+ UpdateLastError(ERROR_CODE_NOT_CONNECTED);
+ return ERROR_CODE_NOT_CONNECTED;
+ }
+
+ if (vecEventNumbers.empty() || vecEventNumbers.size() > 64) {
+ UpdateLastError(ERROR_CODE_INVALID_PARAM);
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ // 第 0 个元素存储数量,最大支持 64 个事件
+ std::array<short, 65> eventno = { 0 };
+ eventno[0] = static_cast<short>(vecEventNumbers.size());
+ std::copy(vecEventNumbers.begin(), vecEventNumbers.end(), eventno.begin() + 1);
+
+ // 初始化输出参数
+ details.nEventNo = 0;
+ details.details.fill(0);
+
+ const int nRet = mdWaitBdEvent(m_nPath, eventno.data(), nTimeoutMs, &details.nEventNo, details.details.data());
+ if (nRet != 0) {
+ UpdateLastError(nRet);
+ LOG_ERROR(m_strLastError);
+ }
+
+ return nRet;
+}
+
+//============================================辅助函数=======================================================
+// 更新最近的错误信息
+void CPerformanceMelsec::UpdateLastError(const int nCode) {
+ if (nCode == 0) {
+ return;
+ }
+
+ // 检查错误码是否存在于映射表中
+ const auto it = m_mapError.find(nCode);
+ if (it != m_mapError.end()) {
+ // 如果找到,直接返回对应语言的错误信息
+ m_strLastError = it->second;
+ }
+ else {
+ // 如果未找到,处理特殊范围
+ m_strLastError = "Unknown error.";
+ if (nCode == -28611 || nCode == -28612) {
+ // 系统出错
+ m_strLastError = "System error.";
+ }
+
+ if (nCode >= -20480 && nCode <= -16384) {
+ // CC-Link 系统检测出的错误
+ m_strLastError = "Error detected in the CC-Link system.";
+ }
+
+ if (nCode >= -12288 && nCode <= -8193) {
+ // CC-Link IE TSN 系统检测出的错误
+ m_strLastError = "Error detected in the CC-Link IE TSN system.";
+ }
+
+ if (nCode >= -8192 && nCode <= -4097) {
+ // CC-Link IE 控制网络系统检测出的错误
+ m_strLastError = "Error detected in the CC-Link IE control network system.";
+ }
+
+ if (nCode >= -4096 && nCode <= -257) {
+ // MELSECNET/10 或 MELSECNET/网络系统错误范围
+ m_strLastError = "Errors detected in MELSECNET/10 or MELSECNET/network system.";
+ }
+
+ if (nCode >= 4096 && nCode <= 16383) {
+ // MELSEC 数据链接库范围
+ m_strLastError = "Internal error detected by MELSEC Data Link Library.";
+ }
+
+ if (nCode == 18944 || nCode == 18945) {
+ // 链接关联出错
+ m_strLastError = "Link association error: Network does not exist, unsupported CPU, or incorrect network No./station number.";
+ }
+
+ if (nCode >= 16384 && nCode <= 20479) {
+ // PLC CPU 检测范围
+ m_strLastError = "Errors detected by the programmable controller CPU in the target station.";
+ }
+
+ if (nCode >= 28416 && nCode <= 28671) {
+ // 冗余功能模块范围
+ m_strLastError = "Error detected in the redundancy module of the target station.";
+ }
+ }
+}
+
+// 检查连接状态和站点参数有效性
+int CPerformanceMelsec::ValidateStation(const StationIdentifier& station) const {
+ // 检查是否已连接
+ if (!m_bConnected.load()) {
+ return ERROR_CODE_NOT_CONNECTED;
+ }
+
+ // 检查网络号和站点号范围
+ if (station.nNetNo < 0 || station.nNetNo > 239 || station.nStNo < 0 || station.nStNo > 255) {
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ return 0; // 参数有效
+}
+
+// 验证站点参数和数据有效性
+int CPerformanceMelsec::ValidateStationAndSize(const StationIdentifier& station, const short nCount) const {
+ // 验证站点参数
+ const int nRet = ValidateStation(station);
+ if (nRet != 0) {
+ return nRet; // 如果站点验证失败,返回对应错误码
+ }
+
+ if (nCount <= 0) {
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ return 0; // 验证通过
+}
+
+// IP字符串转uint32_t
+bool CPerformanceMelsec::ConvertIpStringToUint32(const std::string& strIP, uint32_t& nIP) {
+ nIP = 0;
+ std::stringstream ss(strIP);
+ std::string strSegment;
+ int nShift = 24;
+
+ while (std::getline(ss, strSegment, '.')) {
+ const auto nByte = static_cast<uint32_t>(std::stoi(strSegment));
+ if (nByte > 255) {
+ return false;
+ }
+ nIP |= (nByte << nShift);
+ nShift -= 8;
+ }
+
+ return true;
+}
+
+//============================================静态辅助函数====================================================
+// 延时,并且转发窗口消息
+void CPerformanceMelsec::Delay(const unsigned int nDelayMs) {
+ MSG message;
+ // 如果延迟时间为 0,仅处理一次消息队列
+ if (nDelayMs == 0) {
+ // 非阻塞的检查消息队列
+ if (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&message); // 将消息转化为有效的窗口消息
+ DispatchMessage(&message); // 派发消息给相应的窗口过程
+ }
+ return;
+ }
+
+ DWORD finish;
+ const DWORD start = GetTickCount(); // 获取当前的时间戳(从系统启动以来的毫秒数)
+ do {
+ if (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&message); // 转换消息
+ DispatchMessage(&message); // 处理消息
+ }
+ Sleep(1); // 暂停 1 毫秒,防止过度占用 CPU
+ finish = GetTickCount(); // 获取当前的时间戳
+ } while ((finish - start) < nDelayMs); // 循环直到经过的时间大于指定的延迟时间
+}
+
+BoardType CPerformanceMelsec::FindBoardTypeByChannel(const int nChannel) {
+ if (nChannel >= MELSECNET_CHANNEL(1) && nChannel <= MELSECNET_CHANNEL(4)) {
+ return BoardType::MELSECNET_H;
+ }
+ else if (nChannel >= CC_LINK_CHANNEL(1) && nChannel <= CC_LINK_CHANNEL(4)) {
+ return BoardType::CC_LINK_VER_2;
+ }
+ else if (nChannel >= CC_LINK_IE_CONTROL_CHANNEL(1) && nChannel <= CC_LINK_IE_CONTROL_CHANNEL(4)) {
+ return BoardType::CC_LINK_IE_CONTROL;
+ }
+ else if (nChannel >= CC_LINK_IE_FIELD_CHANNEL(1) && nChannel <= CC_LINK_IE_FIELD_CHANNEL(4)) {
+ return BoardType::CC_LINK_IE_FIELD;
+ }
+ else if (nChannel >= CC_LINK_IE_TSN_CHANNEL(1) && nChannel <= CC_LINK_IE_TSN_CHANNEL(4)) {
+ return BoardType::CC_LINK_IE_TSN;
+ }
+ return BoardType::UNKNOWN;
+}
+
+// 合并网络号和站点号
+short CPerformanceMelsec::CombineStation(const StationIdentifier& station) {
+ return static_cast<short>(station.nStNo | ((station.nNetNo << 8) & 0xFF00));
+}
+
+// 计算软元件类型
+short CPerformanceMelsec::CalculateDeviceType(const StationIdentifier& station, DeviceType enDevType) {
+ int nDevType = static_cast<int>(enDevType);
+
+ // 根据软元件类型的特定规则进行计算
+ if (enDevType == DeviceType::LX || enDevType == DeviceType::LY ||
+ enDevType == DeviceType::LB || enDevType == DeviceType::LW ||
+ enDevType == DeviceType::LSB || enDevType == DeviceType::LSW) {
+ // 网络号加偏移
+ nDevType += station.nNetNo;
+ }
+ else if (enDevType == DeviceType::ER) {
+ // 文件寄存器的块号加偏移
+ nDevType += 0;
+ }
+ else if (enDevType == DeviceType::SPG) {
+ // 起始 I/O No. ÷ 16 的值
+ nDevType += 0 / 16;
+ }
+
+ return static_cast<short>(nDevType);
+}
+
+// std::vector<char>转换为std::vector<short>
+void CPerformanceMelsec::ConvertCharToShort(const std::vector<char>& vecChar, std::vector<short>& vecShort) {
+ vecShort.resize((vecChar.size() + 1) / 2, 0); // 调整 short 容器大小
+ for (size_t i = 0; i < vecChar.size(); i++) {
+ if (i % 2 == 0) {
+ vecShort[i / 2] = static_cast<unsigned char>(vecChar[i]); // 低字节
+ }
+ else {
+ vecShort[i / 2] |= static_cast<unsigned char>(vecChar[i]) << 8; // 高字节
+ }
+ }
+}
+
+// std::vector<short>转换为std::vector<char>
+void CPerformanceMelsec::ConvertShortToChar(const std::vector<short>& vecShort, std::vector<char>& vecChar) {
+ vecChar.resize(vecShort.size() * 2); // 调整 char 容器大小
+ for (size_t i = 0; i < vecShort.size(); i++) {
+ vecChar[i * 2] = static_cast<char>(vecShort[i] & 0xFF); // 低字节
+ vecChar[i * 2 + 1] = static_cast<char>((vecShort[i] >> 8) & 0xFF); // 高字节
+ }
+}
+
+// std::vector<uint8_t>转换为std::vector<short>
+void CPerformanceMelsec::ConvertUint8ToShort(const std::vector<uint8_t>& vecUint8, std::vector<short>& vecShort) {
+ vecShort.resize((vecUint8.size() + 1) / 2, 0); // 调整 short 容器大小
+ for (size_t i = 0; i < vecUint8.size(); i++) {
+ if (i % 2 == 0) {
+ vecShort[i / 2] = static_cast<short>(vecUint8[i]); // 低字节
+ }
+ else {
+ vecShort[i / 2] |= static_cast<short>(vecUint8[i] << 8); // 高字节
+ }
+ }
+}
+
+// std::vector<short>转换为std::vector<uint8_t>
+void CPerformanceMelsec::ConvertShortToUint8(const std::vector<short>& vecShort, std::vector<uint8_t>& vecUint8) {
+ vecUint8.resize(vecShort.size() * 2); // 调整 uint8_t 容器大小
+ for (size_t i = 0; i < vecShort.size(); i++) {
+ vecUint8[i * 2] = static_cast<uint8_t>(vecShort[i] & 0xFF); // 低字节
+ vecUint8[i * 2 + 1] = static_cast<uint8_t>((vecShort[i] >> 8) & 0xFF); // 高字节
+ }
+}
+
+// std::vector<uint32_t>转换为std::vector<short>
+void CPerformanceMelsec::ConvertUint32ToShort(const std::vector<uint32_t>& vecUint32, std::vector<short>& vecShort) {
+ vecShort.resize(vecUint32.size() * 2); // 每个 uint32_t 转换为两个 short
+ for (size_t i = 0; i < vecUint32.size(); i++) {
+ vecShort[i * 2] = static_cast<short>(vecUint32[i] & 0xFFFF); // 低16位
+ vecShort[i * 2 + 1] = static_cast<short>((vecUint32[i] >> 16) & 0xFFFF); // 高16位
+ }
+}
+
+// std::vector<short>转换为std::vector<uint32_t>
+void CPerformanceMelsec::ConvertShortToUint32(const std::vector<short>& vecShort, std::vector<uint32_t>& vecUint32) {
+ vecUint32.resize((vecShort.size() + 1) / 2, 0); // 每两个 short 合并为一个 uint32_t
+ for (size_t i = 0; i < vecUint32.size(); i++) {
+ vecUint32[i] = (static_cast<uint32_t>(static_cast<uint16_t>(vecShort[i * 2 + 1])) << 16) | // 高16位
+ static_cast<uint32_t>(static_cast<uint16_t>(vecShort[i * 2])); // 低16位
+ }
+}
+
+//============================================模板辅助函数====================================================
+// 验证站点参数和数据有效性
+template <typename T>
+int CPerformanceMelsec::ValidateStationAndData(const StationIdentifier& station, const std::vector<T>& vecData) {
+ // 验证站点参数
+ const int nRet = ValidateStation(station);
+ if (nRet != 0) {
+ return nRet; // 如果站点验证失败,返回对应错误码
+ }
+
+ // 验证数据是否为空
+ if (vecData.empty()) {
+ return ERROR_CODE_INVALID_PARAM;
+ }
+
+ return 0; // 验证通过
+}
+
+// 由低转高容器的模板(整型)
+template <typename T, typename U>
+void CPerformanceMelsec::ConvertLowToHigh(const std::vector<T>& vecLow, std::vector<U>& vecHigh) {
+ static_assert(std::is_integral<T>::value && std::is_integral<U>::value, "T and U must be integral types");
+
+ // 自动计算 nGroupSize
+ constexpr size_t nGroupSize = sizeof(U) / sizeof(T);
+
+ // 如果 T 和 U 的大小相等,直接转换
+ if (sizeof(T) == sizeof(U)) {
+ vecHigh.assign(vecLow.begin(), vecLow.end());
+ return;
+ }
+
+ // 如果 U 的大小是 T 的倍数,正常组合
+ static_assert(sizeof(U) > sizeof(T), "Size of U must be greater than or equal to size of T");
+
+ // 计算完整组的数量
+ size_t nHighSize = (vecLow.size() + nGroupSize - 1) / nGroupSize; // 向上取整
+ vecHigh.resize(nHighSize, 0);
+
+ // 合并低位数据到高位数据
+ for (size_t i = 0; i < vecLow.size(); i++) {
+ vecHigh[i / nGroupSize] |= (static_cast<U>(vecLow[i]) << ((i % nGroupSize) * CHAR_BIT * sizeof(T)));
+ }
+
+ return vecHigh;
+}
+
+// 由高转低容器的模板(整型)
+template <typename T, typename U>
+void CPerformanceMelsec::ConvertHighToLow(const std::vector<T>& vecHigh, std::vector<U>& vecLow) {
+ static_assert(std::is_integral<T>::value && std::is_integral<U>::value, "T and U must be integral types");
+
+ // 自动计算 nGroupSize
+ constexpr size_t nGroupSize = sizeof(T) / sizeof(U);
+
+ // 如果 T 和 U 的大小相等,直接转换
+ if (sizeof(T) == sizeof(U)) {
+ vecLow.assign(vecHigh.begin(), vecHigh.end());
+ return;
+ }
+
+ // 如果 T 的大小是 U 的倍数,正常分解
+ static_assert(sizeof(T) > sizeof(U), "Size of T must be greater than or equal to size of U");
+
+ size_t nLowSize = vecHigh.size() * nGroupSize; // 低容器的大小
+ vecLow.resize(nLowSize, 0);
+
+ // 分解高位数据到低位数据
+ for (size_t i = 0; i < vecHigh.size(); i++) {
+ for (size_t j = 0; j < nGroupSize; j++) {
+ vecLow[i * nGroupSize + j] = static_cast<U>((vecHigh[i] >> (j * CHAR_BIT * sizeof(U))) & ((1ULL << (CHAR_BIT * sizeof(U))) - 1));
+ }
+ }
+
+ return vecLow;
+}
\ No newline at end of file
--
Gitblit v1.9.3