LAPTOP-SNT8I5JK\Boounion
2025-02-18 5efff0895ad06eb0c5f04df7efe67a22b358beb3
Merge branch 'liuyang' into clh

# Conflicts:
# SourceCode/Bond/Servo/Model.cpp
已修改11个文件
833 ■■■■■ 文件已修改
SourceCode/Bond/Servo/CCLinkPerformance/PerformanceMelsec.cpp 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CCLinkPerformance/PerformanceMelsec.h 444 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipment.cpp 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipment.h 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CMaster.cpp 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Common.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Model.cpp 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/SECSRuntimeManager.cpp 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/SECSRuntimeManager.h 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoDlg.cpp 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoDlg.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CCLinkPerformance/PerformanceMelsec.cpp
@@ -483,15 +483,33 @@
        return nRet;
    }
    // 计算需要读取的字节大小(按位对齐为字节数)
    if (nDevNo % 8 != 0) {
        nRet = -2;
        UpdateLastError(nRet);
        return nRet;
    }
    const short nDevType = CalculateDeviceType(station, enDevType);
    const auto nSize = static_cast<short>((nBitCount + 7) / 8);     // 向上取整
    std::vector<short> vecTempBuffer((nSize + 1) / 2, 0); // 临时缓冲区,字节对齐
    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) {
        std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
        ConvertShortToUint8(vecTempBuffer, vecData);
        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;
@@ -573,22 +591,35 @@
// 写位数据
int CPerformanceMelsec::WriteBitData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const BitContainer& vecData) {
    // 验证站点参数和数据有效性
    const int nRet = ValidateStationAndData(station, vecData);
    int nRet = ValidateStationAndData(station, vecData);
    if (nRet != 0) {
        UpdateLastError(nRet);
        return nRet;
    }
    // 计算需要写入的字节数(位数据需要按 8 位对齐为字节数)
    const short nDevType = CalculateDeviceType(station, enDevType);
    const auto nSize = static_cast<short>((vecData.size() + 7) / 8);
    std::vector<short> vecBuffer(vecData.size() / 2 + vecData.size() % 2, 0);
    {
        std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护
        ConvertUint8ToShort(vecData, vecBuffer);
    if (nDevNo % 8 != 0) {
        nRet = -2;
        UpdateLastError(nRet);
        return nRet;
    }
    return WriteData(station, nDevType, nDevNo, nSize, vecBuffer.data());
    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());
}
// 写字数据
SourceCode/Bond/Servo/CCLinkPerformance/PerformanceMelsec.h
@@ -10,151 +10,151 @@
#include <sstream>
#include <unordered_map>
// 连接参数
#define PLC_MAX_RETRY 3        // 最大重试次数:在与PLC通信时,如果发生通信错误,将最多重试3次
#define PLC_TIMEOUT 500        // 超时时间(毫秒):每次通信操作的超时等待时间为500毫秒
// 连接参数
#define PLC_MAX_RETRY 3        // 最大重试次数:在与PLC通信时,如果发生通信错误,将最多重试3次
#define PLC_TIMEOUT 500        // 超时时间(毫秒):每次通信操作的超时等待时间为500毫秒
/*
 * 网络通道:指定通信所使用的网络通道号,通常在多通道通信中设置
 * 51 到 54 是 MELSECNET/H 的 1-4 通道
 * 81 到 84 是 CC-Link 的 1-4 通道
 * 151 到 154 是 CC-Link IE 控制器网络的 1-4 通道
 * 181 到 184 是 CC-Link IE 现场网络的 1-4 通道
 * 281 到 284 是 CC-Link IE TSN 网络的 1-4 通道
 * 网络通道:指定通信所使用的网络通道号,通常在多通道通信中设置
 * 51 到 54 是 MELSECNET/H 的 1-4 通道
 * 81 到 84 是 CC-Link 的 1-4 通道
 * 151 到 154 是 CC-Link IE 控制器网络的 1-4 通道
 * 181 到 184 是 CC-Link IE 现场网络的 1-4 通道
 * 281 到 284 是 CC-Link IE TSN 网络的 1-4 通道
 **/
#define MELSECNET_CHANNEL(x) (50 + (x))           // x 范围:1~4
#define CC_LINK_CHANNEL(x) (80 + (x))              // x 范围:1~4
#define CC_LINK_IE_CONTROL_CHANNEL(x) (150 + (x)) // x 范围:1~4
#define CC_LINK_IE_FIELD_CHANNEL(x) (180 + (x))   // x 范围:1~4
#define CC_LINK_IE_TSN_CHANNEL(x) (280 + (x))     // x 范围:1~4
#define MELSECNET_CHANNEL(x) (50 + (x))           // x 范围:1~4
#define CC_LINK_CHANNEL(x) (80 + (x))              // x 范围:1~4
#define CC_LINK_IE_CONTROL_CHANNEL(x) (150 + (x)) // x 范围:1~4
#define CC_LINK_IE_FIELD_CHANNEL(x) (180 + (x))   // x 范围:1~4
#define CC_LINK_IE_TSN_CHANNEL(x) (280 + (x))     // x 范围:1~4
// 自定义错误码
#define ERROR_CODE_UNKNOWN                0x00010000 // δ֪
#define ERROR_CODE_NOT_CONNECTED        0x00020000 // 未连接
#define ERROR_CODE_INVALID_PARAM        0x00030000 // 参数无效
#define ERROR_CODE_INVALID_DATA            0x00040000 // 数据无效
#define ERROR_CODE_STATION_OUT_OF_RANGE 0x00050000 // 站号超出范围
#define ERROR_CODE_GROUP_OUT_OF_RANGE   0x00060000 // 组号超出范围
#define ERROR_CODE_NETWORK_OUT_OF_RANGE 0x00070000 // 网络号超出范围
 // 自定义错误码
