#include "Document.h" #include "VPGlobal.h" #include "GvlManager.h" #include "PouManager.h" #include "TaskManager.h" #include "UiManager.h" #include "WindowAppBlockPort.h" #include "WindowAppBlockGoto.h" #include "WindowAppBlockParallel.h" #include "WindowAppBlockWait.h" #include "WindowAppResourceManagerTree.h" #include "WindowAppGvlView.h" #include "WindowAppPouFrame.h" #include "WindowAppTaskView.h" #include "WindowAppMdiFrame.h" #include "WindowAppUiFrame.h" #include "WindowAppBlockParallel.h" #include "WindowAppItemLink.h" #include "WindowAppItemInterface.h" #include "WindowAppPouScene.h" #include "DialogNewPou.h" #include "ToolDepository.h" #include "Preferences.h" extern ToolDepository toolDepository; // 存档文件的主版本(主版本号不同代表不能兼容) #define DOC_MAJOR_VERSION 3 // 存档文件的次版本(仅次版本号不同则可以兼容) #define DOC_MINOR_VERSION 104 // 硬件组态文件的主版本(主版本号不同代表不能兼容) #define DOC_HW_MAJOR_VERSION 3 // 硬件组态文件的次版本(仅次版本号不同则可以兼容) #define DOC_HW_MINOR_VERSION 102 /* 需要额外特殊处理的几种类型 (1) 系统变量:不参与序列化,但是每次重置时需要恢复到默认值,并且程序启动时无论创建项目与否都自动建立变量; (2) Pou内部变量:不参与序列化,重置时参数恢复默认值,随Pou创建时自动创建,随Pou删除时删除; (3) 任务监视窗体:不参与序列化,重置时参数恢复默认值并清空界面,并且在删除全部窗体时不删除此窗体; (4) 日志信息窗体:不参与序列化,重置时参数恢复默认值并清空界面,并且在删除全部窗体时不删除此窗体; (5) 硬件组态:进行独立序列化(固定名称文件),重置时参数恢复默认值,并且在删除全部窗体时不删除此窗体; */ /// /// 序列化 PORT_BIND_INFO /// /// QDataStream& operator<<(QDataStream& out, const _tagDocBindToolInfo& info) { out << info.strSrcPou; out << info.strSourceTool; out << info.strDestPou; out << info.strDestInf; out << info.infType; return out; } /// /// 反序列化 PORT_BIND_INFO /// /// /// /// QDataStream& operator>>(QDataStream& in, _tagDocBindToolInfo& info) { in >> info.strSrcPou; in >> info.strSourceTool; in >> info.strDestPou; in >> info.strDestInf; in >> info.infType; return in; } Document::Document() { VPGlobal::m_pDoc = this; } Document::~Document() { } /// /// 执行存档文件保存 /// /// bool Document::Save(const QString& strFullPath) { qDebug() << "[Documents] Document save."; // 根据用户选定的路径生成临时文件名,等待压缩之后形成正式的存档文件 QString strSaveFullPath = strFullPath.left(strFullPath.length() - DOC_POSTFIX.length()); strSaveFullPath += TMP_POSTFIX; // 建立存档文件 QFile fileOut(strSaveFullPath); if (!fileOut.open(QFile::WriteOnly | QFile::Truncate)) { Utility::VPCriticalMessageBox(strSaveFullPath + " create failed!"); return false; } // 创建序列化对象 QDataStream out(&fileOut); // 设置版本 out.setVersion(QDataStream::Qt_5_14); // 写入当前版本号 saveVersion(out, DOC_MAJOR_VERSION, DOC_MINOR_VERSION); // 写入系统全局配置区段 saveConfig(out); out << (QString)"saveConfig " + LOAD_DOC_KEYWORDS; // 写入全局和局部变量区段 saveVariables(out); out << (QString)"saveVariables " + LOAD_DOC_KEYWORDS; // 写入POU区段 savePous(out); out << (QString)"savePous " + LOAD_DOC_KEYWORDS; // 写入Link区段 saveLinks(out); out << (QString)"saveLinks " + LOAD_DOC_KEYWORDS; // 保存Task区段 saveTasks(out); out << (QString)"saveTasks " + LOAD_DOC_KEYWORDS; // 保存UI区段 saveUIs(out); out << (QString)"saveUIs " + LOAD_DOC_KEYWORDS; //// 写入硬件区段 //saveHardwares(out); // 关闭文件 fileOut.close(); qDebug() << ("[Document] Serialized to " + strSaveFullPath + " finished."); // 执行压缩 compress(strSaveFullPath); qDebug() << ("[Document] Comressed to " + strSaveFullPath + " finished."); Utility::VPInformationMessageBox("Serialized to " + strSaveFullPath + " finished. DocVersion: " + QString::number(thePrefs.m_nDocVersion)); return true; } /// /// 2022-2-24,执行硬件组态数据保存 /// /// /// bool Document::saveHdw(const QString& strFullPath) { qDebug() << "[Documents] Document harware save."; QFileInfo FileInfo(strFullPath); // 建立存档文件 QFile fileOut(strFullPath); if (!fileOut.open(QFile::WriteOnly | QFile::Truncate)) { Utility::VPCriticalMessageBox(strFullPath + " create failed!"); return false; } // 创建序列化对象 QDataStream out(&fileOut); // 设置版本 out.setVersion(QDataStream::Qt_5_14); // 写入硬件组态文件版本号 saveHdwVersion(out, DOC_HW_MAJOR_VERSION, DOC_HW_MINOR_VERSION); if (thePrefs.m_strHdwConfigID.isEmpty()) { thePrefs.m_strHdwConfigID = "123456789"; } out << thePrefs.m_strHdwConfigID; // 保存硬件组态Pou相关信息 savePou(GROUP_NAME_HARDWARE, out); out << (QString)"savePou " + LOAD_DOC_KEYWORDS; // 保存硬件组态局部变量 saveGVL(GROUP_NAME_HARDWARE, out); out << (QString)"saveGVL " + LOAD_DOC_KEYWORDS; fileOut.close(); qDebug() << ("[Document] Serialized hardware to " + strFullPath + " finished. DocVersion: " + QString::number(thePrefs.m_nDocVersion)); return true; } /// /// 整个系统重置(包括界面和UI) /// void Document::systemReset() { qDebug() << "[Document] System reset."; // 先重置所有的数据结构 g_pGvlManager->reset(); // 重置树状结构和子页面 g_pResourceManager->reset(); // 重置Task数据结构 g_pTaskManager->reset(); //// 重置硬件页面 //VPGlobal::getWindowHdw()->reset(); // 重置所有的Pou数据结构 g_pPouManager->resetAllPous(); // 重置所有UI窗体 g_pUiManager->reset(); //// 重置Link信息 //m_Links.clear(); // 重置Port绑定信息 m_PortBindInfos.clear(); // 重置Goto绑定信息 m_GotoBindInfos.clear(); // 重置Para绑定信息 m_ParallelInfos.clear(); } /// /// 执行存档文件加载 /// /// bool Document::Load(const QString& strFullPath) { qDebug() << "[Document] Load file : " << strFullPath; QString strLoadFullPath = strFullPath; // 解压缩 if (! uncompress(strLoadFullPath)) { return false; } // 打开存档文件 QFile fileIn(strLoadFullPath); if (!fileIn.open(QFile::ReadOnly)) { // Utility::VPCriticalMessageBox(strLoadFullPath + " open failed!"); return false; } // 序列化对象 QDataStream in(&fileIn); // 设置版本 in.setVersion(QDataStream::Qt_5_14); // 读取文档版本号 bool bRet = loadAndCheckVersion(in, DOC_MAJOR_VERSION, DOC_MINOR_VERSION); // 如果版本号有问题则不读取 if (!bRet) { Utility::VPCriticalMessageBox("The document version is incompatible!"); return false; } QString strInfo; // 首先重置系统环境,准备加载新的数据 this->systemReset(); // 读取配置信息 if (! this->loadConfig(in)) { fileIn.close(); return false; } // 检查硬件组态的配置ID与 文档中存储的配置ID是否匹配 thePrefs.m_strHdwConfigID = getHWAndDocConfigID(); if (! checkHWAndDocConfigID(m_strHdwConfigID, thePrefs.m_strHdwConfigID)) { fileIn.close(); return false; } in >> strInfo; if (strInfo.indexOf(LOAD_DOC_KEYWORDS) == -1) { vWarning() << "loadVariables Error"; return false; } // 读取全局和局部变量区段 this->loadVariables(in); in >> strInfo; if (strInfo.indexOf(LOAD_DOC_KEYWORDS) == -1) { vWarning() << "loadVariables Error"; return false; } // 读取Pou区段 this->loadPous(in); in >> strInfo; if (strInfo.indexOf(LOAD_DOC_KEYWORDS) == -1) { vWarning() << "loadPous Error"; return false; } // 读取Link区段 this->loadLinks(in); in >> strInfo; if (strInfo.indexOf(LOAD_DOC_KEYWORDS) == -1) { vWarning() << "loadLinks Error"; return false; } // 读取Task区段 this->loadTasks(in); in >> strInfo; if (strInfo.indexOf(LOAD_DOC_KEYWORDS) == -1) { vWarning() << "loadTasks Error"; return false; } // 读取UI区段 this->loadUIs(in); in >> strInfo; if (strInfo.indexOf(LOAD_DOC_KEYWORDS) == -1) { vWarning() << "loadUIs Error"; return false; } // 至此所有的数据都已经读取完毕,开始执行还原工作 fileIn.close(); // 还原Port绑定关系 this->restorePortBindInfo(); // 还原Goto绑定关系 this->restoreGotoBindInfo(); //// 还原Link信息 //this->restoreLinks(); // 还原ParallelTool相关信息 this->restoreParallelInfos(); // 读取完毕后,删除此临时文件 QFile::remove(strLoadFullPath); Utility::VPInformationMessageBox("Deserialized from " + strFullPath + " finished. DocVersion: " + QString::number(thePrefs.m_nDocVersion) ); return true; } /// /// 2022-2-24增加,加载硬件组态存档数据 /// /// /// bool Document::loadHdw(const QString& strFullPath) { qDebug() << "[Document] Load hardware file : " << strFullPath; // 打开存档文件 QFile fileIn(strFullPath); if (!fileIn.open(QFile::ReadOnly)) { // Utility::VPCriticalMessageBox(strFullPath + " open failed!"); qDebug() << "[Error] Document::loadHdw - load[" << strFullPath << "] failed."; return false; } // 序列化对象 QDataStream in(&fileIn); // 设置版本 in.setVersion(QDataStream::Qt_5_14); // 读取文档版本号 bool bRet = loadAndCheckHdwVersion(in, DOC_HW_MAJOR_VERSION, DOC_HW_MINOR_VERSION); // 如果版本号有问题则不读取 if (!bRet) { Utility::VPCriticalMessageBox("The hardware document version is incompatible!"); return false; } // 旧版本的系统,不能加载配置文件ID if (thePrefs.m_nHdwVersion != 0) { in >> thePrefs.m_strHdwConfigID; } // 首先应该清理硬件组态的所有数据 { POU* pHdPou = g_pPouManager->getPouByName(GROUP_NAME_HARDWARE); if (pHdPou) { pHdPou->reset(); } } QString strInfo; // 加载硬件组态 loadPou(in, GROUP_NAME_HARDWARE); in >> strInfo; if (strInfo.indexOf(LOAD_DOC_KEYWORDS) == -1) { vWarning() << "load Hdw Pou Error"; return false; } // 加载硬件组态局部变量 loadGVL(in, GROUP_NAME_HARDWARE); in >> (QString&)strInfo; if (strInfo.indexOf(LOAD_DOC_KEYWORDS) == -1) { vWarning() << "load Hdw GVL Error"; return false; } fileIn.close(); return true; } //======================================================== // // Version // //======================================================== /// /// 写入当前版本号 /// /// bool Document::saveVersion(QDataStream& out, int major, int minor) { qDebug() << "[Documents] Save [Version]: " + QString::number(major) + "." + QString::number(minor); out << major; out << minor; // 设置文档的版本号 thePrefs.m_nDocVersion = minor; int paranum;//参数数量 { paranum = 1; out << paranum;//先保存参数数量 out << (int)1 << thePrefs.m_nDocVersion; } return true; } /// /// 写入当前版本号 /// /// bool Document::saveHdwVersion(QDataStream& out, int major, int minor) { qDebug() << "[Documents] Save [Version]: " + QString::number(major) + "." + QString::number(minor); out << major; out << minor; // 设置文档的版本号 thePrefs.m_nHdwVersion = minor; int paranum;//参数数量 { paranum = 1; out << paranum;//先保存参数数量 out << (int)1 << thePrefs.m_nHdwVersion; } return true; } /// /// 加载当前版本号 /// /// /// bool Document::loadAndCheckVersion(QDataStream& in, int major, int minor) { int nMajorVersion = 0, nMinorVersion = 0; in >> nMajorVersion; in >> nMinorVersion; int nDocVersion = 0; if (nMinorVersion > 0) { int paranum = 0; int para = 0; in >> paranum;//读取参数数量 for (int i = 0; i < paranum; i++) { in >> para; switch (para) { case 1: in >> nDocVersion; break; default: { vWarning() << "Serialized(In) Error"; return false; } break; } } } thePrefs.m_nDocVersion = nDocVersion; // 比较大版本号是否一致 if (nMajorVersion != major) { QString strDocVer = QString::number(nMajorVersion) + "." + QString::number(nMinorVersion); QString strExeVer = QString::number(major) + "." + QString::number(minor); Utility::VPCriticalMessageBox("Incompatible document version: [exe]" + strDocVer + " " + "[Doc]" + strExeVer); return false; } qDebug() << "[Documents] Load [Version]: " + QString::number(nMajorVersion) + "." + QString::number(nMinorVersion); return true; } /// /// 加载当前版本号 /// /// /// bool Document::loadAndCheckHdwVersion(QDataStream& in, int major, int minor) { int nMajorVersion = 0, nMinorVersion = 0; in >> nMajorVersion; in >> nMinorVersion; int nHdwVersion = 0; if (nMinorVersion > 0) { int paranum = 0; int para = 0; in >> paranum;//读取参数数量 for (int i = 0; i < paranum; i++) { in >> para; switch (para) { case 1: in >> nHdwVersion; break; default: { vWarning() << "Serialized(In) Error"; return false; } break; } } } thePrefs.m_nHdwVersion = nHdwVersion; // 比较大版本号是否一致 if (nMajorVersion != major) { QString strDocVer = QString::number(nMajorVersion) + "." + QString::number(nMinorVersion); QString strExeVer = QString::number(major) + "." + QString::number(minor); Utility::VPCriticalMessageBox("[Hdw] Incompatible document version: [exe]" + strDocVer + " " + "[Doc]" + strExeVer); return false; } qDebug() << "[Hdw] Load [Version]: " + QString::number(nMajorVersion) + "." + QString::number(nMinorVersion); return true; } //======================================================== // // Config // //======================================================== /// /// 写入系统全局配置区段 /// /// bool Document::saveConfig(QDataStream& out) { int paranum = 1; thePrefs.m_strHdwConfigID = getHWAndDocConfigID(); //先保存参数数量 out << paranum; // 硬件ConfigID out << 1 << thePrefs.m_strHdwConfigID; return true; } /// /// 读取系统全局配置区段 /// /// /// bool Document::loadConfig(QDataStream& in) { // 旧版本的系统,不能加载配置文件 if (thePrefs.m_nDocVersion == 0) { return true; } int paranum = 0; int para = 0; // 首先读取参数数量 in >> paranum; for (int i = 0; i < paranum; i++) { in >> para; switch (para) { case 1: in >> m_strHdwConfigID; break; default: { vWarning() << "Serialized(In) Error"; return false; } break; } } return true; } //======================================================== // // Variables // //======================================================== /// /// 写入全局和局部变量区段 /// /// bool Document::saveVariables(QDataStream& out) { qDebug() << "[Documents] Save GVLs..."; //// 遍历所有变量进行储存 //QHash* pAllVariables = g_pVariablesManager->getAllGvls(); //// 写入总数量 //out << pAllVariables->size(); // 2022-2-22,这里需要跳过全局变量分组以及硬件分组 QList AllGvls = g_pGvlManager->getAllSerializedGvls(); // 写入总数量 out << AllGvls.size(); QListIterator it(AllGvls); while (it.hasNext()) { saveGVL(it.next()->strName, out); } return true; } /// /// 写入单组变量 /// /// /// /// bool Document::saveGVL(const QString strGroupName, QDataStream& out) { const GVL* pGvl = g_pGvlManager->getGvl(strGroupName); if (pGvl == nullptr) { qDebug() << "[Documents] Gvl[" << strGroupName << "] hasn't any variable."; out << 0; return false; } else { out << 1; } int nParanum = 4;//参数数量 out << nParanum;//先保存参数数量 out << nParanum;//此处写两次,用来后面做数据校验 out << (int)1 << pGvl->Type; out << (int)2 << pGvl->strName; out << (int)3 << pGvl->gvlMode; out << (int)4 << pGvl->strHdwInstName; // 继续保存其中变量 const VARIABLES& vars = pGvl->Variables; // 以 bShow 为标记为,判断内部变量,并排除掉 int nSize = 0; for (int i = 0; i < vars.size(); i++) { if (vars[i]->bShow == true) { nSize++; } } // 写入变量数量 out << nSize; //// 2022-2-22修改,只保存序列化的变量 //const VARIABLES& vars = g_pGvlManager->getSerializedVariablesByGroup(pGvl->strName); //// 写入变量数量 //out << vars.size(); // 写入变量细节 for (const VARIABLE* var : vars) { if (var->bShow) { this->saveInterface(out, var); // 2022-9-2 增加,保存变量的触发状态 if (var->eventTrigger != nullptr) { out << 1; } else { out << 0; } } } qDebug() << "[Documents] Saved GVL Group[" << pGvl->strName << "]: " << nSize << " counts."; return true; } /// /// 读取全局和局部变量区段 /// /// /// bool Document::loadVariables(QDataStream& in) { qDebug() << "[Documents] Load GVLs..."; // 读入分组个数 int nGroup = 0; in >> nGroup; // 循环读取所有变量分组 for (int i = 0; i < nGroup; i++) { // 逐一读取每一个变量组 loadGVL(in); } return true; } /// /// 读取单组的变量信息 /// /// /// bool Document::loadGVL(QDataStream& in, const QString& strLoadName, bool bLoadName) { // 首先获取本Group是否有局部变量信息 int nContinue = 0; in >> nContinue; if (nContinue == 0) { return true; } // 变量分组名称 QString strGroupName; QString strHdwInstName; // 变量类型 TOOL_TYPE type = TOOL_TYPE::TOOL_TYPE_STANDARD; GVL_MODE gvlType = GVL_MODE::GVL_BASIC; int nPara = 0; int nParanum = 0; in >> nParanum;//读取参数数量 // 此处的校验值 int nCheck = 0; in >> nCheck;//读取校验参数 int nMaxParanum = 4;// 该参数的最大数量(需要根据参数的数量来调整,用来校验) if (nParanum < 0 || nParanum != nCheck || nParanum > nMaxParanum) { return false; } for (int i = 0; i < nParanum; i++) { in >> nPara; switch (nPara) { case 1: in >> type; break; case 2: in >> strGroupName; break; case 3: in >> gvlType; break; case 4: in >> strHdwInstName; break; default: { vWarning() << "GVL Serialized(In) Error"; } break; } } if (!bLoadName) { strGroupName = strLoadName; } // 根据不同的类型添加不同的页面 QWidget* pNewView = nullptr; // 如果是全局变量,则需要创建全局变量视图 if (type == TOOL_TYPE::TOOL_TYPE_GLOBAL_VARIABLE) { DllTool* pHdwTool = nullptr; TOOL* pTool = nullptr; if (gvlType == GVL_MODE::GVL_DB) { POU* pHdwPou = g_pPouManager->getHdwPou(); pTool = pHdwPou->GetToolByName(strHdwInstName); if (pTool == nullptr) { vWarning() << strHdwInstName << " not found in hardware configuration "; } else { pHdwTool = pTool->pDllPtr; } } // 创建全局变量视图 pNewView = (WindowAppGvlView*)(g_pResourceManager->addGvlNode(strGroupName, gvlType, pHdwTool, false)); g_pGvlManager->bindHdwToolAndDB(pTool, strGroupName); } // 局部变量则创建Pou视图 else if (type == TOOL_TYPE::TOOL_TYPE_LOCAL_VARIABLE) { // 创建Pou视图 pNewView = (WindowAppPouFrame*)(g_pResourceManager->addPouNode(strGroupName)); } int nVarCount = 0; in >> nVarCount; // 循环读取所有变量 for (int j = 0; j < nVarCount; j++) { VARIABLE* pNewVariable = new VARIABLE(nullptr); // in >> *pNewVariable; // 读取变量 this->loadInterface(in, pNewVariable); // 2022-9-27,由于index目前不参与序列化了,所以需要根据手动重新设置index(不过变量的Index基本没有用到) pNewVariable->nIndex = j; // 2022-9-2 增加,如果变量是带有触发事件的,需要额外生成一下触发事件 int nTigger; in >> nTigger; if (nTigger == 1) { pNewVariable->eventTrigger = new ToolEvent(strGroupName, pNewVariable->strName, TOOL_EVENT_TYPE::TASK_TRIGGER); } // 直接读取,但是无需执行任何操作 // Tip:因为变量会作为Port的接口被绑定,在后续保存Tool信息的时候会重新保存一下link信息 // loadLinks(in, pNewVariable->strFullName, nullptr, false, LINK_MODE::LINK_NORMAL); // 数据结构中添加本变量信息 g_pGvlManager->addNewVariable(strGroupName, pNewVariable); // 在界面中添加新的变量 if (type == TOOL_TYPE::TOOL_TYPE_GLOBAL_VARIABLE) { ((WindowAppGvlView*)pNewView)->addNewVariable(pNewVariable); } else if (type == TOOL_TYPE::TOOL_TYPE_LOCAL_VARIABLE) { ((WindowAppPouFrame*)pNewView)->addNewVariable(pNewVariable); } //// 2022-3-3 如果是DB模式的,还需要额外再读取一下DB和硬件Tool的绑定关系 //if (pNewVariable->pParentTool->gvlMode == GVL_MODE::GVL_DB) //{ // QString strHdwName; // in >> strHdwName; // POU* pHdwPou = g_pPouManager->getHdwPou(); // TOOL* HdwTool = pHdwPou->GetToolByName(strHdwName); // DllTool* pHdwTool = HdwTool->pDllPtr; // g_pGvlManager->bindHdwToolAndDB(pHdwTool, strGroupName); //} } qDebug() << "[Documents] Loaded GVL group[" << strGroupName << "]: " << nVarCount << " counts."; return true; } //======================================================== // // Pous // //======================================================== /// /// 写入Pou区段 /// /// /// bool Document::savePous(QDataStream& out) { qDebug() << "[Documents] Save POUs..."; // 获取全部Pou const QMap& allPous = g_pPouManager->getAllPous(); // 写入Pou总数量 out << allPous.size() - 1; // 遍历所有的Pou信息 QMapIterator it(allPous); while (it.hasNext()) { it.next(); // 2022-2-24 跳过硬件Pou if (it.key() != GROUP_NAME_HARDWARE) { savePou(it.key(), out); } } return true; } /// /// 写入单个Pou /// /// /// /// bool Document::savePou(const QString strPouName, QDataStream& out) { // 写入本Pou的分组名称 out << strPouName; // 逐个保存Pou信息 POU* pPou = g_pPouManager->getPouByName(strPouName); if (pPou == nullptr) { qDebug() << "[Documents] save pou, but can't find pou[" << strPouName << "]."; return false; } // 2022-8-30 增加,首先序列化Pou界面相关参数 out << pPou->parentScene()->parentFrame(); // 遍历本Pou下的所有工具和接口 const QMap& allTools = pPou->GetAllTools(); // 写入总数量 out << allTools.size(); QMapIterator i(allTools); while (i.hasNext()) { TOOL* pTool = i.next().value(); // 保存Tool信息 this->saveTool(out, pTool); // 保存此Tool在界面中的坐标 out << pPou->GetToolItemPos(pTool->strInstanceName); // 如果是Port类型,还需要额外存储一下Port的绑定关系 if (pTool->isPortTool()) { this->savePortBindInfo(out, pTool); } // 如果是Goto类型,还需要额外存储一下Goto的绑定关系 else if (pTool->isGotoTool()) { this->saveGotoBindInfo(out, pTool); } // 2022-9-4,如果是Wait类型,还需要额外存储一下Wait工具的相关参数 else if (pTool->isWaitTool()) { this->saveWaitToolSettings(out, pPou, pTool); } //// 如果是Parallel类型,还需要额外存储一下Parallel的绑定关系 //else if (pTool->isParallelTool()) //{ // this->saveParallelBindInfo(out, pTool); //} else { // 2021-8-14增加,标准工具则再保存dll中的内部变量 if (pTool->pDllPtr) { // 工具序列化出错的时候,会抛出异常,需要退出后续序列化 try { pTool->pDllPtr->SerializedToDoc(out); } catch (...) { return false; } } } qDebug() << "[Documents] Saved " << pTool->strInstanceName << ": " << pTool->Interfaces.size() << " interfaces."; } return true; } /// /// 读取Pou区段 /// /// /// bool Document::loadPous(QDataStream& in) { qDebug() << "[Documents] Load POUs..."; // 读入Pou总数量 int nPouCount = 0; in >> nPouCount; // 循环读取所有Pou分组 for (int i = 0; i < nPouCount; i++) { loadPou(in); } return true; } /// /// 读取单组的Pou相关信息 /// /// /// 之前已经加载过的Pou分组名字 /// 本次是否还需要继续加载名字 /// bool Document::loadPou(QDataStream& in, const QString& strLoadName, bool bLoadName) { // 创建对应的Pou页面 WindowAppPouFrame* pouFrame = nullptr; // 2021-8-24增加,由于单独导入导出Pou的时候,需要提前加载一下名字 // 用于判断当前加载的Pou节点是否已经存在 // Pou分组名称 QString strPouName = strLoadName; if (bLoadName) { in >> strPouName; } // 创建Pou视图 pouFrame = qobject_cast(g_pResourceManager->addPouNode(strPouName, false)); // 2022-8-30 增加,首先完成pouFrame的参数序列化 in >> pouFrame; // 读入工具数量 int nToolCount = 0; in >> nToolCount; // 逐个生成对应的工具 for (int j = 0; j < nToolCount; j++) { // 根据信息创建新的工具 TOOL* pNewTool = new TOOL(); // 加载本工具信息 bool state = this->loadTool(in, pNewTool); if (!state) { vWarning() << "[Error] Document::loadPou - LoadTools Error in " << strPouName; return false; } // 读取本Tool的屏幕坐标 QPointF toolPos; in >> toolPos; // 如果本工具是Port类型,还需要额外读取Port绑定信息 if (pNewTool->isPortTool()) { this->loadPortBindInfo(in, strLoadName, bLoadName); } // 如果本工具是Goto类型,还需要额外读取Goto绑定信息 else if (pNewTool->isGotoTool()) { this->loadGotoBindInfo(in); } // 如果是Parallel工具,额外保存并行工具信息用于最后执行恢复 else if (pNewTool->isParallelTool()) { this->loadParallelInfos(in, pNewTool); } // 2022-6-12 直接用Scene指针创建对应的工具Block g_pPouManager->getPouSceneByName(strPouName)->addToolItem(pNewTool, toolPos, true); // 2021-8-14增加,保存dll中的内部变量(仅限标准工具) if (pNewTool->isStandardTool()) { if (pNewTool->pDllPtr == nullptr) { return false; } this->loadFromToolDll(in ,pNewTool); } // 2022-9-4,如果是Wait工具,在功能块创建完毕之后,还需要额外读取一下Wait工具的相关参数保存到功能块中 else if (pNewTool->isWaitTool()) { this->loadWaitToolSettings(in, g_pPouManager->getPouByName(strPouName), pNewTool); } } qDebug() << "[Documents] Load pou [" + strPouName + "] finished."; return true; } /// /// 从Tool的Dll中反序列化 /// /// /// void Document::loadFromToolDll(QDataStream& in, TOOL*& pNewTool) { pNewTool->pDllPtr->SerializedFromDoc(in); //QFuture future = QtConcurrent::run( // pNewTool->pDllPtr, // &DllTool::SerializedFromDoc, // in //); // //while (true) //{ // if (future.isFinished()) // { // break; // } // QCoreApplication::processEvents(); // Utility::qSleep(100); //} for (int i = 0; i < pNewTool->Interfaces.size(); i++) { const _INTERFACE* pInf = pNewTool->Interfaces[i]; // 如果是动态加载的端口,不从工具的基础端口里去查找,直接添加 if (pInf->bDynamic) { // TODO: 加载的端口,Ptr未绑定值,需要调用dll 对应的函数,来绑定变量 if (pNewTool->pDllPtr != nullptr) { DLL_INF inf; inf.strName = pInf->strName; inf.Direction = pInf->Direction; inf.Type = pInf->Type; inf.value.passMode = pInf->value.passMode; inf.value.type = pInf->value.type; inf.nIndex = pInf->nIndex; inf.bDynamic = pInf->bDynamic; pNewTool->pDllPtr->AddInterface(inf); pNewTool->pDllPtr->bindValuePtrByName(pInf->strName, pInf->nIndex); } } } } //======================================================== // // Tools // //======================================================== /// /// 写入Tool区段 /// /// bool Document::saveTool(QDataStream& out, const TOOL* pTool) { int nParanum = 11;//参数数量 out << nParanum;//先保存参数数量 out << (int)1 << pTool->Type; out << (int)2 << pTool->strPouName; out << (int)3 << pTool->strName; out << (int)4 << pTool->strAliasName; out << (int)5 << pTool->strInstanceName; out << (int)6 << pTool->strInfo; out << (int)7 << pTool->strVersion; out << (int)8 << pTool->bEnable; out << (int)9 << pTool->nIndex; out << (int)10 << pTool->strComment; out << (int)11 << pTool->bEnable; // 保存所有的接口信息 this->saveToolInterfaces(out, pTool); return true; } /// /// 读入Tool区段 /// /// /// bool Document::loadTool(QDataStream& in, TOOL*& pNewTool) { int nPara; int nParanum; in >> nParanum;//读取参数数量 // 校验值 int nMaxParanum = 11;// 该参数的最大数量(需要根据参数的数量来调整,用来校验) if (nParanum < 0 || nParanum > nMaxParanum) { return false; } for (int i = 0; i < nParanum; i++) { in >> nPara; switch (nPara) { case 1: in >> pNewTool->Type; break; case 2: in >> pNewTool->strPouName; break; case 3: in >> pNewTool->strName; break; case 4: in >> pNewTool->strAliasName; break; case 5: in >> pNewTool->strInstanceName; break; case 6: in >> pNewTool->strInfo; break; case 7: in >> pNewTool->strVersion; break; case 8: in >> pNewTool->bEnable; break; case 9: in >> pNewTool->nIndex; break; case 10: in >> pNewTool->strComment; break; case 11: in >> pNewTool->bEnable; break; default: { vWarning() << "[Error] Document::loadTool - invalid para index: " << nPara; } break; } } int nInfCount = 0; in >> nInfCount; TOOL* pTool_Dll = toolDepository.getToolByName(pNewTool->strName); if (pTool_Dll == nullptr && pNewTool->isStandardTool()) { return false; } // 处理工具改名的相关问题 if (pTool_Dll->strName != pNewTool->strName) { pNewTool->strName = pTool_Dll->strName; pNewTool->strAliasName = pTool_Dll->strAliasName; } // 读取接口信息 this->loadToolInterfaces(in, pNewTool, pTool_Dll, nInfCount); return true; } //======================================================== // // Interfaces // //======================================================== /// /// 写入所有的Interface信息 /// /// /// /// bool Document::saveToolInterfaces(QDataStream& out, const TOOL* pTool) { // 写入接口数量 out << pTool->Interfaces.size(); // 继续遍历标准接口,逐一保存 for (_INTERFACE* pInf : pTool->Interfaces) { // out << *pInf; this->saveInterface(out, pInf); } //// 如果是具备ToolStart接口的,还需要额外保存一下ToolStart接口信息 //if (pTool->startInterface != nullptr) //{ // // out << *pTool->TopInterface; // this->saveInterfaces(out, pTool->startInterface); //} //// 如果具备ToolEnd接口的,还需要额外保存一下ToolEnd接口信息 //if (pTool->endInterface != nullptr) //{ // this->saveInterfaces(out, pTool->endInterface); //} // 2022-8-25,遍历ToolInterface接口(如果有的话),逐一保存 for (const auto& toolInterface : pTool->ToolInterfaces) { this->saveInterface(out, toolInterface); } return true; } /// /// 写入Interface信息 /// /// /// /// bool Document::saveInterface(QDataStream& out, const _INTERFACE* inf) { //int nParanum = 19;//参数数量 //out << nParanum;//先保存参数数量 //out << (int)1 << inf->strName; //out << (int)2 << inf->strFullName; //out << (int)3 << inf->strNameWithType; //out << (int)4 << inf->Direction; //out << (int)5 << inf->Type; //out << (int)6 << inf->nIndex; //out << (int)7 << inf->bEnable; //out << (int)8 << inf->strComment; //out << (int)9 << inf->bSerialized; //out << (int)10 << inf->bShow; //out << (int)11 << inf->bDynamic; //out << (int)12 << (int&)inf->parent()->gvlMode; //out << (int)13 << inf->bWatch; //out << (int)14 << inf->bDataLink; //out << (int)15 << inf->valueString; //out << (int)16 << inf->bComplexLinkIndex; //out << (int)17 << inf->accessMode; //out << (int)18 << inf->strCommAddress; //out << (int)19 << inf->bShowName; // 2022-9-27,此处不再保存接口的index了 // 因为下次反序列化的时候接口可能已经变动了,index就已经不准了 int nParanum = 18;//参数数量 out << nParanum;//先保存参数数量 out << (int)1 << inf->strName; out << (int)2 << inf->strFullName; out << (int)3 << inf->strNameWithType; out << (int)4 << inf->Direction; out << (int)5 << inf->Type; out << (int)6 << inf->bEnable; out << (int)7 << inf->strComment; out << (int)8 << inf->bSerialized; out << (int)9 << inf->bShow; out << (int)10 << inf->bDynamic; out << (int)11 << (int&)inf->parent()->gvlMode; out << (int)12 << inf->bWatch; out << (int)13 << inf->bDataLink; out << (int)14 << inf->valueString; out << (int)15 << inf->bComplexLinkIndex; out << (int)16 << inf->accessMode; out << (int)17 << inf->strCommAddress; out << (int)18 << inf->bShowName; // Pou类型的接口是不存具体数值的,只有变量类型的才会存 // 2022-2-23 增加,只有基础模式的变量才需要保存值 if (inf->Type == INF_TYPE::INF_TYPE_VALUE && inf->parent()->gvlMode == GVL_MODE::GVL_BASIC ) { out << inf->value; } //// 2022-3-31 增加,Goto的输入接口也需要保存值(bool类型) //else if (inf.pParentTool->isGotoTool() // && inf.Direction == INF_DIRECTION::INF_DIR_IN ) //{ // out << inf.value; //} else { out << inf->value.passMode; out << inf->value.type; } return true; } /// /// 读取所有的Interface信息 /// /// /// /// /// bool Document::loadToolInterfaces(QDataStream& in, TOOL*& pNewTool, TOOL*& pTool_Dll, int nInfCount) { // 判断是删除了端口,还是增加了端口, // 如果新dll删除了端口,则引用就版本存储下来的端口(暂用新工具的端口,序列化上来过期的端口将被抛弃) // 如果新dll增加了端口,则将新添加的的端口插入进来,注意,新端口可能不是在末添加的,可能是任意位置 for (int i = 0; i < nInfCount; i++) { _INTERFACE* pNewInf = new _INTERFACE(pNewTool); // 2022-9-27由于目前接口index不参与序列化了,后面需要手工补一下接口序号 this->loadInterface(in, pNewInf); // 如果是动态加载的端口,不从工具的基础端口里去查找,直接添加 if (pNewInf->bDynamic || pNewTool->isPortTool()) { pNewTool->Interfaces.push_back(pNewInf); // TODO: 加载的端口,Ptr未绑定值,需要调用dll 对应的函数,来绑定变量 } else { // TODO:2022-3-31 此处的代码是有问题的,因为作为Goto这种动态绑定的端口,端口名字都会随着绑定接口的名称而改变 // 所以肯定是在toolDepository中查询不到的,并且 pTool_Dll 作为动态生成的端口,使用完毕之后需要手工删除 // 如果在工具中找到对应端口,则说明该端口正常使用中 // 端口被标记为废弃,是因为 // 1:直接删除端口的代码, // 2:端口代码没删除,而是内部标记为废弃 // 3:修改了端口的数据类型或者输入输出类型,或者数据传递类型 // INF_DISCARD Discard = INF_DISCARD::INF_MARK_DELETE; for (int j = 0; j < pTool_Dll->Interfaces.size(); j++) { _INTERFACE* pDllInf = pTool_Dll->Interfaces[j]; // 必须确保所有的类型都一样 if (pDllInf->strName == pNewInf->strName) { if (pDllInf->Direction == pNewInf->Direction && pDllInf->value.passMode == pNewInf->value.passMode && pDllInf->value.type == pNewInf->value.type ) { // 所有数据类型都校验成功,引用 DLL 实际配置的端口信息(在此接受工具开发者主动废弃端口) Discard = pDllInf->Discard; break; } // 如果数据类型和其它属性校验失败,证明数据数据已经被修改 else { Discard = INF_DISCARD::INF_MARK_CHANGE; // TODO:如果数据结构已经被修改。需要在功能块上添加一个最新的接口(从dll中加载的) //pDllInf->strName += "(new)"; //pNewTool->Interfaces.push_back(pDllInf); } } } pNewInf->Discard = Discard; pNewTool->Interfaces.push_back(pNewInf); } } // 如果dll 中添加了新的端口,则需要在pNewTool->Interfaces中,插入新添加的 if (pTool_Dll->Interfaces.size() > nInfCount) { for (int i = 0; i < pTool_Dll->Interfaces.size(); i++) { _INTERFACE* pDllInf = pTool_Dll->Interfaces[i]; // 遍历序DLL 的端口,与DOC 的对比,检查是否有新添加进来的端口 bool bIsLookupOK = false; // for (int j = 0; j < pNewTool->Interfaces.size(); j++) { _INTERFACE* pDocInf = pNewTool->Interfaces[j]; if (pDllInf->strName == pDocInf->strName && pDllInf->strNameWithType == pDocInf->strNameWithType && pDllInf->value.type == pDocInf->value.type ) { bIsLookupOK = true; } } // 如果在 Doc加载上来的端口序列中没找到,即 i 的位置插入这个端口 if (!bIsLookupOK) { pNewTool->Interfaces.insert(i, pDllInf); } } } // 2022-9-28,端口反序列化结束,并且已经和Dll比对完毕后,给所有的端口重新编一下序号 int infIndex = 0; for (int i = 0; i < pNewTool->Interfaces.size(); i++) { // 需要跳过废弃的端口 // if (pNewTool->Interfaces[i]->Discard != INF_DISCARD::INF_MARK_DELETE // && pNewTool->Interfaces[i]->Discard != INF_DISCARD::INF_MARK_DISCARD // ) { pNewTool->Interfaces[i]->nIndex = infIndex++; } } // 2022-4-7 如果是标准工具还需要继续读取ToolInterface接口的相关信息 if (pNewTool->isHaveToolInterfaces()) { pNewTool->ToolInterfaces.clear(); // 重新读取ToolInterface // (TODO:此处固定按2个接口读的,因为写入的时候并没有写入ToolInterface的数量,有隐患) for (int i = 0; i < TOOL_INTERFACE_COUNT; i++) { _INTERFACE* newInf = new _INTERFACE(pNewTool, INF_TYPE::INF_TYPE_TOOL); pNewTool->ToolInterfaces.push_back(newInf); this->loadInterface(in, pNewTool->ToolInterfaces[i]); } //// 如果本工具加入了并行组,则保存Link信息用于后续恢复 //if (pNewTool->bParallelized) //{ // // loadLinks(in, pNewTool->TopInterface->strFullName, pView, true, LINK_MODE::LINK_PARALLEL); //} //// 如果是其他情况,则跳过link的处理 //// 例如Goto工具的连接,或者是未连接等等 //else //{ // // loadLinks(in, pNewTool->TopInterface->strFullName, pView, false, LINK_MODE::LINK_NORMAL); //} } return true; } /// /// 读取Interface信息 /// /// /// /// bool Document::loadInterface(QDataStream& in, _INTERFACE*& inf) { GVL_MODE gvlMode = GVL_MODE::GVL_BASIC; int nPara; int nParanum; in >> nParanum;//读取参数数量 //// 校验 //int nMaxParanum = 19;// 该参数的最大数量(需要根据参数的数量来调整,用来校验) //if (nParanum < 0 || nParanum > nMaxParanum) //{ // return false; //} //for (int i = 0; i < nParanum; i++) //{ // in >> nPara; // switch (nPara) // { // case 1: in >> inf->strName; break; // case 2: in >> inf->strFullName; break; // case 3: in >> inf->strNameWithType; break; // case 4: in >> inf->Direction; break; // case 5: in >> inf->Type; break; // case 6: in >> inf->nIndex; break; // case 7: in >> inf->bEnable; break; // case 8: in >> inf->strComment; break; // case 9: in >> inf->bSerialized; break; // case 10: in >> inf->bShow; break; // case 11: in >> inf->bDynamic; break; // case 12: in >> (int&)gvlMode; break; // case 13: in >> inf->bWatch; break; // case 14: in >> inf->bDataLink; break; // case 15: in >> inf->valueString; break; // case 16: in >> inf->bComplexLinkIndex; break; // case 17: in >> inf->accessMode; break; // case 18: in >> inf->strCommAddress; break; // case 19: in >> inf->bShowName; break; // // default: // { // vWarning() << "_INTERFACE Serialized(In) Error"; // } // break; // } //} // 2022-9-27,去掉了index序列化,因为下次反序列化的时候接口可能已经变动了,index需要重新计算,而不是直接读取 // 校验,该参数的最大数量(需要根据参数的数量来调整,用来校验) int nMaxParanum = 18; if (nParanum < 0 || nParanum > nMaxParanum) { return false; } for (int i = 0; i < nParanum; i++) { in >> nPara; switch (nPara) { case 1: in >> inf->strName; break; case 2: in >> inf->strFullName; break; case 3: in >> inf->strNameWithType; break; case 4: in >> inf->Direction; break; case 5: in >> inf->Type; break; case 6: in >> inf->bEnable; break; case 7: in >> inf->strComment; break; case 8: in >> inf->bSerialized; break; case 9: in >> inf->bShow; break; case 10: in >> inf->bDynamic; break; case 11: in >> (int&)gvlMode; break; case 12: in >> inf->bWatch; break; case 13: in >> inf->bDataLink; break; case 14: in >> inf->valueString; break; case 15: in >> inf->bComplexLinkIndex; break; case 16: in >> inf->accessMode; break; case 17: in >> inf->strCommAddress; break; case 18: in >> inf->bShowName; break; default: { vWarning() << "_INTERFACE Serialized(In) Error"; } break; } } // 2022-2-23 增加,只有基础模式的变量才需要读取数值 if (inf->Type == INF_TYPE::INF_TYPE_VALUE) { if (gvlMode == GVL_MODE::GVL_BASIC) { in >> inf->value; } // 如果是其他类型的数值,则需要重置为默认值 else { in >> inf->value.passMode; in >> inf->value.type; inf->value.setValue(QString("")); } } //// 2022-3-31 增加,Goto的输入接口也需要保存值(bool类型) //else if (inf->pParentTool->isGotoTool() // && inf->Direction == INF_DIRECTION::INF_DIR_IN) //{ // in >> inf->value; //} // 如果是dll中的值,则留空 else { in >> inf->value.passMode; in >> inf->value.type; } // 如果属性设置了不参与序列化,即把该变量的值设置为空, if (!inf->bSerialized) { inf->value.setValue(QString("")); } return true; } //======================================================== // // Port // //======================================================== /// /// 写入Port信息 /// /// /// bool Document::savePortBindInfo(QDataStream& out, const TOOL* pTool) { const _INTERFACE* pPortInf = pTool->Interfaces[0]; // 是否已经处于绑定状态 if (pPortInf->isBinded()) { out << 1; DOC_BIND_TOOL_INFO info; info.strSrcPou = pTool->strPouName; info.strSourceTool = pTool->strInstanceName; info.strDestPou = pPortInf->bindedParent()->strPouName; info.strDestInf = pPortInf->pBindInterface->strFullName; info.infType = pPortInf->bindedParent()->Type; out << info; } // 否则,写入空 else { out << 0; } return true; } /// /// 读取Port信息 /// /// /// bool Document::loadPortBindInfo(QDataStream& in, const QString& strLoadName, bool bLoadName) { int nBindCount = 0; in >> nBindCount; // 如果没有绑定,则无需读取 if (nBindCount <= 0) { return true; } DOC_BIND_TOOL_INFO info; in >> info; if (! bLoadName) { info.strSrcPou = strLoadName; info.strDestPou = strLoadName; } m_PortBindInfos.push_back(info); return true; } /// /// 还原Port绑定关系 /// /// /// bool Document::restorePortBindInfo() { for (const DOC_BIND_TOOL_INFO& info : m_PortBindInfos) { // 首先找到这个Port POU* pou = g_pPouManager->getPouByName(info.strSrcPou); TOOL* pPort = pou->GetToolByName(info.strSourceTool); // 找到此Port对应的功能块 WindowAppBlockPort* pPortItem = qgraphicsitem_cast(pou->GetToolItem(info.strSourceTool)); // 检查错误 if (pou == nullptr || pPort == nullptr || pPortItem == nullptr) { qDebug() << "[Error] Document::restorePortBindInfo - pou[" << info.strSrcPou << "] or PORT[" << info.strSourceTool << "] or pPortItem is nullptr."; continue; } // 然后找到这个接口(2022-3-2处理,区分Pou和Gvl) _INTERFACE* pInf = nullptr; // 如果绑定的是工具接口 if (info.infType == TOOL_TYPE::TOOL_TYPE_STANDARD) { POU* toolPou = g_pPouManager->getPouByName(info.strDestPou); if (toolPou != nullptr) { pInf = toolPou->GetInterface(info.strDestInf); } } // 如果绑定的是变量 else if(info.infType == TOOL_TYPE::TOOL_TYPE_GLOBAL_VARIABLE || info.infType == TOOL_TYPE::TOOL_TYPE_LOCAL_VARIABLE ) { GVL* gvl = g_pGvlManager->getGvl(info.strDestPou); if (gvl != nullptr) { pInf = gvl->getInterfaceByFullName(info.strDestInf); } } // 不应该执行到这里 else { vWarning() << "[Error] Document::restorePortBindInfo - Unknown infType [" << (short)info.infType << "] if inf[" << info.strDestInf << "]."; // 直接重置本Port接口的绑定状态 pPort->setPortError(); continue; } // 2022-3-23,此处inf为空有可能是因为单独输入输出Pou的时候,关联的接口并没有导入所致,所以直接清空Port的绑定状态 if (pInf == nullptr) { vWarning() << "[Error] Document::restorePortBindInfo - inf [" << info.strDestInf << "] is nullptr."; // 重置本Port接口的绑定状态 pPort->setPortError(); continue; } // 重新执行Port的绑定动作 pPortItem->bindInterface(pInf); } return true; } //======================================================== // // Goto // //======================================================== /// /// 写入Goto的绑定关系 /// /// /// /// bool Document::saveGotoBindInfo(QDataStream& out, const TOOL* pTool) { const _INTERFACE* pPortInf = pTool->Interfaces[GOTO_OUTPUT_INDX]; // 是否已经处于绑定状态 if (pPortInf->isBinded()) { out << 1; DOC_BIND_TOOL_INFO info; info.strSrcPou = pTool->strPouName; info.strSourceTool = pTool->strInstanceName; info.strDestPou = pPortInf->bindedParent()->strPouName; info.strDestInf = pPortInf->pBindInterface->strFullName; info.infType = pPortInf->bindedParent()->Type; out << info; } // 否则,写入空 else { out << 0; } return true; } /// /// 读取Goto的绑定关系 /// TODO:此处同样涉及到Tool改名问题 /// /// /// bool Document::loadGotoBindInfo(QDataStream& in) { int nBindCount = 0; in >> nBindCount; // 如果没有绑定,则无需读取 if (nBindCount <= 0) { return true; } DOC_BIND_TOOL_INFO info; in >> info; m_GotoBindInfos.push_back(info); return true; } /// /// 还原Goto绑定关系 /// /// bool Document::restoreGotoBindInfo() { for (auto info : m_GotoBindInfos) { // 首先找到这个Port POU* pou = g_pPouManager->getPouByName(info.strSrcPou); TOOL* pGoto = pou->GetToolByName(info.strSourceTool); // 找到此Goto对应的功能块 WindowAppBlockGoto* pItem = qgraphicsitem_cast(pou->GetToolItem(info.strSourceTool)); // 检查错误 if (pou == nullptr || pGoto == nullptr || pItem == nullptr) { qDebug() << "[Error] Document::restoreGotoBindInfo - pou[" << info.strSrcPou << "] or GotoTool[" << info.strSourceTool << "] or pItem is nullptr."; continue; } _INTERFACE* pInf = nullptr; POU* toolPou = g_pPouManager->getPouByName(info.strDestPou); if (toolPou != nullptr) { pInf = toolPou->GetInterface(info.strDestInf); } // 检查错误 if (pInf == nullptr) { vWarning() << "[Error] Document::restoreGotoBindInfo - inf [" << info.strDestInf << "] is nullptr."; continue; } // 重新执行Port的绑定动作 pItem->bindOutputInterface(pInf->parent()); // 此处为Goto的输入接口重置bool类型的值为false pGoto->Interfaces[GOTO_INPUT_INDX]->value.setValue(false); } return true; } //======================================================== // // Links // //======================================================== /// /// 保存Link信息 /// /// /// bool Document::saveLinks(QDataStream& out) { // 遍历所有Pou取出其中的Link信息 // 获取全部Pou const QMap& allPous = g_pPouManager->getAllPous(); // 写入总数量 out << allPous.size(); // 遍历所有的Pou信息 QMapIterator it(allPous); while (it.hasNext()) { it.next(); //// 跳过硬件Pou //if (it.key() == GROUP_NAME_HARDWARE) //{ // continue; //} // 保存本Pou中的Link信息 this->saveLink(out, it.value()); } return true; } /// /// 保存单组Pou中的Link信息 /// /// /// /// bool Document::saveLink(QDataStream& out, POU* pou) { // 然后写入本Pou所有的Link信息 const QVector& allLinks = pou->getAllLinks(); // 总数量 int linkCount = allLinks.size(); out << linkCount; if (linkCount <= 0) { return true; } // 如果有Link的话,则写入Pou的名字 out << pou->pouName(); // 然后写入所有Link细节信息 for (const auto& one_link : allLinks) { // 起始接口全名 out << one_link.pSrcItem->m_infInfo->strFullName; // 终点接口全名 out << one_link.pDstItem->m_infInfo->strFullName; // 连接模式 out << one_link.linkMode; // 本Link在Scene中的坐标(用于还原Link线段) out << one_link.pLinkItem->getAllLinkLinePoints(); } return true; } /// /// 读取Link信息 /// /// /// bool Document::loadLinks(QDataStream& in) { // 首先读取Pou总数量 int pouCount = 0; in >> pouCount; for (int i = 0; i < pouCount; i++) { // 读取本Pou的link数量 int linkCount = 0; in >> linkCount; if (linkCount <= 0) { continue; } // 读取并恢复本Pou中的Link this->loadLink(in, linkCount); } return true; } /// /// 读取单组Pou中的Link信息 /// /// /// bool Document::loadLink(QDataStream& in, const int& linkCount) { // 读入Pou名称 QString pouName; in >> pouName; // 获取本Pou POU* pou = g_pPouManager->getPouByName(pouName); // Error if (pou == nullptr) { qDebug() << "[Error] Document::loadLink - can't find pou[" << pouName << "]"; return false; } // 读取并恢复本Pou下的所有Link for (int i = 0; i < linkCount; i++) { DOC_LINK_INFO linkInfo; // 起始接口全名 in >> linkInfo.strSrcInf; // 目的接口全名 in >> linkInfo.strDesInf; // 连接模式 in >> linkInfo.linkMode; // Link线段坐标 in >> linkInfo.lines; // 通过Pou中的Scene重新建立Link pou->parentScene()->addLink( linkInfo.strSrcInf, linkInfo.strDesInf, linkInfo.linkMode, linkInfo.lines ); } return true; } //======================================================== // // Parallel // //======================================================== /// /// 读取并行工具的配置信息 /// /// /// /// bool Document::loadParallelInfos(QDataStream& in, TOOL*& pNewTool) { Q_UNUSED(in); DOC_TOOL_INFO docTool; docTool.strPouName = pNewTool->strPouName; docTool.strToolInstanceName = pNewTool->strInstanceName; m_ParallelInfos.push_back(docTool); return true; } /// /// 还原并行工具相关信息(由于并行母线的长度需要所有的Tool和Link全部恢复之后才可以判定) /// void Document::restoreParallelInfos() { // 遍历所有的并行母线恢复信息 for (auto parainfo: m_ParallelInfos) { // 首先找到这个工具所在Pou POU* pou = g_pPouManager->getPouByName(parainfo.strPouName); // 找到此工具对应的功能块 WindowAppBlockParallel* pItem = qgraphicsitem_cast(pou->GetToolItem(parainfo.strToolInstanceName)); // 刷新并行母线 pItem->updateParallelLine(); } } //======================================================== // // Wait Tool // //======================================================== /// /// 写入WaitTool的配置参数信息 /// /// /// /// bool Document::saveWaitToolSettings(QDataStream& out, POU* pou, TOOL*& pTool) { // WaitTool的设置 - 绑定的ToolEvent信息 // 到TaskManager中查询ToolEvent信息 ToolEvent* event = g_pTaskManager->getEventByTool(pTool); if (event != nullptr) { out << event->groupName(); out << event->name(); } else { out << ""; out << ""; } // 找到此Tool对应的功能块 WindowAppBlockWait* pWaitBlock = qgraphicsitem_cast(pou->GetToolItem(pTool->strInstanceName)); // WaitTool的设置 - 单步执行时是否跳过等待 out << pWaitBlock->m_bSkipWait; // WaitTool的设置 - 等待的超时时间 out << pWaitBlock->m_nTimeout; // WaitTool的设置 - 等待的触发变量值 out << pWaitBlock->m_WaitValue; return true; } /// /// 读取WaitTool的配置参数信息 /// /// /// bool Document::loadWaitToolSettings(QDataStream& in, POU* pou, TOOL*& pNewTool) { QString groupName, name; in >> groupName; in >> name; // 如果确实绑定了Event,则此处重新到TaskManager注册一下 if (!groupName.isEmpty() && !name.isEmpty()) { // 首先检查是否是系统内置事件 VARIABLE* var = g_pTaskManager->getSysEventByName(name); // 如果不是的话,再判断是否是某一个变量 if (var == nullptr) { // 转换成对应的Variable var = g_pGvlManager->getVariableByName(groupName, name); // 校验是否有异常 if (var == nullptr) { vDebug() << "[Error] Load event[" << groupName << "," << name << "] failed."; return false; } // 重新到TaskManager绑定此对应关系 g_pTaskManager->registerToolEvent(var->eventTrigger, pNewTool); } } // 找到此Tool对应的功能块 WindowAppBlockWait* pWaitBlock = qgraphicsitem_cast(pou->GetToolItem(pNewTool->strInstanceName)); // Error if (pWaitBlock == nullptr) { vDebug() << "WaitBlock is nullptr."; return false; } // WaitTool的设置 - 单步执行时是否跳过等待 in >> pWaitBlock->m_bSkipWait; // WaitTool的设置 - 等待的超时时间 in >> pWaitBlock->m_nTimeout; // WaitTool的设置 - 等待的触发变量值 in >> pWaitBlock->m_WaitValue; return true; } //======================================================== // // Tasks // //======================================================== /// /// 保存Task区段 /// /// bool Document::saveTasks(QDataStream& out) { qDebug() << "Save [Tasks]."; // 从公共区域获取所有的Task数据 const QMap& allTasks = g_pTaskManager->getAllTasks(); // 写入Task总数量 out << allTasks.size(); // 遍历所有的Task信息 QMapIterator it(allTasks); while (it.hasNext()) { // 获取Task const TASK* pTask = it.next().value(); // 写入Task基本信息 out << *pTask; // pous size out << pTask->pous.size(); // pous QVectorIterator it(pTask->pous); while (it.hasNext()) { out << it.next()->pouName(); } qDebug() << " Saved " << pTask->strName; } return true; } /// /// 读取Task区段 /// /// bool Document::loadTasks(QDataStream& in) { qDebug() << "Load [Tasks]."; // 创建默认的Task Monitor View g_pResourceManager->addDefaultTaskMonitorNode(GROUP_NAME_TASKMONITOR); // 创建默认的LogView g_pResourceManager->addDefaultLogViewNode(GROUP_NAME_LOGVIEW); // 读入Task数量 int nTask = 0; in >> nTask; // 循环读取所有Task信息 for (int i = 0; i < nTask; i++) { // 读入Task名字 QString strNewTaskName; in >> strNewTaskName; // 添加对应的节点,并且创建对应的Task视图 WindowAppTaskView* pTaskView = qobject_cast(g_pResourceManager->addTaskNode(strNewTaskName, false)); TASK* pNewTask = new TASK(strNewTaskName); // 读入Task基础信息 in >> *pNewTask; // 2022-9-2,如果Task带有事件触发信息,则绑定一下事件 if (pNewTask->modeType == TASK_MODE_TYPE::MODE_VAL && !pNewTask->strModeName.isEmpty()) { pNewTask->bindEventByMode(); } // 将用户输入的Task参数绑定到视图中 pTaskView->addNewTask(pNewTask); // 继续读取Pou信息 int nPouCount = 0; in >> nPouCount; QVector taskPous; for (int j = 0; j < nPouCount; j++) { QString strPouName; in >> strPouName; taskPous.push_back(g_pPouManager->getPouByName(strPouName)); } // 还原Pou信息到Task中 pTaskView->addTaskPous(taskPous); } return true; } //======================================================== // // SinglePou // //======================================================== /// /// 导出整个Pou(pou、tool、link、variables) /// /// /// /// bool Document::saveSinglePou(const QString& strPouFullPath, const QString& strPouName) { // 建立存档文件 QFile fileOut(strPouFullPath); if (!fileOut.open(QFile::WriteOnly | QFile::Truncate)) { Utility::VPCriticalMessageBox(strPouFullPath + " create failed!"); return false; } // 序列化对象 QDataStream out(&fileOut); // 设置版本 out.setVersion(QDataStream::Qt_5_14); // 保存单个Pou相关信息 savePou(strPouName, out); // 保存本组局部变量 saveGVL(strPouName, out); fileOut.close(); Utility::VPInformationMessageBox("Serialized to " + strPouFullPath + " finished."); return true; } /// /// 导入单个Pou(pou、tool、link、variables) /// /// /// bool Document::loadSinglePou(const QString& strPouFullPath) { qDebug() << "Load file : " << strPouFullPath; // 打开存档文件 QFile fileIn(strPouFullPath); if (!fileIn.open(QFile::ReadOnly)) { Utility::VPCriticalMessageBox(strPouFullPath + " open failed!"); return false; } // 序列化对象 QDataStream in(&fileIn); // 设置版本 in.setVersion(QDataStream::Qt_5_14); // 首先加载一下选择文档的Pou名字 QString strLoadName; in >> strLoadName; int nPouSize = g_pPouManager->getAllPous().size(); QString strPouName("Pou_" + QString::number(nPouSize)); DialogNewPou dlgNewPou; dlgNewPou.setDefaultName(strPouName); int res = dlgNewPou.exec(); if (res != QDialog::Accepted) { fileIn.close(); return false; } // 使用新的POU名字 strLoadName = dlgNewPou.m_strPouName + POU_POSTFIX; // 读取Pou相关数据信息(不需要再次读取名字了) bool state = loadPou(in, strLoadName, false); if (!state) { vWarning() << "Load Pou Error" << strLoadName; return false; } // 读取本Pou的局部变量 this->loadGVL(in, strLoadName, false); // 还原Port绑定关系 this->restorePortBindInfo(); // 还原Goto绑定关系 this->restoreGotoBindInfo(); //// 还原Link信息 //this->restoreLinks(); // 还原Parallel工具信息 this->restoreParallelInfos(); Utility::VPInformationMessageBox("Deserialized from " + strPouFullPath + " finished."); fileIn.close(); return true; } /// /// 单个Pou重置(包括页面和数据结构) /// void Document::pouReset(const QString& strPouName) { qDebug() << "[Document] Reset pou - " << strPouName; // 重置Pou信息 g_pPouManager->RemovePou(strPouName); // 重置Variable g_pGvlManager->removeGvl(strPouName); // 清除对应的子页面 VPGlobal::getMdiFrame()->deleteSubView(strPouName); // 清除临时数据结构(link、port info、Goto) // m_Links.clear(); m_PortBindInfos.clear(); m_GotoBindInfos.clear(); m_ParallelInfos.clear(); } //======================================================== // // UI // //======================================================== /// /// 写入UI区段 /// /// /// bool Document::saveUIs(QDataStream& out) { qDebug() << "Save [UIs]."; g_pUiManager->serialized(out); return true; } /// /// 读取UI区段 /// /// /// bool Document::loadUIs(QDataStream& in) { qDebug() << "Load [UIs]."; // g_pUiManager->deserialized(in); // 读入UI的数量 int nUI = 0; in >> nUI; for (int i = 0; i < nUI; i++) { // 读入UI名字 QString strNewUiName; in >> strNewUiName; // 添加对应的节点,并且创建对应的UI视图 WindowAppUiFrame* pUiFrame = qobject_cast(g_pResourceManager->addUiNode(strNewUiName, false, true)); // 进行 WindowAppUiView* 的序列化 in >> pUiFrame->getUiView(); } // 只有ui数量大于 0 的时候,才发布页面 if ( nUI>0 ) { // 通过Manager发布所有UI bool bRet = g_pUiManager->publishAll(); if (bRet) { } } return true; } //======================================================== // // Compress // //======================================================== /// /// 压缩 /// /// bool Document::compress(QString strPath) { QString strTmpPath = strPath; // 生成正式的文件名 strPath = strPath.left(strPath.length() - TMP_POSTFIX.length()); strPath += DOC_POSTFIX; QByteArray bufferDoc; QByteArray bufferHdw; // 打包后的数据缓冲区 QByteArray buffer; // 读取工程文件 { QFile fileTmp(strTmpPath); fileTmp.open(QIODevice::ReadOnly); qint64 docLength = fileTmp.size(); bufferDoc = fileTmp.read(docLength); fileTmp.close(); qDebug() << "Doc 压缩前字节大小:" << bufferDoc.size(); } // 读取硬件组态文件 { QFileInfo fileInfo(DOC_HARDWARE_FULLPATH); if (fileInfo.exists() != true) { saveHdw(DOC_HARDWARE_FULLPATH); } QFile fileTmp(DOC_HARDWARE_FULLPATH); fileTmp.open(QIODevice::ReadOnly); qint64 docLength = fileTmp.size(); bufferHdw = fileTmp.read(docLength); fileTmp.close(); qDebug() << "Hdw 压缩前字节大小:" << bufferDoc.size(); } QString strDocMD5 = QCryptographicHash::hash(bufferDoc, QCryptographicHash::Md5).toHex().toUpper(); QString strHdwMD5 = QCryptographicHash::hash(bufferHdw, QCryptographicHash::Md5).toHex().toUpper(); QBuffer bufferTmp; bufferTmp.open(QIODevice::WriteOnly); QDataStream ar(&bufferTmp); ar.setVersion(QDataStream::Qt_5_14); int paranum = 5;//参数数量 ar << paranum;//先保存参数数量 ar << (int)1 << (int)DOC_MINOR_VERSION; ar << (int)2 << strDocMD5; ar << (int)3 << strHdwMD5; ar << (int)4 << bufferDoc; ar << (int)5 << bufferHdw; // 压缩文件数据(第二个参数为压缩级别,0 - 9) buffer = qCompress(bufferTmp.buffer() , 5); qDebug() << "压缩后字节大小:" << buffer.size(); // 保存文件 QFile fileSave(strPath); fileSave.open(QIODevice::WriteOnly); fileSave.write(buffer); fileSave.close(); // 释放资源 bufferTmp.close(); // 删除临时文件 QFile::remove(strTmpPath); return true; } /// /// 解压缩 /// /// bool Document::uncompress(QString& strPath) { QString saveFileName = strPath; // 生成解压后的临时文件名,按照临时文件名进行读取 strPath = strPath.left(strPath.length() - DOC_POSTFIX.length()); strPath += TMP_POSTFIX; // 读取文件 QFile fileTmp(saveFileName); fileTmp.open(QIODevice::ReadOnly); qint64 docLength = fileTmp.size(); QByteArray buffer = fileTmp.read(docLength); fileTmp.close(); QByteArray bufferDoc; QByteArray bufferHdw; int nVersion= 0; QString strLoadDocMD5; QString strLoadHdwMD5; // 解压缩 buffer = qUncompress(buffer); QDataStream ar(buffer); ar.setVersion(QDataStream::Qt_5_14); int para = 0; int paranum = 0; ar >> paranum;//读取参数数量 if (paranum == 5) { for (int i = 0; i < paranum; i++) { ar >> para; switch (para) { case 1: ar >> nVersion; break; case 2: ar >> strLoadDocMD5; break; case 3: ar >> strLoadHdwMD5; break; case 4: ar >> bufferDoc; break; case 5: ar >> bufferHdw; break; default: { vWarning() << "Serialized(In) Error"; return false; } break; } } QString strDocMD5 = QCryptographicHash::hash(bufferDoc, QCryptographicHash::Md5).toHex().toUpper(); QString strHdwMD5 = QCryptographicHash::hash(bufferHdw, QCryptographicHash::Md5).toHex().toUpper(); if (strLoadDocMD5 != strDocMD5) { return false; } if (strLoadHdwMD5 != strLoadHdwMD5) { return false; } // 将buffer 存起来 m_bufferHdw = bufferHdw; } else { bufferDoc = buffer; } // 写入临时文件 QFile fileSave(strPath); fileSave.open(QIODevice::WriteOnly); fileSave.write(bufferDoc); fileSave.close(); return true; } //======================================================== // // Other // //======================================================== /// /// 2022-3-25 检查接口是否还有效(防止Port端绑定对象无效后,依旧链接的问题) /// /// /// bool Document::checkInfValid(const QString& infGroupName, const QString& infFullName) { POU* pou = g_pPouManager->getPouByName(infGroupName); if (pou == nullptr) { return false; } _INTERFACE* pInf = pInf = pou->GetInterface(infFullName); if (pInf == nullptr) { return false; } // 如果类型无效,则说明是Port接口并且尚未绑定接口 if (pInf->Type == INF_TYPE::INF_TYPE_UNKNOWN) { return false; } //// 如果是Port类型,并且绑定信息无效了,则返回false //if (pInf->parent()->Type == TOOL_TYPE::TOOL_TYPE_PORT_INPUT // || pInf->parent()->Type == TOOL_TYPE::TOOL_TYPE_PORT_OUTPUT // ) //{ // if (pInf->pBindInterface == nullptr) // { // return false; // } //} return true; } /// /// 检查硬件组态与文档是否匹配 /// /// /// /// bool Document::checkHWAndDocConfigID(QString strDocID, QString strHardwareID) { // 只有当前硬件配置的ID 与 文档 ID 相匹配,才继续加载文档 bool bLoadHdw = false; QStringList value = strDocID.split("|"); for (int i=0; i< value.size(); i++) { QString str = value[i]; if (strHardwareID.indexOf(str) == -1) { bLoadHdw = true; continue; } } if (bLoadHdw == true) { if (m_bufferHdw.size() == 0) { return true; } // TODO:需要引导用户从备份的文件中释放硬件组态文件,并加载释放的文件 m_bufferHdw; DOC_HARDWARE_FULLPATH; { // 写入临时文件 QFile fileSave(DOC_HARDWARE_FULLPATH); fileSave.open(QIODevice::WriteOnly); fileSave.write(m_bufferHdw); fileSave.close(); // 重新加载 loadHdw(DOC_HARDWARE_FULLPATH); } } return true; } /// /// 根据硬件组态的工具实例名有工具名字生成一个ID。此ID后面会保存进硬件组态的数据中 /// /// QString Document::getHWAndDocConfigID() { QString strID; POU* pHdPou = g_pPouManager->getPouByName(GROUP_NAME_HARDWARE); if (pHdPou != nullptr) { QMap allTools = pHdPou->GetAllTools(); QMapIterator i(allTools); while (i.hasNext()) { const TOOL* pTool = i.next().value(); strID.append(pTool->strName); strID.append("."); strID.append(pTool->strInstanceName); strID.append("|"); } strID.chop(1); } return strID; }