You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

449 lines
14 KiB
Python

10 months ago
import os
import shutil
import subprocess
import time
from PyQt5.QtCore import QTimer
import sys
import zipfile
sys.path.append('../../')
import json
from model.ClientModel.Client import Client
from model.HistoryDBModel.HistoryDBManage import HistoryDBManage
from model.ProjectModel.VarManage import TcRtdManage, AnalogManage, HartVarManage, \
HartSimulateVarManage
from model.ProjectModel.GlobalConfigManager import GlobalConfigManager
10 months ago
from model.UserModel.UserManage import UserManage
from utils import Globals
from utils.DBModels.ClientModels import Project, ClientDB
from utils.DBModels.ProjectBaseModel import *
10 months ago
from utils.DBModels.ProtocolModel import ModbusTcpMasterVar, ModbusRtuMasterVar, ModbusRtuSlaveVar,\
ModbusTcpSlaveVar, TCPSetting, RTUSetting, InfluxMem, HartVar, TcRtdVar, AnalogVar, HartSimulateVar
10 months ago
from utils.DBModels.UserModels import User
9 months ago
from utils.DBModels.DeviceModels import DeviceDB
10 months ago
7 months ago
from protocol.ProtocolManage import ProtocolManage
10 months ago
class ProjectManage(object):
"""工程管理类"""
def __init__(self):
super(ProjectManage, self).__init__()
@classmethod
def createProject(self, name, des):
# 创建工程
name = str(name)
des = str(des)
Client.initDB()
if Project.getByName(name):
return '已有同名工程'
else:
os.mkdir(os.path.join('project', name))
os.mkdir(os.path.join('project', name, 'db'))
os.mkdir(os.path.join('project', name, 'config'))
os.mkdir(os.path.join('project', name, 'log'))
# with open(os.path.join('project', name, 'config', 'config.json'), 'w') as f:
# json.dump({'description': des}, f)
10 months ago
project = Project()
project.createProject(name, des)
dbPath = os.path.join('project', name, 'db', 'project.db')
projectDB = SqliteDatabase(dbPath)
project_proxy.initialize(projectDB)
projectDB.connect()
9 months ago
projectDB.create_tables([ModbusTcpMasterVar,
ModbusRtuMasterVar,
ModbusRtuSlaveVar,
ModbusTcpSlaveVar,
User,
TCPSetting,
RTUSetting,
HartVar,
TcRtdVar,
HartSimulateVar,
AnalogVar,
InfluxMem,
DeviceDB],safe=True)
10 months ago
10 months ago
manageList = [HartVarManage(), AnalogManage(), TcRtdManage(), HartSimulateVarManage()]
for l in manageList:
l.initVar()
10 months ago
UserManage.initUser()
modbusMasterTcpSetting = TCPSetting()
modbusMasterTcpSetting.initSetting('master')
modbusSlaveTcpSetting = TCPSetting()
modbusSlaveTcpSetting.initSetting('slave')
modbusMasterRtuSetting = RTUSetting()
modbusMasterRtuSetting.initSetting('master')
modbusMasterRtuSetting = RTUSetting()
modbusMasterRtuSetting.initSetting('slave')
projectDB.close()
@classmethod
def deleteProject(self, name):
# 删除工程
name = str(name)
# print(name)
# print(Globals.getValue('currentPro'))
Client.initDB()
if not Project.getByName(name):
print('不存在的工程', Project.getByName(name))
return
try:
if name == Globals.getValue('currentPro'):
self.closePopen()
Globals.getValue('protocolManage').shutdown()
10 months ago
Globals.getValue('currentProDB').close()
Globals.setValue('currentPro', None)
10 months ago
QTimer.singleShot(1000, lambda:shutil.rmtree(os.path.join('project', name)))
except OSError as e:
print(e)
Project.deleteProject(name = name)
6 months ago
histroyTableName = 'p' + name
HistoryDBManage.deleteTable(table=histroyTableName)
10 months ago
@classmethod
def switchProject(self, name):
# 切换工程
currentDB = Globals.getValue('currentProDB')
7 months ago
currentProtocolManage = Globals.getValue('protocolManage')
currentHistoryDB = Globals.getValue('historyDBManage')
10 months ago
if currentDB:
currentDB.close()
if currentProtocolManage:
7 months ago
currentProtocolManage.shutdown()
if currentHistoryDB:
currentHistoryDB.close()
7 months ago
10 months ago
Client.initDB()
dbPath = os.path.join('project', name, 'db', 'project.db')
if ClientDB.getByName():
ClientDB.update(value = name).where(ClientDB.currentProject == '0').execute()
else:
clientDB = ClientDB()
clientDB.createMes(name)
projectDB = SqliteDatabase(dbPath)
project_proxy.initialize(projectDB)
projectDB.connect()
self.closePopen()
Globals.setValue('currentPro', name)
Globals.setValue('currentProDB', projectDB)
Globals.setValue('currentProType', 1)#切换工程
# 加载全局配置
globalConfig = GlobalConfigManager.loadGlobalConfig()
Globals.setValue('currentProjectConfig', globalConfig)
6 months ago
histroyTableName = 'p' + name
historyDBManage = HistoryDBManage(table=histroyTableName)
Globals.setValue('historyDBManage', historyDBManage)
7 months ago
# protocolManage.writeVariableValue('无源4-20mA输出通道1', 150)
# protocolManage.writeVariableValue('有源24V数字输出通道12', 1)
# protocolManage.writeVariableValue('热偶输出2', 40)
# protocolManage.writeVariableValue('热阻输出3', 50)
# print(protocolManage.readVariableValue('有源/无源4-20mA输入通道2'))
# print(protocolManage.readVariableValue('无源24V数字输入通道2'))
# print(a)
6 months ago
# print(Globals.getValue('historyDBManage'))
protocolManage = ProtocolManage()
7 months ago
Globals.setValue('protocolManage', protocolManage)
6 months ago
Globals.getValue('HistoryWidget').exchangeProject()
Globals.getValue('MainWindow').procedureManagerWidget.initDB()
Globals.getValue('MainWindow').procedureManagerWidget.initUI()
# 根据全局配置动态更新变量管理界面
1 month ago
if not currentDB:
self.updateDynamicVarWidgets(globalConfig)
# 通知控制系统工程已切换
try:
from control.ControlManager import getControlManager
controlManager = getControlManager()
# 确保控制系统已初始化
if not controlManager.isInitialized:
controlManager.initialize()
controlManager.switchProject()
# 延迟自动启动规则,确保协议管理器完全初始化
controlManager.delayedAutoStart(3000) # 3秒延迟
except Exception as e:
print(f"通知控制系统工程切换失败: {e}")
7 months ago
10 months ago
# if Globals.getValue('FFThread').isRunning():
# Globals.getValue('FFThread').reStart()
# return
# initDB
@classmethod
def closePopen(self):
if Globals.getValue('currentProType') == '5':
RTDTCThread = Globals.getValue('RTDTCThread')
RTDTCThread.pause()
RTDTCThread.RTDTCCom.quit()
elif Globals.getValue('currentProType') == '6':
AnalogThread = Globals.getValue('AnalogThread')
AnalogThread.pause()
AnalogThread.AnalogCom.close()
elif Globals.getValue('currentProType') == '7':
Globals.getValue('FFThread').pause()
elif Globals.getValue('currentProType') == '8':
FFSimulateThread = Globals.getValue('FFSimulateThread')
FFSimulateThread.pause()
FFSimulateThread.FFSimulate.stop()
elif Globals.getValue('currentProType') == '9':
HartSimulateThread = Globals.getValue('HartSimulateThread')
HartSimulateThread.pause()
HartSimulateThread.HartSimulate.stop()
popen = Globals.getValue('popen')
popenBeat = Globals.getValue('beatPopen')
if popen:
popen.kill()
Globals.setValue('popen', None)
if popenBeat:
popenBeat.kill()
Globals.setValue('beatPopen', None)
@classmethod
def startProtocol(self):
# 0 : MODBUSTCP 主站模式
# 1 : MODBUSTCP 从站模式
# 2 : MODBUSRTU 主站模式
# 3 : MODBUSRTU 从站模式
# 5 : RTD/TC
proType = Globals.getValue('currentProType')
if proType == '0':
popen = subprocess.Popen("celery -A MBTCPMTask worker -l error -P eventlet --purge", stdout=subprocess.PIPE, cwd = 'protocol/Celery/MBTCPMaster')
QTimer.singleShot(5000, lambda:self.startBeat(proType))
elif proType == '1':
popen = subprocess.Popen("celery -A MBTCPSTask worker -l error -P solo -c 2 --purge", stdout=subprocess.PIPE, cwd = 'protocol/Celery/MBTCPSlave')
QTimer.singleShot(5000, lambda:self.startBeat(proType))
elif proType == '2':
popen = subprocess.Popen("celery -A MBRTUMTask worker -l error -P solo -c 2 --purge", stdout=subprocess.PIPE, cwd = 'protocol/Celery/MBRTUMaster')
QTimer.singleShot(5000, lambda:self.startBeat(proType))
elif proType == '3':
popen = subprocess.Popen("celery -A MBRTUSTask worker -l error -P solo -c 2 --purge", stdout=subprocess.PIPE, cwd = 'protocol/Celery/MBRTUSlave')
QTimer.singleShot(5000, lambda:self.startBeat(proType))
elif proType == '4':
popen = subprocess.Popen("celery -A HARTTask worker -l error -P solo -c 2 --purge", stdout=subprocess.PIPE, cwd = 'protocol/Celery/HARTCelery')
QTimer.singleShot(5000, lambda:self.startBeat(proType))
# popenBeat = subprocess.Popen('celery -A HARTTask beat', stdout=subprocess.PIPE, cwd = 'protocol/Celery/HARTCelery')
elif proType == '5':
RTDTCThread = Globals.getValue('RTDTCThread')
RTDTCThread.RTDTCCom.start()
if RTDTCThread.isRunning():
RTDTCThread.reStart()
else:
RTDTCThread.start()
return
elif proType in ['6']:
AnalogThread = Globals.getValue('AnalogThread')
AnalogThread.AnalogCom.connect()
AnalogThread.start()
return
elif proType in ['8']:
FFSimulateThread = Globals.getValue('FFSimulateThread')
FFSimulateThread.FFSimulate.start()
FFSimulateThread.start()
return
elif proType in ['9']:
HartSimulateThread = Globals.getValue('HartSimulateThread')
HartSimulateThread.HartSimulate.start()
HartSimulateThread.start()
return
else:
return
Globals.setValue('popen', popen)
# popen.kill()
@classmethod
def startBeat(self, proType):
if proType == '0':
popenBeat = subprocess.Popen('celery -A MBTCPMTask beat', stdout=subprocess.PIPE, cwd = 'protocol/Celery/MBTCPMaster')
elif proType == '1':
popenBeat = subprocess.Popen('celery -A MBTCPSTask beat', stdout=subprocess.PIPE, cwd = 'protocol/Celery/MBTCPSlave')
elif proType == '2':
popenBeat = subprocess.Popen('celery -A MBRTUMTask beat', stdout=subprocess.PIPE, cwd = 'protocol/Celery/MBRTUMaster')
elif proType == '3':
popenBeat = subprocess.Popen('celery -A MBRTUSTask beat', stdout=subprocess.PIPE, cwd = 'protocol/Celery/MBRTUSlave')
elif proType == '4':
popenBeat = subprocess.Popen('celery -A HARTTask beat', stdout=subprocess.PIPE, cwd = 'protocol/Celery/HARTCelery')
Globals.setValue('beatPopen', popenBeat)
@classmethod
def editProject(self, name, Nname, des):
# 修改工程信息
name = str(name)
Nname = str(Nname)
des = str(des)
Client.initDB()
if name == Nname:
if name == Globals.getValue('currentPro'):
self.editCurProject(name, Nname, des)
return
Project.update(projectName = Nname, description = des).where(Project.projectName == name).execute()
QTimer.singleShot(1000, lambda:os.rename(os.path.join('project', name), os.path.join('project', Nname)))
elif Project.getByName(Nname):
return '已有同名工程'
elif not Project.getByName(name):
print('不存在的工程')
return
else:
if name == Globals.getValue('currentPro'):
self.editCurProject(name, Nname, des)
return
Project.update(projectName = Nname, description = des).where(Project.projectName == name).execute()
QTimer.singleShot(1000, lambda:os.rename(os.path.join('project', name), os.path.join('project', Nname)))
@classmethod
def editCurProject(self, name, Nname, des):
self.closePopen()
Globals.getValue('currentProDB').close()
Project.update(projectName = Nname, description = des).where(Project.projectName == name).execute()
QTimer.singleShot(1000, lambda:self.reNameCurProDir(name, Nname))
Globals.getValue('MainWindow').varWidget.initIcon()
Globals.setValue('currentPro', Nname)
@classmethod
def reNameCurProDir(self, name, Nname):
os.rename(os.path.join('project', name), os.path.join('project', Nname))
self.switchProject(Nname)
@classmethod
def updateDynamicVarWidgets(self, globalConfig):
"""根据全局配置动态更新变量管理界面"""
try:
mainWindow = Globals.getValue('MainWindows')
if not mainWindow:
return
# 获取变量管理标签页
varManageTabWidget = mainWindow.varManageTabWidget
if not hasattr(varManageTabWidget, 'updateTabs'):
return
# 更新标签页
varManageTabWidget.updateTabs(globalConfig)
except Exception as e:
print(f"更新动态变量管理界面失败: {e}")
10 months ago
@classmethod
def getAllProject(self):
# 查询所有工程
projects = Project.get_all()
if projects is 'error':
return
l = []
for x in projects:
l.append([x.id, x.projectName, x.description, x.createTime])
return l
@classmethod
def getByName(self, name):
# 查询指定工程信息
project = Project.getByName(name)
if project:
return [project.id, project.projectName, project.description, project.createTime]
else:
return False
@classmethod
def exportProject(self, zipName, projects):
"""
将多个文件夹打包成一个zip文件
:param projects: 要打包的工程列表
:param zipName: 结果zip文件的名称
"""
folders = [os.path.join('project', project) for project in projects]
with zipfile.ZipFile(zipName, 'w', zipfile.ZIP_DEFLATED) as zipf:
for folder in folders:
for root, dirs, files in os.walk(folder):
for file in files:
# 获取文件路径
filePath = os.path.join(root, file)
# 获取文件相对于根目录的相对路径
relPath = os.path.join(*filePath.split(os.sep)[-3:])
# 使用修改后的路径将文件写入归档文件
zipf.write(filePath, arcname=relPath)
if not dirs and not files:
# 获取文件夹路径
folderPath = os.path.join(root)
# 获取文件夹相对于根目录的相对路径
relPath = os.path.join(*folderPath.split(os.sep)[-2:])
# 使用修改后的路径将文件夹写入归档文件
zipInfo = zipfile.ZipInfo(relPath + '/')
zipf.writestr(zipInfo, '')
# To parse a zip file and get the list of folders inside it, use the following code:
@classmethod
def importProject(self, zipName):
l = self.getAllProject()
allName = [x[1] for x in l]
with zipfile.ZipFile(zipName, 'r') as zipf:
zipProjects = set()
repeatProject = set()
[zipProjects.add(x.split('/')[0]) for x in zipf.namelist()]
for projectName in allName:
if projectName in zipProjects:
repeatProject.add(projectName)
if repeatProject:
return repeatProject
zipf.extractall('project')
projectList = []
for n in zipProjects:
jsonPath = os.path.join('project', n, 'config', 'config.json')
with open(jsonPath, 'r') as f:
data = json.load(f)
# proType = data['ProjectType']
des = data['description']
project = Project()
project.createProject(n, des)
projectList.append(n)
return projectList
# ProjectManage.importProject('1.project')