#define ERROR_CODE_UNKNOWN                0x00010000 // 未知
#define ERROR_CODE_NOT_CONNECTED        0x00020000 // 未连接
#define ERROR_CODE_INVALID_PARAM        0x00030000 // 参数无效
#define ERROR_CODE_INVALID_DATA            0x00040000 // 数据无效
#define ERROR_CODE_STATION_OUT_OF_RANGE 0x00050000 // 站号超出范围
#define ERROR_CODE_GROUP_OUT_OF_RANGE   0x00060000 // 组号超出范围
#define ERROR_CODE_NETWORK_OUT_OF_RANGE 0x00070000 // 网络号超出范围
// 板块类型
// 板块类型
enum class BoardType {
    UNKNOWN = -1,                                        // 未知类型
    UNKNOWN = -1,                                        // 未知类型
    MELSECNET_H = MELSECNET_CHANNEL(1),                    // MELSECNET/H
    CC_LINK_VER_2 = CC_LINK_CHANNEL(1),                    // CC-Link Ver. 2
    CC_LINK_IE_CONTROL = CC_LINK_IE_CONTROL_CHANNEL(1),    // CC-Link IE 控制网络
    CC_LINK_IE_FIELD = CC_LINK_IE_FIELD_CHANNEL(1),     // CC-Link IE 现场网络
    CC_LINK_IE_CONTROL = CC_LINK_IE_CONTROL_CHANNEL(1),    // CC-Link IE 控制网络
    CC_LINK_IE_FIELD = CC_LINK_IE_FIELD_CHANNEL(1),     // CC-Link IE 现场网络
    CC_LINK_IE_TSN = CC_LINK_IE_TSN_CHANNEL(1)          // CC-Link IE TSN
};
// 软元件类型枚举
// 软元件类型枚举
enum class DeviceType {
    /*
     * ER、LX、LY、LB、LW、LSB、LSW和SPG软元件都是范围型
     * ER:DevER0~256
     * LX:DevLX1~255,DevLX(x)    (DevX*1000+(x))
     * LY:DevLY1~255,DevLY(x)    (DevY*1000+(x))
     * LB:DevLB1~255,DevLB(x)    (DevB*1000+(x))
     * LW:DevLW1~255,DevLW(x)    (DevW*1000+(x))
     * LSB:DevLSB1~255,DevLSB(x) (DevQSB*1000+(x))
     * LSW:DevLSW1~255,DevLSW(x) (DevQSW*1000+(x))
     * SPG:DevSPG0~255,DevSPG(x) (29*1000+(x))
     * 扩展文件寄存器代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定块No.(0~256)
     * 链接直接软元件代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定网络No.(1~255)
     * 智能功能模块软元件代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定(起始I/ONo.÷16)的值
     * 扩展文件寄存器和链接直接软元件在随机读取(mdRandR、mdRandREx)函数中,即使指定实际不存在的软元件也有可能正常结束
     * MAIL和MAILMC在SEND功能及RECV功能中,与软元件访问一样,指定各功能对应的软元件类型,进行数据的发送(mdSend、mdSendEx)或数据的读取(mdReceive、mdReceiveEx)
     * ER、LX、LY、LB、LW、LSB、LSW和SPG软元件都是范围型
     * ER:DevER0~256
     * LX:DevLX1~255,DevLX(x)    (DevX*1000+(x))
     * LY:DevLY1~255,DevLY(x)    (DevY*1000+(x))
     * LB:DevLB1~255,DevLB(x)    (DevB*1000+(x))
     * LW:DevLW1~255,DevLW(x)    (DevW*1000+(x))
     * LSB:DevLSB1~255,DevLSB(x) (DevQSB*1000+(x))
     * LSW:DevLSW1~255,DevLSW(x) (DevQSW*1000+(x))
     * SPG:DevSPG0~255,DevSPG(x) (29*1000+(x))
     * 扩展文件寄存器代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定块No.(0~256)
     * 链接直接软元件代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定网络No.(1~255)
     * 智能功能模块软元件代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定(起始I/ONo.÷16)的值
     * 扩展文件寄存器和链接直接软元件在随机读取(mdRandR、mdRandREx)函数中,即使指定实际不存在的软元件也有可能正常结束
     * MAIL和MAILMC在SEND功能及RECV功能中,与软元件访问一样,指定各功能对应的软元件类型,进行数据的发送(mdSend、mdSendEx)或数据的读取(mdReceive、mdReceiveEx)
     **/
    X = 0x0001,      // 输入 (位)
    Y = 0x0002,      // 输出 (位)
    L = 0x0003,      // 锁存继电器 (位)
    M = 0x0004,      // 内部继电器 (位)
    SM = 0x0005,     // 特殊继电器 (位)
    F = 0x0006,      // 报警器 (位)
    TT = 0x0007,     // 定时器 (触点) (位)
    TC = 0x0008,     // 计数器 (线圈) (位)
    CT = 0x0009,     // 计数器 (触点) (位)
    CC = 0x000A,     // 计数器 (线圈) (字)
    TN = 0x000B,     // 定时器 (当前值) (字)
    CN = 0x000C,     // 计数器 (当前值) (字)
    D = 0x000D,      // 数据寄存器 (字)
    SD = 0x000E,     // 特殊寄存器 (字)
    TM = 0x000F,     // 定时器 (设置值主) (字)
    TS = 0x0010,     // 定时器 (设置值主1) (字)
    TS2 = 0x3E82,    // 定时器 (设置值主2) (字)
    TS3 = 0x3E83,    // 定时器 (设置值主3) (字)
    CM = 0x0011,     // 计数器 (设置值主) (字)
    CS = 0x0012,     // 计数器 (设置值主1) (字)
    CS2 = 0x4652,    // 计数器 (设置值主2) (字)
    CS3 = 0x4653,    // 计数器 (设置值主3) (字)
    A = 0x0013,      // 累加器 (字)
    Z = 0x0014,      // 变址寄存器 (字)
    V = 0x0015,      // 变址寄存器 (字)
    R = 0x0016,      // 文件寄存器 (块切换方式) (字)
    ER = 0x55F0,     // 扩展文件寄存器 (块切换方式) (0x55F0~0x56F0) (字) (在随机读取(mdRandR、mdRandREx)函数中,即使指定实际不存在的软元件也有可能正常结束。(读取数据不正确。))
    ZR = 0x00DC,     // 文件寄存器 (连号访问方式) (字)
    B = 0x0017,      // 链接继电器 (位)
    W = 0x0018,      // 链接寄存器 (字)
    QSB = 0x0019,    // 链接特殊继电器 (位)
    STT = 0x001A,    // 累计定时器 (触点) (位)
    STC = 0x001B,    // 累计定时器 (线圈) (位)
    QSW = 0x001C,    // 链接特殊寄存器 (字)
    QV = 0x001E,     // 变址继电器 (位)
    MRB = 0x0021,     // 随机访问缓冲 (字)
    STN = 0x0023,    // 累计定时器 (当前值) (字)
    LZ = 0x0026,     // 超长变址寄存器 (双字)
    RD = 0x0027,     // 刷新数据寄存器 (字)
    LTT = 0x0029,    // 超长定时器 (触点) (位)
    LTC = 0x002A,    // 超长定时器 (线圈) (位)
    LTN = 0x002B,    // 超长定时器 (当前值) (双字)
    LCT = 0x002C,    // 超长计数器 (触点) (位)
    LCC = 0x002D,    // 超长计数器 (线圈) (位)
    LCN = 0x002E,    // 超长计数器 (当前值) (双字)
    LSTT = 0x002F,   // 超长累计定时器 (触点) (位)
    LSTC = 0x0030,   // 超长累计定时器 (线圈) (位)
    LSTN = 0x0031,   // 超长累计定时器 (当前值) (双字)
    SPB = 0x0032,     // 缓冲存储器 (字)
    MAIL = 0x0065,   // 特殊软元件类型:邮件类型 (10进制 101)
    MAILMC = 0x0066, // 特殊软元件类型:无确认邮件 (10进制 102)
    LX = 0x03E9,     // 链接直接软元件 (链接输入) (0x03E9~0x04E7) (位)
    LY = 0x07D1,     // 链接直接软元件 (链接输出) (0x07D1~0x08CF) (位)
    LB = 0x59D9,     // 链接直接软元件 (链接继电器) (0x59D9~0x5AD7) (位)
    LW = 0x5DC1,     // 链接直接软元件 (链接寄存器) (0x5DC1~0x5EBF) (字)
    LSB = 0x61A9,    // 链接直接软元件 (链接特殊继电器) (0x61A9~0x62A7) (位)
    LSW = 0x6D61,    // 链接直接软元件 (链接特殊寄存器) (0x6D61~0x6E5F) (字)
    SPG = 0x7148,    // 智能功能模块软元件 (0x7148~0x7247) (字)
    X = 0x0001,      // 输入 (位)
    Y = 0x0002,      // 输出 (位)
    L = 0x0003,      // 锁存继电器 (位)
    M = 0x0004,      // 内部继电器 (位)
    SM = 0x0005,     // 特殊继电器 (位)
    F = 0x0006,      // 报警器 (位)
    TT = 0x0007,     // 定时器 (触点) (位)
    TC = 0x0008,     // 计数器 (线圈) (位)
    CT = 0x0009,     // 计数器 (触点) (位)
    CC = 0x000A,     // 计数器 (线圈) (字)
    TN = 0x000B,     // 定时器 (当前值) (字)
    CN = 0x000C,     // 计数器 (当前值) (字)
    D = 0x000D,      // 数据寄存器 (字)
    SD = 0x000E,     // 特殊寄存器 (字)
    TM = 0x000F,     // 定时器 (设置值主) (字)
    TS = 0x0010,     // 定时器 (设置值主1) (字)
    TS2 = 0x3E82,    // 定时器 (设置值主2) (字)
    TS3 = 0x3E83,    // 定时器 (设置值主3) (字)
    CM = 0x0011,     // 计数器 (设置值主) (字)
    CS = 0x0012,     // 计数器 (设置值主1) (字)
    CS2 = 0x4652,    // 计数器 (设置值主2) (字)
    CS3 = 0x4653,    // 计数器 (设置值主3) (字)
    A = 0x0013,      // 累加器 (字)
    Z = 0x0014,      // 变址寄存器 (字)
    V = 0x0015,      // 变址寄存器 (字)
    R = 0x0016,      // 文件寄存器 (块切换方式) (字)
    ER = 0x55F0,     // 扩展文件寄存器 (块切换方式) (0x55F0~0x56F0) (字) (在随机读取(mdRandR、mdRandREx)函数中,即使指定实际不存在的软元件也有可能正常结束。(读取数据不正确。))
    ZR = 0x00DC,     // 文件寄存器 (连号访问方式) (字)
    B = 0x0017,      // 链接继电器 (位)
    W = 0x0018,      // 链接寄存器 (字)
    QSB = 0x0019,    // 链接特殊继电器 (位)
    STT = 0x001A,    // 累计定时器 (触点) (位)
    STC = 0x001B,    // 累计定时器 (线圈) (位)
    QSW = 0x001C,    // 链接特殊寄存器 (字)
    QV = 0x001E,     // 变址继电器 (位)
    MRB = 0x0021,     // 随机访问缓冲 (字)
    STN = 0x0023,    // 累计定时器 (当前值) (字)
    LZ = 0x0026,     // 超长变址寄存器 (双字)
    RD = 0x0027,     // 刷新数据寄存器 (字)
    LTT = 0x0029,    // 超长定时器 (触点) (位)
    LTC = 0x002A,    // 超长定时器 (线圈) (位)
    LTN = 0x002B,    // 超长定时器 (当前值) (双字)
    LCT = 0x002C,    // 超长计数器 (触点) (位)
    LCC = 0x002D,    // 超长计数器 (线圈) (位)
    LCN = 0x002E,    // 超长计数器 (当前值) (双字)
    LSTT = 0x002F,   // 超长累计定时器 (触点) (位)
    LSTC = 0x0030,   // 超长累计定时器 (线圈) (位)
    LSTN = 0x0031,   // 超长累计定时器 (当前值) (双字)
    SPB = 0x0032,     // 缓冲存储器 (字)
    MAIL = 0x0065,   // 特殊软元件类型:邮件类型 (10进制 101)
    MAILMC = 0x0066, // 特殊软元件类型:无确认邮件 (10进制 102)
    LX = 0x03E9,     // 链接直接软元件 (链接输入) (0x03E9~0x04E7) (位)
    LY = 0x07D1,     // 链接直接软元件 (链接输出) (0x07D1~0x08CF) (位)
    LB = 0x59D9,     // 链接直接软元件 (链接继电器) (0x59D9~0x5AD7) (位)
    LW = 0x5DC1,     // 链接直接软元件 (链接寄存器) (0x5DC1~0x5EBF) (字)
    LSB = 0x61A9,    // 链接直接软元件 (链接特殊继电器) (0x61A9~0x62A7) (位)
    LSW = 0x6D61,    // 链接直接软元件 (链接特殊寄存器) (0x6D61~0x6E5F) (字)
    SPG = 0x7148,    // 智能功能模块软元件 (0x7148~0x7247) (字)
};
// 数据类型
// 数据类型
enum class DataType {
    BIT = 1,   // λ (1λ)
    WORD = 2,  // 字 (16位)
    DWORD =4   // 双字 (32位)
    BIT = 1,   // 位 (1位)
    WORD = 2,  // 字 (16位)
    DWORD = 4  // 双字 (32位)
};
// 控制代码
// 控制代码
enum class ControlCode {
    RUN = 0,   // 远程 RUN
    STOP = 1,  // 远程 STOP
    PAUSE = 2  // 远程 PAUSE
    RUN = 0,   // 远程 RUN
    STOP = 1,  // 远程 STOP
    PAUSE = 2  // 远程 PAUSE
};
// 版本信息
// 版本信息
struct BoardVersion {
    char fixedValue[2];       // 固定值
    char checksum[2];         // 校验和
    char swVersion[2];        // 软件版本
    char date[6];             // 日期 (格式 YYMMDD)
    uint32_t reserved;        // 保留区域 (4 字节)
    char swModel[16];         // 软件型号
    char hwModel[16];         // 硬件型号
    char twoPortMemory[2];    // 两端口存储器占用容量
    char twoPortAttribute[2]; // 两端口属性
    char availableBias[2];    // 可使用偏置
    char moduleType[10];      // 机型类型
    char fixedValue[2];       // 固定值
    char checksum[2];         // 校验和
    char swVersion[2];        // 软件版本
    char date[6];             // 日期 (格式 YYMMDD)
    uint32_t reserved;        // 保留区域 (4 字节)
    char swModel[16];         // 软件型号
    char hwModel[16];         // 硬件型号
    char twoPortMemory[2];    // 两端口存储器占用容量
    char twoPortAttribute[2]; // 两端口属性
    char availableBias[2];    // 可使用偏置
    char moduleType[10];      // 机型类型
    // 输出结构体内容为字符串 (便于调试)
    // 输出结构体内容为字符串 (便于调试)
    std::string toString() const {
        std::ostringstream oss;
        oss << "Fixed Value: " << fixedValue[0] << fixedValue[1] << "\n"
@@ -172,61 +172,55 @@
    }
};
// 站点标识符,默认使用本站
// 站点标识符,默认使用本站
struct StationIdentifier {
    /*
     * [Network No.]
     * 0 表示本站
     * 1~239 表示普通网络号
     * 0 表示本站
     * 1~239 表示普通网络号
     **/
    /*
     * [Station No.]
     * MELSECNET/H:1~64 表示其他站点,255 表示本站
     * CC-Link 系列网络的范围类似,区别在于站号的取值范围
     * MELSECNET/H             : 1~64(Other stations),255(Own station)
     * CC-Link                 : 0~63(Other stations),255(Own station)
     * CC-Link IE Controller   : 1~120(Other stations),255(Own station)
     * CC-Link IE Field        : 0~120(Other stations),255(Own station)
     * CC-Link IE TSN          : 0~120(Other stations),255(Own station)
     **/
     /*
      * [Station No.]
      * MELSECNET/H:1~64 表示其他站点,255 表示本站
      * CC-Link 系列网络的范围类似,区别在于站号的取值范围
      * MELSECNET/H             : 1~64(Other stations),255(Own station)
      * CC-Link                 : 0~63(Other stations),255(Own station)
      * CC-Link IE Controller   : 1~120(Other stations),255(Own station)
      * CC-Link IE Field        : 0~120(Other stations),255(Own station)
      * CC-Link IE TSN          : 0~120(Other stations),255(Own station)
      **/
    /*
     * 高 8 位(网络号): 指定设备所属的网络
     * 低 8 位(站点号): 指定设备在网络中的编号
     * 用一个参数传递设备的网络号和站点号时: nSt = station.nStNo | ((station.nNetNo << 8) & 0xFF00);
     **/
      /*
       * 高 8 位(网络号): 指定设备所属的网络
       * 低 8 位(站点号): 指定设备在网络中的编号
       * 用一个参数传递设备的网络号和站点号时: nSt = station.nStNo | ((station.nNetNo << 8) & 0xFF00);
       **/
    short nNetNo = 0;    // 网络编号:PLC所连接的网络编号,0表示默认网络
    short nStNo = 255;   // 站点编号:指定与PLC连接的站点编号,255通常表示广播或所有站点
    short nNetNo = 0;    // 网络编号:PLC所连接的网络编号,0表示默认网络
    short nStNo = 255;   // 站点编号:指定与PLC连接的站点编号,255通常表示广播或所有站点
    StationIdentifier()
    {
        nNetNo = 0;
        nStNo = 255;
    }
    // 自定义构造函数,覆盖默认值
    // 自定义构造函数,覆盖默认值
    explicit StationIdentifier(const short net, const short st) : nNetNo(net), nStNo(st) {}
    // 将“网络号”和“站点号”组合成一个最终编码
    // 将“网络号”和“站点号”组合成一个最终编码
    short StationIdentifier::toNetworkStationCode() const {
        return static_cast<short>(nStNo | ((nNetNo << 8) & 0xFF00));
    }
    // 重载 < 运算符(用于排序或比较,通常用于 map 或 set 中作为 key)
    // 重载 < 运算符(用于排序或比较,通常用于 map 或 set 中作为 key)
    bool operator<(const StationIdentifier& other) const {
        return std::tie(nNetNo, nStNo) <
               std::tie(other.nNetNo, other.nStNo);
            std::tie(other.nNetNo, other.nStNo);
    }
    // 重载 == 运算符(用于相等比较)
    // 重载 == 运算符(用于相等比较)
    bool operator==(const StationIdentifier& other) const {
        return std::tie(nNetNo, nStNo) ==
               std::tie(other.nNetNo, other.nStNo);
            std::tie(other.nNetNo, other.nStNo);
    }
    // 重载 = 运算符(用于赋值)
    // 重载 = 运算符(用于赋值)
    StationIdentifier& operator=(const StationIdentifier& other) {
        if (this != &other) {
            nNetNo = other.nNetNo;
@@ -236,16 +230,16 @@
    }
};
// 板状态
// 板状态
struct BoardStatus {
    short nStationValue = 0;    // 站号的设备值 (buf[0])
    short nGroupValue = 0;      // 组 No. 的设备值 (buf[1])
    short nNetworkValue = 0;    // 网络 No. 的设备值 (buf[2])
    short nReserved1 = 0;       // 保留字段 (buf[3])
    short nReserved2 = 0;       // 保留字段 (buf[4])
    short nReserved3 = 0;       // 保留字段 (buf[5])
    short nStationValue = 0;    // 站号的设备值 (buf[0])
    short nGroupValue = 0;      // 组 No. 的设备值 (buf[1])
    short nNetworkValue = 0;    // 网络 No. 的设备值 (buf[2])
    short nReserved1 = 0;       // 保留字段 (buf[3])
    short nReserved2 = 0;       // 保留字段 (buf[4])
    short nReserved3 = 0;       // 保留字段 (buf[5])
    // 将数组映射到结构体
    // 将数组映射到结构体
    static BoardStatus fromBuffer(const short buf[6]) {
        return {
            buf[0],
@@ -257,7 +251,7 @@
        };
    }
    // 将结构体内容映射到数组
    // 将结构体内容映射到数组
    void toBuffer(short buf[6]) const {
        buf[0] = nStationValue;
        buf[1] = nGroupValue;
@@ -267,7 +261,7 @@
        buf[5] = nReserved3;
    }
    // 调试输出
    // 调试输出
    std::string toString() const {
        std::ostringstream oss;
        oss << "Station Value: " << nStationValue << "\n"
@@ -280,12 +274,12 @@
    }
};
// 事件详情
// 事件详情
struct EventDetails {
    short nEventNo;                      // 发生的事件号
    std::array<short, 4> details;         // 存储事件详情信息
    short nEventNo;                      // 发生的事件号
    std::array<short, 4> details;         // 存储事件详情信息
    // 解析事件详情,返回格式化字符串
    // 解析事件详情,返回格式化字符串
    std::string toString() const {
        std::ostringstream oss;
        oss << "Details[0]: " << details[0] << ", "
@@ -296,33 +290,33 @@
    }
};
// SoftElement 结构体定义
// SoftElement 结构体定义
struct SoftElement {
    short nType;         // 软元件类型
    short nElementCount; // 点数
    long nStartNo;       // 起始软元件编号
    short nType;         // 软元件类型
    short nElementCount; // 点数
    long nStartNo;       // 起始软元件编号
};
// 错误信息
// 错误信息
struct ErrorInfo {
    int nErrorCode = 0;              // 错误码
    std::string strErrorMessageCn;   // 中文描述
    std::string strErrorMessageEn;   // 英文描述
    int nErrorCode = 0;              // 错误码
    std::string strErrorMessageCn;   // 中文描述
    std::string strErrorMessageEn;   // 英文描述
    // 将结构体序列化为字符串
    // 将结构体序列化为字符串
    std::string toString() const {
        std::ostringstream oss;
        oss << nErrorCode << "|" << strErrorMessageCn << "|" << strErrorMessageEn;
        return oss.str();
    }
    // 从字符串反序列化为结构体
    // 从字符串反序列化为结构体
    static ErrorInfo fromString(const std::string& line) {
        ErrorInfo info;
        std::istringstream iss(line);
        std::string token;
        // 使用分隔符 "|" 解析字符串
        // 使用分隔符 "|" 解析字符串
        std::getline(iss, token, '|');
        info.nErrorCode = std::stoi(token);
@@ -336,47 +330,47 @@
    }
};
using BitContainer = std::vector<uint8_t>;        // 每个元素存储 8 个位
using WordContainer = std::vector<uint16_t>;    // 每个元素存储 16 位
using DWordContainer = std::vector<uint32_t>;    // 每个元素存储 32 位
using BitContainer = std::vector<bool>;            // 每个元素存储 1  位
using WordContainer = std::vector<uint16_t>;    // 每个元素存储 16 位
using DWordContainer = std::vector<uint32_t>;    // 每个元素存储 32 位
// CPerformanceMelsec 类声明
// CPerformanceMelsec 类声明
class CPerformanceMelsec {
public:
    // 获取最近的错误信息
    // 获取最近的错误信息
    std::string GetLastError() const;
    // 错误信息加载与保存接口
    static bool LoadErrorInfoFromFile(const std::string& filename);  // 从文件加载错误信息
    static bool SaveErrorInfoToFile(const std::string& filename);    // 保存错误信息到文件
    // 错误信息加载与保存接口
    static bool LoadErrorInfoFromFile(const std::string& filename);  // 从文件加载错误信息
    static bool SaveErrorInfoToFile(const std::string& filename);    // 保存错误信息到文件
    // 连接/断开
    // 连接/断开
    int Connect(short nChannel, short nMode = -1);
    int Disconnect();
    // 初始化可编程控制器软元件信息表
    // 初始化可编程控制器软元件信息表
    int InitializeController();
    //    获取版本信息
    //    获取版本信息
    int GetBoardVersion(BoardVersion& version);
    // 板复位
    // 板复位
    int BoardReset();
    // 板LED读取
    // 板LED读取
    int ReadBoardLed(std::vector<short>& vecLedBuffer);
    // 读取目标站点CPU类型
    // 读取目标站点CPU类型
    int ReadCPUCode(const StationIdentifier& station, short& nCPUCode);
    // 板模式获取/设置
    // 板模式获取/设置
    int SetBoardMode(short nMode);
    int GetBoardMode(short& nMode);
    // 获取板状态
    // 获取板状态
    int GetBoardStatus(BoardStatus& status);
    // 读写数据
    // 读写数据
    int ReadData(const StationIdentifier& station, short nDevType, short nDevNo, short nSize, std::vector<short>& vecData);
    int ReadBitData(const StationIdentifier& station, DeviceType enDevType, short nDevNo, short nBitCount, BitContainer& vecData);
    int ReadWordData(const StationIdentifier& station, DeviceType enDevType, short nDevNo, short nWordCount, WordContainer& vecData);
@@ -386,68 +380,68 @@
    int WriteWordData(const StationIdentifier& station, DeviceType enDevType, short nDevNo, const WordContainer& vecData);
    int WriteDWordData(const StationIdentifier& station, DeviceType enDevType, short nDevNo, const DWordContainer& vecData);
    // 扩展读写数据
    // 扩展读写数据
    long ReadDataEx(const StationIdentifier& station, long nDevType, long nDevNo, long nSize, std::vector<char>& vecData);
    long WriteDataEx(const StationIdentifier& station, long nDevType, long nDevNo, const std::vector<char>& vecData);
    // 扩展软元件随机读写(支持多个软元件)
    // 扩展软元件随机读写(支持多个软元件)
    long ReadRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, std::vector<char>& vecData);
    long WriteRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, const std::vector<char>& vecData);
    // 远程设备站/远程站的缓冲存储器读写
    // 远程设备站/远程站的缓冲存储器读写
    long ReadRemoteBuffer(const StationIdentifier& station, long nOffset, long nSize, std::vector<char>& vecData);
    long WriteRemoteBuffer(const StationIdentifier& station, long nOffset, const std::vector<char>& vecData);
    long ReadRemoteBufferByIp(const std::string& strIP, long nOffset, long nSize, std::vector<char>& vecData);
    long WriteRemoteBufferByIp(const std::string& strIP, long nOffset, const std::vector<char>& vecData);
    // 设置/复位对象站的指定位软元件
    // 设置/复位对象站的指定位软元件
    int SetBitDevice(const StationIdentifier& station, DeviceType enDevType, short nDevNo);
    int ResetBitDevice(const StationIdentifier& station, DeviceType enDevType, short enDevNo);
    // 扩展设置/复位对象站的指定位软元件
    // 扩展设置/复位对象站的指定位软元件
    long SetBitDeviceEx(const StationIdentifier& station, long nDevType, long nDevNo);
    long ResetBitDeviceEx(const StationIdentifier& station, long nDevType, long nDevNo);
    // 执行对象站的CPU
    // 执行对象站的CPU
    int ControlCPU(const StationIdentifier& station, ControlCode enControlCode);
    // 事件等待,vecEventNumbers[0, 64],nTimeoutMs[-1, 2147483647]
    // 同时发生了多个事件的情况下,首先检测出其中一个事件。 再次执行了本函数的情况下检测出其它事件。
    // 事件等待,vecEventNumbers[0, 64],nTimeoutMs[-1, 2147483647]
    // 同时发生了多个事件的情况下,首先检测出其中一个事件。 再次执行了本函数的情况下检测出其它事件。
    int WaitForBoardEvent(std::vector<short> vecEventNumbers, int nTimeoutMs, EventDetails& details);
