#include "AQTSampleMachinePlug.hpp" #include #include /** * @brief 构造函数 * @param parent 父对象指针 * * 初始化插件对象,包括: * - 初始化所有指针为空 * - 创建DDS类型支持对象 * - 初始化数据点信息 */ AQTSampleMachinePlug::AQTSampleMachinePlug(QObject *parent) : QObject(parent) , participant_(nullptr) , publisher_(nullptr) , subscriber_(nullptr) , commandTopic_(nullptr) , commandReader_(nullptr) , configTopic_(nullptr) , configReader_(nullptr) , pointsTopic_(nullptr) , pointsReader_(nullptr) , machineinfoTopic_(nullptr) , machineinfoWriter_(nullptr) , plcConnected_(false) // 初始化 PLC 连接状态 { // 初始化类型支持 commandType_ = TypeSupport(new SampleCommandPubSubType()); configType_ = TypeSupport(new SampleConfigPubSubType()); pointsType_ = TypeSupport(new SamplePointsPubSubType()); machineinfoType_ = TypeSupport(new SampleMachineInfoPubSubType()); } /** * @brief 析构函数 * * 清理插件对象,主要是清理所有DDS相关的资源。 */ AQTSampleMachinePlug::~AQTSampleMachinePlug() { // PLC 资源会在 PLCController 的析构函数中自动清理 // 清理DDS资源 cleanupDDSEntities(); } /** * @brief 初始化插件 * @param domainId DDS域ID * @param domainName DDS域名称 * @return 初始化是否成功 * * 初始化DDS通信环境,包括: * - 创建DDS参与者 * - 初始化所有DDS实体 */ bool AQTSampleMachinePlug::init(uint32_t domainId, const QString& domainName) { // 创建DDS参与者 DomainParticipantQos participantQos; participantQos.name(domainName.toStdString()); participant_ = DomainParticipantFactory::get_instance()->create_participant(domainId, participantQos); if (!participant_) { qDebug() << "Failed to create participant"; return false; } // 初始化其他DDS实体 if (!initDDSEntities()) { qDebug() << "Failed to initialize DDS entities"; return false; } // 连接到 PLC plcConnected_ = plc_.connect("192.168.0.1", 0, 1); // 使用默认参数 if (!plcConnected_) { qDebug() << "Failed to connect to PLC"; return false; } return true; } /** * @brief 获取数据点信息列表 * @return 所有支持的数据点信息列表 */ QList AQTSampleMachinePlug::getDataPoints() { // 在函数调用时创建并返回数据点列表 return { // ===== SampleConfigData 相关数据点 (1-9) ===== {1, "UnifiedDepth", "uint16"}, // 统一深度(全段面采样时使用) {2, "SampleOrder", "uint8"}, // 大车行进顺序(1:原点往外,2:往原点采) // ===== SampleCommand 相关数据点 (10-29) ===== {10, "CommandMaskBits", "uint32"}, // 命令掩码,表示哪些字段被修改 {11, "ControlType", "uint8"}, // 控制类型(1:就地,2:远程) {12, "StartSample", "int32"}, // 启动采样命令 (CommandState: STOP=0, START=1) {13, "StopSample", "int32"}, // 停止采样命令 (CommandState: STOP=0, START=1) {14, "Reset", "int32"}, // 系统复位命令 (CommandState: STOP=0, START=1) {15, "PositionCheck", "int32"}, // 位置确认命令 (CommandState: STOP=0, START=1) {16, "EmergencyUp", "int32"}, // 紧急提升命令 (CommandState: STOP=0, START=1) {17, "EmergencyStop", "int32"}, // 系统急停命令 (CommandState: STOP=0, START=1) {18, "ControlBelt1", "int32"}, // 启停皮带1 (CommandState: STOP=0, START=1) {19, "ControlBelt2", "int32"}, // 启停皮带2 (CommandState: STOP=0, START=1) {20, "ControlCrush1", "int32"}, // 启停破碎机1 (CommandState: STOP=0, START=1) {21, "ControlCrush2", "int32"}, // 启停破碎机2 (CommandState: STOP=0, START=1) {22, "ControlDivider", "int32"}, // 启停缩分器 (CommandState: STOP=0, START=1) {23, "ControlCoalDistributor", "int32"}, // 启停布煤器 (CommandState: STOP=0, START=1) {24, "ReductionRatio", "uint16"}, // 缩分比 {25, "CommandVersion", "uint8"}, // 命令结构体版本号 // ===== SampleInfo 相关数据点 (30-49) ===== {30, "StatusMaskBits", "uint32"}, // 状态掩码,表示哪些字段被修改 {31, "BigPoint", "uint16"}, // 大车位置 {32, "SmallPoint", "uint16"}, // 小车位置 {33, "DepthPoint", "uint16"}, // 深度位置 {34, "HasToXY", "uint8"}, // 到达坐标点(0:未到达,1:已到达) {35, "HasSamplePoints", "uint8"}, // 已采样点数 {36, "GameOver", "uint8"}, // 采样完成(0:未完成,1:完成) {37, "BackState", "uint16"}, // 采样状态返回 {38, "SampleState", "int32"}, // 采样机状态 (DeviceState: OFF=0, ON=1, ERROR=2) {39, "Belt1State", "int32"}, // 皮带1状态 (DeviceState: OFF=0, ON=1, ERROR=2) {40, "Belt2State", "int32"}, // 皮带2状态 (DeviceState: OFF=0, ON=1, ERROR=2) {41, "Crush1State", "int32"}, // 破碎机1状态 (DeviceState: OFF=0, ON=1, ERROR=2) {42, "Crush2State", "int32"}, // 破碎机2状态 (DeviceState: OFF=0, ON=1, ERROR=2) {43, "DividerState", "int32"}, // 缩分器状态 (DeviceState: OFF=0, ON=1, ERROR=2) {44, "BigLimit", "uint8"}, // 大车限位(0:未限位,1:已限位) {45, "BigZeroLimit", "uint8"}, // 大车零点限位(0:未限位,1:已限位) {46, "SmallLimit", "uint8"}, // 小车限位(0:未限位,1:已限位) {47, "SmallZeroLimit", "uint8"}, // 小车零点限位(0:未限位,1:已限位) {48, "UpLimit", "uint8"}, // 升高限位(0:未限位,1:已限位) {49, "DownLimit", "uint8"}, // 降低限位(0:未限位,1:已限位) {50, "StatusVersion", "uint8"}, // 状态结构体版本号 // ===== SamplePoints 相关数据点 (51-99) ===== {51, "TotalPoints", "uint8"}, // 采样点数量 // 点1 {52, "Point1X", "uint16"}, // 采样点1 X坐标 {53, "Point1Y", "uint16"}, // 采样点1 Y坐标 {54, "Point1Z", "uint16"}, // 采样点1 Z坐标 {55, "Point1Drop", "int32"}, // 采样点1 落料选择 (DropChoice: BYPASS=0, CRUSHER=1) // 点2 {56, "Point2X", "uint16"}, // 采样点2 X坐标 {57, "Point2Y", "uint16"}, // 采样点2 Y坐标 {58, "Point2Z", "uint16"}, // 采样点2 Z坐标 {59, "Point2Drop", "int32"}, // 采样点2 落料选择 (DropChoice: BYPASS=0, CRUSHER=1) // 点3 {60, "Point3X", "uint16"}, // 采样点3 X坐标 {61, "Point3Y", "uint16"}, // 采样点3 Y坐标 {62, "Point3Z", "uint16"}, // 采样点3 Z坐标 {63, "Point3Drop", "int32"}, // 采样点3 落料选择 (DropChoice: BYPASS=0, CRUSHER=1) // 点4 {64, "Point4X", "uint16"}, // 采样点4 X坐标 {65, "Point4Y", "uint16"}, // 采样点4 Y坐标 {66, "Point4Z", "uint16"}, // 采样点4 Z坐标 {67, "Point4Drop", "int32"}, // 采样点4 落料选择 (DropChoice: BYPASS=0, CRUSHER=1) // 点5 {68, "Point5X", "uint16"}, // 采样点5 X坐标 {69, "Point5Y", "uint16"}, // 采样点5 Y坐标 {70, "Point5Z", "uint16"}, // 采样点5 Z坐标 {71, "Point5Drop", "int32"}, // 采样点5 落料选择 (DropChoice: BYPASS=0, CRUSHER=1) // 点6 {72, "Point6X", "uint16"}, // 采样点6 X坐标 {73, "Point6Y", "uint16"}, // 采样点6 Y坐标 {74, "Point6Z", "uint16"}, // 采样点6 Z坐标 {75, "Point6Drop", "int32"} // 采样点6 落料选择 (DropChoice: BYPASS=0, CRUSHER=1) }; } /** * @brief 发布数据到DDS网络 * @param dataList 要发布的数据项列表 * @return 发布是否成功 * * 将数据项列表转换为相应的DDS消息并发布: * - 检查写入器是否可用 * - 转换数据格式 * - 发送数据 */ bool AQTSampleMachinePlug::publishData(const QList& dataList) { if (dataList.isEmpty()) { qDebug() << "Empty data list received"; return false; } try { int firstId = dataList.first().ID; if (firstId >= 30 && firstId <= 50) { // 机器信息数据 if (!machineinfoWriter_) { qDebug() << "Machineinfo writer not initialized"; return false; } SampleMachineInfo info = dataItemsToMachineInfo(dataList); machineinfoWriter_->write(&info); return true; } else { qDebug() << "Invalid data ID range:" << firstId; return false; } } catch (const std::exception& e) { qDebug() << "Error publishing data:" << e.what(); return false; } } /** * @brief 初始化DDS实体 * @return 初始化是否成功 * * 创建并初始化所有DDS通信所需的实体,包括: * - 注册数据类型 * - 创建发布者和订阅者 * - 创建主题 * - 创建数据写入器和读取器 * - 设置数据监听器 */ bool AQTSampleMachinePlug::initDDSEntities() { try { // 注册所有类型 commandType_.register_type(participant_); machineinfoType_.register_type(participant_); configType_.register_type(participant_); pointsType_.register_type(participant_); // 创建发布者和订阅者 publisher_ = participant_->create_publisher(PUBLISHER_QOS_DEFAULT); subscriber_ = participant_->create_subscriber(SUBSCRIBER_QOS_DEFAULT); if (!publisher_ || !subscriber_) { qDebug() << "Failed to create publisher or subscriber"; return false; } // 创建所有主题 commandTopic_ = participant_->create_topic("SampleMachine/Command", commandType_.get_type_name(), TOPIC_QOS_DEFAULT); machineinfoTopic_ = participant_->create_topic("SampleMachine/MachineInfo", machineinfoType_.get_type_name(), TOPIC_QOS_DEFAULT); configTopic_ = participant_->create_topic("SampleMachine/Config", configType_.get_type_name(), TOPIC_QOS_DEFAULT); pointsTopic_ = participant_->create_topic("SampleMachine/Points", pointsType_.get_type_name(), TOPIC_QOS_DEFAULT); if (!commandTopic_ || !machineinfoTopic_ || !configTopic_ || !pointsTopic_) { qDebug() << "Failed to create topics"; return false; } // 创建写入器和读取器 commandReader_ = subscriber_->create_datareader(commandTopic_, DATAREADER_QOS_DEFAULT); configReader_ = subscriber_->create_datareader(configTopic_, DATAREADER_QOS_DEFAULT); pointsReader_ = subscriber_->create_datareader(pointsTopic_, DATAREADER_QOS_DEFAULT); machineinfoWriter_ = publisher_->create_datawriter(machineinfoTopic_, DATAWRITER_QOS_DEFAULT); if (!commandReader_ || !configReader_ || !pointsReader_ || !machineinfoWriter_) { qDebug() << "Failed to create readers and writers"; return false; } // 设置监听器 commandReader_->set_listener(this, StatusMask::data_available()); configReader_->set_listener(this, StatusMask::data_available()); pointsReader_->set_listener(this, StatusMask::data_available()); return true; } catch (const std::exception& e) { qDebug() << "Error initializing DDS entities:" << e.what(); return false; } } /** * @brief 清理DDS实体 * * 清理所有已创建的DDS资源,包括: * - 删除所有包含的实体(主题、读写器等) * - 删除参与者 */ void AQTSampleMachinePlug::cleanupDDSEntities() { // 清理所有DDS资源 if (participant_) { participant_->delete_contained_entities(); DomainParticipantFactory::get_instance()->delete_participant(participant_); } } /** * @brief 数据可用性回调函数 * @param reader 触发回调的数据读取器 * * 当订阅的主题收到新数据时被调用。根据读取器类型分别处理状态数据和采样点数据, * 将DDS数据转换为DataItem列表并通过信号和回调函数通知外部。 */ void AQTSampleMachinePlug::on_data_available(DataReader* reader) { // if (reader == commandReader_) { // SampleCommand command; // SampleInfo info; // while (reader->take_next_sample(&command, &info) == ReturnCode_t::RETCODE_OK) { // if (info.valid_data) { // auto dataItems = commandToDataItems(command); // emit onDataUpdated(dataItems); // } // } // } // else if (reader == configReader_) { // SampleConfig config; // SampleInfo info; // while (reader->take_next_sample(&config, &info) == ReturnCode_t::RETCODE_OK) { // if (info.valid_data) { // auto dataItems = configToDataItems(config); // emit onDataUpdated(dataItems); // } // } // } // else if (reader == pointsReader_) { // SamplePoints points; // SampleInfo info; // while (reader->take_next_sample(&points, &info) == ReturnCode_t::RETCODE_OK) { // if (info.valid_data) { // auto dataItems = pointsToDataItems(points); // emit onDataUpdated(dataItems); // } // } // } } /** * @brief 将采样机状态转换为数据项列表 * @param infos 采样机状态数据 * @return 转换后的数据项列表 * * 按照预定义的数据点ID,将SampleInfo结构体中的状态信息转换为DataItem列表。 * 包括设备状态、位置信息、限位信息等。 */ QList AQTSampleMachinePlug::machineInfoToDataItems(const SampleMachineInfo& info) { QList items; // 按照数据点定义的顺序转换状态数据 items.append(DataItem(30, info.maskBits())); items.append(DataItem(31, info.bigPoint())); items.append(DataItem(32, info.smallPoint())); items.append(DataItem(33, info.depthPoint())); items.append(DataItem(34, info.hasToXY())); items.append(DataItem(35, info.hasSamplePoints())); items.append(DataItem(36, info.gameOver())); items.append(DataItem(37, info.backState())); items.append(DataItem(38, static_cast(info.sampleState()))); items.append(DataItem(39, static_cast(info.belt1State()))); items.append(DataItem(40, static_cast(info.belt2State()))); items.append(DataItem(41, static_cast(info.crush1State()))); items.append(DataItem(42, static_cast(info.crush2State()))); items.append(DataItem(43, static_cast(info.dividerState()))); items.append(DataItem(44, info.bigLimit())); items.append(DataItem(45, info.bigZeroLimit())); items.append(DataItem(46, info.smallLimit())); items.append(DataItem(47, info.smallZeroLimit())); items.append(DataItem(48, info.upLimit())); items.append(DataItem(49, info.downLimit())); items.append(DataItem(50, info.version())); return items; } // Config 相关转换 SampleConfig AQTSampleMachinePlug::dataItemsToConfig(const QList& items) { SampleConfig config; for (const auto& item : items) { switch (item.ID) { case 1: config.unifiedDepth(item.value.toUInt()); break; case 2: config.sampleOrder(item.value.toUInt()); break; default: break; } } return config; } QList AQTSampleMachinePlug::configToDataItems(const SampleConfig& config) { QList items; items.append(DataItem(1, config.unifiedDepth())); items.append(DataItem(2, config.sampleOrder())); return items; } // Command 相关转换 SampleCommand AQTSampleMachinePlug::dataItemsToCommand(const QList& items) { SampleCommand command; command.maskBits(0); // 初始化掩码为0 for (const auto& item : items) { switch (item.ID) { case 10: // 命令掩码 command.maskBits(item.value.toUInt()); break; case 11: // 控制类型 command.controlType(item.value.toUInt()); command.maskBits(command.maskBits() | 0x00000002); break; case 12: // 开始采样 command.startSample(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000004); break; case 13: // 停止采样 command.stopSample(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000008); break; case 14: // 复位 command.reset(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000010); break; case 15: // 位置检查 command.positionCheck(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000020); break; case 16: // 紧急上升 command.emergencyUp(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000040); break; case 17: // 紧急停止 command.emergencyStop(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000080); break; case 18: // 控制传送带1 command.controlBelt1(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000100); break; case 19: // 控制传送带2 command.controlBelt2(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000200); break; case 20: // 控制破碎机1 command.controlCrush1(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000400); break; case 21: // 控制破碎机2 command.controlCrush2(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00000800); break; case 22: // 控制分流器 command.controlDivider(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00001000); break; case 23: // 控制分煤器 command.controlCoalDistributor(static_cast(item.value.toInt())); command.maskBits(command.maskBits() | 0x00002000); break; case 24: // 减速比 command.reductionRatio(item.value.toUInt()); command.maskBits(command.maskBits() | 0x00004000); break; case 25: // 版本号 command.version(item.value.toUInt()); command.maskBits(command.maskBits() | 0x00008000); break; } } return command; } QList AQTSampleMachinePlug::commandToDataItems(const SampleCommand& command) { QList items; items.append(DataItem(10, command.maskBits())); items.append(DataItem(11, command.controlType())); items.append(DataItem(12, static_cast(command.startSample()))); items.append(DataItem(13, static_cast(command.stopSample()))); items.append(DataItem(14, static_cast(command.reset()))); items.append(DataItem(15, static_cast(command.positionCheck()))); items.append(DataItem(16, static_cast(command.emergencyUp()))); items.append(DataItem(17, static_cast(command.emergencyStop()))); items.append(DataItem(18, static_cast(command.controlBelt1()))); items.append(DataItem(19, static_cast(command.controlBelt2()))); items.append(DataItem(20, static_cast(command.controlCrush1()))); items.append(DataItem(21, static_cast(command.controlCrush2()))); items.append(DataItem(22, static_cast(command.controlDivider()))); items.append(DataItem(23, static_cast(command.controlCoalDistributor()))); items.append(DataItem(24, command.reductionRatio())); items.append(DataItem(25, command.version())); return items; } // Points 相关转换 SamplePoints AQTSampleMachinePlug::dataItemsToPoints(const QList& items) { SamplePoints points; for (const auto& item : items) { switch (item.ID) { case 51: points.totalPoints(item.value.toUInt()); break; case 52: points.x1(item.value.toUInt()); break; case 53: points.y1(item.value.toUInt()); break; case 54: points.z1(item.value.toUInt()); break; case 55: points.o1(static_cast(item.value.toInt())); break; case 56: points.x2(item.value.toUInt()); break; case 57: points.y2(item.value.toUInt()); break; case 58: points.z2(item.value.toUInt()); break; case 59: points.o2(static_cast(item.value.toInt())); break; case 60: points.x3(item.value.toUInt()); break; case 61: points.y3(item.value.toUInt()); break; case 62: points.z3(item.value.toUInt()); break; case 63: points.o3(static_cast(item.value.toInt())); break; case 64: points.x4(item.value.toUInt()); break; case 65: points.y4(item.value.toUInt()); break; case 66: points.z4(item.value.toUInt()); break; case 67: points.o4(static_cast(item.value.toInt())); break; case 68: points.x5(item.value.toUInt()); break; case 69: points.y5(item.value.toUInt()); break; case 70: points.z5(item.value.toUInt()); break; case 71: points.o5(static_cast(item.value.toInt())); break; case 72: points.x6(item.value.toUInt()); break; case 73: points.y6(item.value.toUInt()); break; case 74: points.z6(item.value.toUInt()); break; case 75: points.o6(static_cast(item.value.toInt())); break; } } return points; } QList AQTSampleMachinePlug::pointsToDataItems(const SamplePoints& points) { QList items; items.append(DataItem(51, points.totalPoints())); items.append(DataItem(52, points.x1())); items.append(DataItem(53, points.y1())); items.append(DataItem(54, points.z1())); items.append(DataItem(55, static_cast(points.o1()))); items.append(DataItem(56, points.x2())); items.append(DataItem(57, points.y2())); items.append(DataItem(58, points.z2())); items.append(DataItem(59, static_cast(points.o2()))); items.append(DataItem(60, points.x3())); items.append(DataItem(61, points.y3())); items.append(DataItem(62, points.z3())); items.append(DataItem(63, static_cast(points.o3()))); items.append(DataItem(64, points.x4())); items.append(DataItem(65, points.y4())); items.append(DataItem(66, points.z4())); items.append(DataItem(67, static_cast(points.o4()))); items.append(DataItem(68, points.x5())); items.append(DataItem(69, points.y5())); items.append(DataItem(70, points.z5())); items.append(DataItem(71, static_cast(points.o5()))); items.append(DataItem(72, points.x6())); items.append(DataItem(73, points.y6())); items.append(DataItem(74, points.z6())); items.append(DataItem(75, static_cast(points.o6()))); return items; } // MachineInfo 相关转换 SampleMachineInfo AQTSampleMachinePlug::dataItemsToMachineInfo(const QList& items) { SampleMachineInfo info; for (const auto& item : items) { switch (item.ID) { case 30: info.maskBits(item.value.toUInt()); break; case 31: info.bigPoint(item.value.toUInt()); break; case 32: info.smallPoint(item.value.toUInt()); break; case 33: info.depthPoint(item.value.toUInt()); break; case 34: info.hasToXY(item.value.toUInt()); break; case 35: info.hasSamplePoints(item.value.toUInt()); break; case 36: info.gameOver(item.value.toUInt()); break; case 37: info.backState(item.value.toUInt()); break; case 38: info.sampleState(static_cast(item.value.toInt())); break; case 39: info.belt1State(static_cast(item.value.toInt())); break; case 40: info.belt2State(static_cast(item.value.toInt())); break; case 41: info.crush1State(static_cast(item.value.toInt())); break; case 42: info.crush2State(static_cast(item.value.toInt())); break; case 43: info.dividerState(static_cast(item.value.toInt())); break; case 44: info.bigLimit(item.value.toUInt()); break; case 45: info.bigZeroLimit(item.value.toUInt()); break; case 46: info.smallLimit(item.value.toUInt()); break; case 47: info.smallZeroLimit(item.value.toUInt()); break; case 48: info.upLimit(item.value.toUInt()); break; case 49: info.downLimit(item.value.toUInt()); break; case 50: info.version(item.value.toUInt()); break; } } return info; } bool AQTSampleMachinePlug::writeToPLC(const SampleConfig& config) { if (!plcConnected_) return false; // 准备数据缓冲区 uint8_t buffer[32]; memset(buffer, 0, sizeof(buffer)); // 填充数据 *reinterpret_cast(buffer) = config.unifiedDepth(); *reinterpret_cast(buffer + 2) = config.sampleOrder(); // 写入 PLC return plc_.writeDB(PLCDataAreas::DB_CONFIG, 0, sizeof(buffer), buffer); } bool AQTSampleMachinePlug::readFromPLC(SampleMachineInfo& info) { if (!plcConnected_) return false; // 准备数据缓冲区 uint8_t buffer[64]; // 从 PLC 读取数据 if (!plc_.readDB(PLCDataAreas::DB_STATUS, 0, sizeof(buffer), buffer)) { return false; } // 解析数据 info.maskBits(*reinterpret_cast(buffer)); info.bigPoint(*reinterpret_cast(buffer + 4)); info.smallPoint(*reinterpret_cast(buffer + 6)); // ... 解析其他数据 return true; } bool AQTSampleMachinePlug::config() { if (!configDialog_) { configDialog_ = new ConfigDialog(); connect(configDialog_, &ConfigDialog::configFinished, this, &AQTSampleMachinePlug::onDialogConfigurationChanged); } configDialog_->show(); return true; } void AQTSampleMachinePlug::onDialogConfigurationChanged(const QString& config) { if (applyConfiguration(config)) { emit onConfigurationChanged(config); } } bool AQTSampleMachinePlug::applyConfiguration(const QString& configuration) { QJsonDocument doc = QJsonDocument::fromJson(configuration.toUtf8()); if (!doc.isObject()) { qDebug() << "Invalid configuration format"; return false; } QJsonObject config = doc.object(); // 断开现有连接 if (plcConnected_) { plc_.disconnect(); plcConnected_ = false; } // 应用新配置 plcConnected_ = plc_.connect( config["ip"].toString().toStdString().c_str(), config["rack"].toInt(), config["slot"].toInt() ); if (!plcConnected_) { qDebug() << "Failed to connect to PLC with new configuration"; return false; } // 更新数据块配置 PLCDataAreas::DB_CONFIG = config["db_config"].toInt(); PLCDataAreas::DB_COMMAND = config["db_command"].toInt(); PLCDataAreas::DB_STATUS = config["db_status"].toInt(); PLCDataAreas::DB_POINTS = config["db_points"].toInt(); return true; }