#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;
}