private:
    // 锁定与解锁(多线程同步保护)
    // 锁定与解锁(多线程同步保护)
    void Lock() { m_mtx.lock(); }
    void Unlock() { m_mtx.unlock(); }
protected:
    // 构造函数/析构函数
    // 构造函数/析构函数
    explicit CPerformanceMelsec(BoardType enBoardType);
    virtual ~CPerformanceMelsec();
    // 辅助函数
    void UpdateLastError(int nCode);                                 // 更新最近的错误信息
    int ValidateStation(const StationIdentifier& station) const;     // 检查连接状态和站点参数有效性
    // 辅助函数
    void UpdateLastError(int nCode);                                 // 更新最近的错误信息
    int ValidateStation(const StationIdentifier& station) const;     // 检查连接状态和站点参数有效性
    int ValidateStationAndSize(const StationIdentifier& station, short nCount) const;
    // 静态辅助函数
    static void Delay(unsigned int nDelayMs);                        // 延时,并且转发窗口消息
    static BoardType FindBoardTypeByChannel(int nChannel);            // 查找板块类型
    static short CombineStation(const StationIdentifier& station);  // 合并网络号和站点号
    static short CalculateDeviceType(const StationIdentifier& station, DeviceType enDevType); // 计算软元件类型
    // 静态辅助函数
    static void Delay(unsigned int nDelayMs);                        // 延时,并且转发窗口消息
    static BoardType FindBoardTypeByChannel(int nChannel);            // 查找板块类型
    static short CombineStation(const StationIdentifier& station);  // 合并网络号和站点号
    static short CalculateDeviceType(const StationIdentifier& station, DeviceType enDevType); // 计算软元件类型
    // IP转换
    // IP转换
    static bool ConvertIpStringToUint32(const std::string& strIP, uint32_t& nIP);
    // 容器转换
    // 容器转换
    static void ConvertCharToShort(const std::vector<char>& vecChar, std::vector<short>& vecShort);
    static void ConvertShortToChar(const std::vector<short>& vecShort, std::vector<char>&vecChar);
    static void ConvertShortToChar(const std::vector<short>& vecShort, std::vector<char>& vecChar);
    static void ConvertUint8ToShort(const std::vector<uint8_t>& vecUint8, std::vector<short>& vecShort);
    static void ConvertShortToUint8(const std::vector<short>& vecShort, std::vector<uint8_t>& vecUint8);
    static void ConvertUint32ToShort(const std::vector<uint32_t>& vecUint32, std::vector<short>& vecShort);
    static void ConvertShortToUint32(const std::vector<short>& vecShort, std::vector<uint32_t>& vecUint32);
    // 模板辅助函数
    // 模板辅助函数
    template <typename T>
    int ValidateStationAndData(const StationIdentifier& station, const std::vector<T>& vecData);
@@ -457,15 +451,15 @@
    template <typename T, typename U>
    void ConvertHighToLow(const std::vector<T>& vecHigh, std::vector<U>& vecLow);
    // 成员变量
    std::mutex m_mtx;                       // 互斥锁保护
    BoardType m_enBoardType;                // 板块类型
    long m_nPath;                           // 通信路径
    std::atomic<bool> m_bConnected;         // 是否已连接
    std::string m_strLastError;             // 最近一次错误信息
    // 成员变量
    std::mutex m_mtx;                       // 互斥锁保护
    BoardType m_enBoardType;                // 板块类型
    long m_nPath;                           // 通信路径
    std::atomic<bool> m_bConnected;         // 是否已连接
    std::string m_strLastError;             // 最近一次错误信息
    // 静态成员变量
    static std::unordered_map<int, std::string> m_mapError; // 错误码映射表
    // 静态成员变量
    static std::unordered_map<int, std::string> m_mapError; // 错误码映射表
};
#endif // PERFORMANCE_MELSEC_H
SourceCode/Bond/Servo/CEquipment.cpp
@@ -5,7 +5,7 @@
namespace SERVO {
    CEquipment::CEquipment()
    CEquipment::CEquipment() : m_nID(0), m_strName(""), m_strDescription(""), m_station(0, 255)
    {
        m_listener = { nullptr, nullptr };
        m_alive = {FALSE, 0, FALSE};
@@ -34,6 +34,16 @@
    }
    void CEquipment::setID(int nID)
    {
        m_nID = nID;
    }
    int CEquipment::getID()
    {
        return m_nID;
    }
    void CEquipment::setName(const char* pszName)
    {
        m_strName = pszName;
SourceCode/Bond/Servo/CEquipment.h
@@ -39,6 +39,8 @@
    public:
        virtual const char* getClassName() = 0;
        virtual void setListener(EquipmentListener listener);
        void setID(int nID);
        int getID();
        void setName(const char* pszName);
        std::string& getName();
        void setDescription(const char* pszDescription);
@@ -65,6 +67,7 @@
    protected:
        EquipmentListener m_listener;
        int m_nID;
        std::string m_strName;
        std::string m_strDescription;
        CRITICAL_SECTION m_criticalSection;
SourceCode/Bond/Servo/CMaster.cpp
@@ -64,6 +64,7 @@
        // 初始化添加各子设备
        {
            CEFEM* pEquipment = new CEFEM();
            pEquipment->setID(1);
            pEquipment->setName("EFEM(ROBOT)");
            pEquipment->setDescription("EFEM(ROBOT).");
            pEquipment->setReadBitBlock(0x4000, 0x45ff);
SourceCode/Bond/Servo/Common.h
@@ -7,6 +7,7 @@
#define RX_CODE_PASSIVE_STATUS_CHANGED    1001
#define RX_CODE_MES_MESSAGE                1002
#define RX_HSMS_TERMINAL_TEXT            1003
#define RX_CODE_EQ_ALIVE                1004
/* Channel Name */
SourceCode/Bond/Servo/Model.cpp
@@ -93,6 +93,7 @@
    masterListener.onEqAlive = [&](void* pMaster, SERVO::CEquipment* pEquipment, BOOL bAlive) -> void {
        LOGI("<CModel>Equipment onAlive:%s(%s).\n", pEquipment->getName().c_str(),
            bAlive ? _T("ON") : _T("OFF"));
        notifyPtr(RX_CODE_EQ_ALIVE, pEquipment);
    };
    masterListener.onEqCimStateChanged = [&](void* pMaster, SERVO::CEquipment* pEquipment, BOOL bOn) -> void {
SourceCode/Bond/Servo/SECSRuntimeManager.cpp
@@ -249,6 +249,28 @@
    return 0;
}
// 获取指定 ID 的 SystemSV 数据
std::vector<std::vector<std::string>> SECSRuntimeManager::getSystemSVByID(int nID) {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_pDB == nullptr) {
        return {};
    }
    std::string query = "SELECT * FROM SystemSV WHERE ID = " + std::to_string(nID) + ";";
    return m_pDB->fetchResults(query);
}
// 获取所有 SystemSV 数据
std::vector<std::vector<std::string>> SECSRuntimeManager::getAllSystemSV() {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_pDB == nullptr) {
        return {};
    }
    std::string query = "SELECT * FROM SystemSV;";
    return m_pDB->fetchResults(query);
}
// 更新指定 ID 的 SystemSV 数据
int SECSRuntimeManager::updateIDSystemSV(int nID, int sNewID) {
    std::lock_guard<std::mutex> lock(m_mutex);
@@ -468,6 +490,152 @@
    return 0; // 插入成功,返回 0 表示操作成功完成。
}
// 查找指定 ID 的 EqpSV 数据
std::vector<std::vector<std::string>> SECSRuntimeManager::getEqpSVByID(int nID) {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_pDB == nullptr) {
        return {};
    }
    // 查询 SQL 语句
    std::string querySQL = "SELECT * FROM EqpSV WHERE ID = " + std::to_string(nID) + ";";
    return m_pDB->fetchResults(querySQL); // 直接返回查询结果
}
// 查找所有 EqpSV 数据
std::vector<std::vector<std::string>> SECSRuntimeManager::getAllEqpSV() {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_pDB == nullptr) {
        return {};
    }
    // 查询 SQL 语句,获取所有数据
    std::string querySQL = "SELECT * FROM EqpSV;";
    return m_pDB->fetchResults(querySQL); // 直接返回查询结果
}
// 更新指定 ID 的 EqpSV 数据
int SECSRuntimeManager::updateEqpSV(int nID, int nNewID, const std::string& sName, const std::string& sDataType, int nLength, const std::string& sUnit, const std::string& sRemark, int nSeqNo) {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_pDB == nullptr) {
        return 1; // 如果数据库未连接,返回 1
    }
    // 检查是否存在该 ID
    if (!isIDDuplicate(nID)) {
        return 2; // 如果 ID 不存在,返回错误代码 2
    }
    // 检查新的 ID 是否已存在
    if (isIDDuplicate(nNewID) && nNewID != nID) {
        return 3; // 如果新 ID 已存在,返回错误代码 3
    }
    // 构建更新 SQL 语句
    std::string updateSQL = "UPDATE EqpSV SET ";
    bool firstField = true;
    // 如果新的 ID 被提供,更新 ID
    if (nNewID > 0 && nNewID != nID) {
        updateSQL += "ID = " + std::to_string(nNewID);
        firstField = false;
    }
    // 更新 Name
    if (!sName.empty()) {
        if (!firstField) updateSQL += ", ";
        updateSQL += "Name = '" + sName + "'";
        firstField = false;
    }
    // 更新 DataType
    if (!sDataType.empty()) {
        if (!firstField) updateSQL += ", ";
        updateSQL += "DataType = '" + sDataType + "'";
        firstField = false;
    }
    // 更新 Length
    if (nLength > 0) {
        if (!firstField) updateSQL += ", ";
        updateSQL += "Length = " + std::to_string(nLength);
        firstField = false;
    }
    // 更新 Unit
    if (sUnit != "NULL" && !sUnit.empty()) {
        if (!firstField) updateSQL += ", ";
        updateSQL += "Unit = '" + sUnit + "'";
        firstField = false;
    }
    else if (sUnit == "NULL") {
        if (!firstField) updateSQL += ", ";
        updateSQL += "Unit = NULL";
        firstField = false;
    }
    // 更新 Remark
    if (!sRemark.empty()) {
        if (!firstField) updateSQL += ", ";
        updateSQL += "Remark = '" + sRemark + "'";
        firstField = false;
    }
    // 更新 SeqNo
    if (nSeqNo > 0) {
        if (!firstField) updateSQL += ", ";
        updateSQL += "SeqNo = " + std::to_string(nSeqNo);
    }
    // 添加 WHERE 子句,更新指定 ID 的数据
    updateSQL += " WHERE ID = " + std::to_string(nID) + ";";
    // 执行更新操作
    if (!m_pDB->executeQuery(updateSQL)) {
        return 4; // 如果更新失败,返回错误代码 4
    }
    return 0; // 更新成功,返回 0
}
// 删除指定 ID 的 EqpSV 数据
int SECSRuntimeManager::deleteEqpSVByID(int nID) {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_pDB == nullptr) {
        return 1;
    }
    // 检查是否存在该 ID
    if (!isIDDuplicate(nID)) {
        return 2; // 如果 ID 不存在,返回错误代码 2
    }
    // 构建删除 SQL 语句
    std::string deleteSQL = "DELETE FROM EqpSV WHERE ID = " + std::to_string(nID) + ";";
    if (!m_pDB->executeQuery(deleteSQL)) {
        return 3; // 如果删除失败,返回错误代码 3
    }
    return 0; // 删除成功,返回 0
}
// 删除 EqpSV 表中的所有数据
int SECSRuntimeManager::deleteAllEqpSV() {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_pDB == nullptr) {
        return 1;
    }
    // 构建删除所有数据的 SQL 语句
    std::string deleteSQL = "DELETE FROM EqpSV;";
    if (!m_pDB->executeQuery(deleteSQL)) {
        return 2; // 如果删除失败,返回错误代码 2
    }
    return 0; // 删除成功,返回 0
}
// 初始化 SystemDV 表
void SECSRuntimeManager::initSystemDVTable() {
    std::lock_guard<std::mutex> lock(m_mutex);
SourceCode/Bond/Servo/SECSRuntimeManager.h
@@ -59,6 +59,29 @@
    int addSystemSV(int nID, const std::string& sName, const std::string& sDataType, int nLength, const std::string& sUnit, const std::string& sRemark, int nSystemID);
    /**
     * 获取指定 ID 的 SystemSV 数据
     * @param nID: 需要查找的 SystemSV 的 ID。
     * @return std::vector<std::vector<std::string>>: 返回一个二维字符串向量,表示查询结果。每行代表一条记录,每列代表该记录的一个字段值。
     *          如果指定的 ID 存在,则返回该 ID 对应的记录;如果不存在,则返回空的二维向量。
     *
     * 此函数用于通过指定的 `ID` 从 SystemSV 表中查找对应的数据。首先根据传入的 `ID` 构造查询 SQL 语句,
     * 然后执行查询操作,返回查询结果。查询结果是一个二维字符串向量,表示查询到的记录。每行是一个
     * 字符串向量,每个元素是该行中的一列数据。如果找不到对应的数据,函数将返回一个空的二维向量。
     */
    std::vector<std::vector<std::string>> getSystemSVByID(int nID);
    /**
    * 获取所有 SystemSV 数据
    * @return std::vector<std::vector<std::string>>: 返回一个二维字符串向量,表示查询结果。每行代表一条记录,每列代表该记录的一个字段值。
    *          如果表中有数据,则返回所有记录;如果表为空,则返回空的二维向量。
    *
    * 此函数用于从 SystemSV 表中获取所有的数据。通过构造 SQL 查询语句来选择所有记录,并执行查询操作。
    * 返回的结果是一个二维字符串向量,表示表中的所有记录。每行数据是一个字符串向量,其中包含该记录的各个字段。
    * 如果表中没有数据,函数将返回一个空的二维向量。
    */
    std::vector<std::vector<std::string>> getAllSystemSV();
    /**
     * 更新指定 ID 的 SystemSV 数据
     * @param nID: 需要更新的 SystemSV 的当前 ID。
     * @param sNewID: 要更新为的新 ID。
@@ -126,7 +149,7 @@
    int deleteAllSystemSV();
    /**
    * 初始化Eqp表
    * 初始化 EqpSV 表
    */
    void initEqpSVTable();
@@ -152,48 +175,126 @@
     */
    int addEqpSV(int nID, const std::string& sName, const std::string& sDataType, int nLength, const std::string& sUnit, const std::string& sRemark, int nSeqNo);
    /**
    * 获取指定 ID 的 EqpSV 数据
    * @param nID: 需要查找的 EqpSV 的 ID。
    * @return std::vector<std::vector<std::string>>: 返回一个二维字符串向量,表示查询结果。每行代表一条记录,每列代表该记录的一个字段值。
    *          如果指定的 ID 存在,则返回该 ID 对应的记录;如果不存在,则返回空的二维向量。
    *
    * 此函数用于通过指定的 `ID` 从 EqpSV 表中查找对应的数据。首先根据传入的 `ID` 构造查询 SQL 语句,
    * 然后执行查询操作,返回查询结果。查询结果是一个二维字符串向量,表示查询到的记录。每行是一个
    * 字符串向量,每个元素是该行中的一列数据。如果找不到对应的数据,函数将返回一个空的二维向量。
    */
    std::vector<std::vector<std::string>> SECSRuntimeManager::getEqpSVByID(int nID);
    /**
    * 获取所有 EqpSV 数据
    * @return std::vector<std::vector<std::string>>: 返回一个二维字符串向量,表示查询结果。每行代表一条记录,每列代表该记录的一个字段值。
    *          如果表中有数据,则返回所有记录;如果表为空,则返回空的二维向量。
    *
    * 此函数用于从 EqpSV 表中获取所有的数据。通过构造 SQL 查询语句来选择所有记录,并执行查询操作。
    * 返回的结果是一个二维字符串向量,表示表中的所有记录。每行数据是一个字符串向量,其中包含该记录的各个字段。
    * 如果表中没有数据,函数将返回一个空的二维向量。
    */
    std::vector<std::vector<std::string>> SECSRuntimeManager::getAllEqpSV();
    /**
     * 更新指定 ID 的 EqpSV 数据
     * @param nID: 需要更新的 EqpSV 的 ID,必须是已存在的 ID。
     * @param nNewID: 更新后的 ID,必须是唯一的。
     * @param sName: 更新后的 EqpSV 名称,必须是唯一的。
     * @param sDataType: 更新后的数据类型,表示该设备值的类型,例如 "ASCII"、"UINT_1" 等。
     * @param nLength: 更新后的设备值的数据长度,通常为一个正整数,用于表示该数据的长度。
     * @param sUnit: 更新后的设备值的单位。如果为空或者为 "NULL",则插入数据库中的 NULL 值。
     * @param sRemark: 更新后的备注信息,描述该设备值的其他信息,可用于说明该字段的用途或特性。
     * @param nSeqNo: 更新后的该数据的序号,用于排序。
     * @return 1: 数据库未连接。
     * @return 2: ID 不存在,无法更新数据。
     * @return 3: 新 ID 重复,无法更新数据。
     * @return 4: 更新数据失败。
     * @return 0: 更新成功,数据已更新到 EqpSV 表中。
     *
     * 此函数用于更新指定 ID 的 EqpSV 数据。首先检查 `ID` 是否存在,如果不存在则返回错误代码 2。
     * 然后检查新的 `ID` 是否已存在,如果已存在则返回错误代码 3。接下来,构造更新 SQL 语句,包含
     * 需要更新的字段,并执行更新操作。如果更新失败,则返回错误代码 4。如果一切顺利,返回 0 表示
     * 数据成功更新。
     */
    int updateEqpSV(int nID, int nNewID, const std::string& sName, const std::string& sDataType, int nLength, const std::string& sUnit, const std::string& sRemark, int nSeqNo);
    /**
    * 初始化SystemDV表
     * 更新指定 ID 的 EqpSV 数据
     * @param nID: 需要更新的 EqpSV 的当前 ID。
     * @param sNewID: 要更新为的新 ID。
     * @return 1: 数据库未连接。
     * @return 2: 未找到指定的 ID。
     * @return 3: 新的 ID 已经存在,无法更新。
     * @return 4: 更新操作失败。
     * @return 0: 更新成功。
     *
     * 此函数用于更新 `EqpSV` 表中指定 `nID` 的记录,将其 `ID` 字段更新为 `sNewID`。
     * 在执行更新前,函数会检查:
     * 1. 当前的 `nID` 是否存在于表中。
     * 2. 新的 `sNewID` 是否已经存在于表中,如果存在,则无法进行更新。
     *
     * 如果 `nID` 不存在,则返回错误代码 2。如果 `sNewID` 已经存在,则返回错误代码 3。
     * 如果数据库更新失败,则返回错误代码 4。成功时,返回 0 表示操作成功。
     */
    int deleteEqpSVByID(int nID);
    /**
    * 删除所有 EqpSV 数据
    * @return 1: 数据库未连接。
    * @return 2: 删除操作失败。
    * @return 0: 删除成功。
    *
    * 此函数用于删除 `EqpSV` 表中的所有记录。如果数据库未连接,则返回错误代码 1。
    * 如果删除操作失败,则返回错误代码 2。删除成功后,返回 0 表示删除成功。
    */
    int deleteAllEqpSV();
    /**
    * 初始化 SystemDV 表
    */ 
    void initSystemDVTable();
    /**
    * 初始化EqpDV表
    * 初始化 EqpDV 表
    */ 
    void initEqpDVTable();
    /**
    * 初始化SystemEC表
    * 初始化 SystemEC 表
    */ 
    void initSystemECTable();
    /**
    * 初始化EqpEC表
    * 初始化 EqpEC 表
    */
    void initEqpECTable();
    /**
    * 初始化SystemEvent表
    * 初始化 SystemEvent 表
    */
    void initSystemEventTable();
    /**
    * 初始化EqpEvent表
    * 初始化 EqpEvent 表
    */
    void initEqpEventTable();
    /**
    * 初始化EventLink表
    * 初始化 EventLink 表
    */
    void initEventLinkTable();
    /**
     * 初始化PPID表
     * 初始化 PPID 表
     */
    void initPPIDTable();
    /**
    * 初始化RPTID表
    * 初始化 RPTID 表
    */ 
    void initRPTIDTable();
SourceCode/Bond/Servo/ServoDlg.cpp
@@ -144,6 +144,21 @@
                    ShowTerminalText(pszText);
                }
            }
            else if (RX_CODE_EQ_ALIVE == code) {
                // 通知设备状态
                SERVO::CEquipment* pEquipment = nullptr;
                if (pAny->getPtrValue("ptr", (void*&)pEquipment)) {
                    if (pEquipment != nullptr) {
                        int nID = pEquipment->getID();
                        BOOL bAlive = pEquipment->isAlive();
                        if (1 == nID) {
                            DeviceStatus status = bAlive ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
                            UpdateDeviceStatus(INDICATE_ROBOT_ARM1, status);
                            UpdateDeviceStatus(INDICATE_ROBOT_ARM2, status);
                        }
                    }
                }
            }
            pAny->release();
        }, [&]() -> void {
            // onComplete
@@ -660,12 +675,12 @@
    COLORREF newFrameColor2;
    switch (status) {
    case Online:
    case ONLINE:
        newBackgroundColor = RGB(255, 0, 0);
        newFrameColor1 = RGB(22, 22, 22);
        newFrameColor2 = RGB(255, 127, 39);
        break;
    case Offline:
    case OFFLINE:
        newBackgroundColor = RGB(0, 255, 0);
        newFrameColor1 = RGB(22, 22, 22);
        newFrameColor2 = RGB(255, 127, 39);
SourceCode/Bond/Servo/ServoDlg.h
@@ -9,8 +9,8 @@
#include "TerminalDisplayDlg.h"
enum DeviceStatus {
    Online,       // 在线
    Offline,      // 离线
    ONLINE,       // 在线
    OFFLINE,      // 离线
};
// CServoDlg 对话框