0717更新 修复趋势

main
zcwBit 5 months ago
parent a5ec3feadd
commit 02f42ab766

@ -207,7 +207,7 @@ class MainWindow(QMainWindow):
def reFreshTrendWidget(self):
if Globals.getValue('currentPro') == -1:
return
self.trendWidget.getMems()
# self.trendWidget.getMems()
self.exButtonClicked(2)
def initProcedureDB(self):

@ -9,88 +9,77 @@ from matplotlib.figure import Figure
from protocol.Celery.MBTCPMaster import app as MBTCPMApp
from celery.result import AsyncResult
from utils.Queue import Queue
from model.ProjectModel.VarManage import ModbusVarManage
from utils import Globals
class ActualTrend(QtWidgets.QMainWindow):
def __init__(self, parent=None, varName = None):
super(ActualTrend, self).__init__(parent)
self.varName = varName
self.protocolManage = Globals.getValue('protocolManage')
_main = QtWidgets.QWidget()
self.setCentralWidget(_main)
layout = QtWidgets.QVBoxLayout(_main)
dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(dynamic_canvas)
self.addToolBar(QtCore.Qt.BottomToolBarArea,
NavigationToolbar(dynamic_canvas, self))
self.x = [] #建立空的x轴数组和y轴数组
self.x = [] # 建立空的x轴数组和y轴数组
self.y = []
self.xQueue = Queue(length = 20)
self.yQueue = Queue(length = 20)
# self.n = 0
self.maxPoints = 200
self._dynamic_ax = dynamic_canvas.figure.subplots()
self._timer = dynamic_canvas.new_timer(
1000, [(self._update_canvas, (), {})])
500, [(self._update_canvas, (), {})])
self._timer.start()
def _update_canvas(self):
# self.n += 1
# 防止canvas已被销毁时报错
if not hasattr(self, '_dynamic_ax') or not hasattr(self._dynamic_ax, 'figure') or not hasattr(self._dynamic_ax.figure, 'canvas'):
return
varName = self.varName
varMM = ModbusVarManage.getByName(varName)
self._dynamic_ax.clear()
if not varName or not self.protocolManage:
return
# 获取实时值
yValue = self.protocolManage.readVariableValue(varName)
acttime = time.strftime('%H:%M:%S', time.localtime(time.time()))
# print(acttime)
if varName != '':
if MBTCPMApp.backend.get('ModBus') is not None:
uid = MBTCPMApp.backend.get('ModBus').decode('utf-8')
else:
return
# print(uid)
res = AsyncResult(uid) # 参数为task id
if res.result:
# print(res.result, res.date_done)
try:
yValue = (res.result[varName])
self.yQueue.enqueue(yValue)
self.xQueue.enqueue(acttime)
except Exception as e:
print(e)
if not varMM[-2] and not varMM[-1]:
minY = min(self.yQueue.list) - 5
maxY = max(self.yQueue.list) + 5
else:
minY = float(varMM[-2]) - float(5)
maxY = float(varMM[-1]) + float(5)
name_list = self.xQueue.list
value_list = np.array(self.yQueue.list)
pos_list = np.arange(len(name_list))
if yValue is not None:
self.x.append(acttime)
self.y.append(yValue)
# 只保留最新maxPoints个点
if len(self.x) > self.maxPoints:
self.x = self.x[-self.maxPoints:]
self.y = self.y[-self.maxPoints:]
self._dynamic_ax.clear()
pos_list = np.arange(len(self.x))
self._dynamic_ax.set_xticks(pos_list)
self._dynamic_ax.set_xticklabels(name_list)
self._dynamic_ax.plot(pos_list, value_list, 'bo-', linewidth=2)
# self._dynamic_ax.set_xlim(0,10)
self._dynamic_ax.set_ylim(minY, maxY)
# print(id(self._dynamic_ax))
# 限制最多显示16个主刻度自动旋转字体更大更明显
import matplotlib.ticker as ticker
max_xticks = 12
if len(self.x) > max_xticks:
step = max(1, len(self.x) // max_xticks)
display_labels = [label if i % step == 0 else '' for i, label in enumerate(self.x)]
else:
display_labels = self.x
self._dynamic_ax.set_xticklabels(display_labels, rotation=30, fontsize=13, fontweight='bold')
self._dynamic_ax.plot(pos_list, self.y, 'bo-', linewidth=2)
if self.y:
minY = min(self.y) - 5
maxY = max(self.y) + 5
self._dynamic_ax.set_ylim(minY, maxY)
from matplotlib.ticker import MaxNLocator
self._dynamic_ax.yaxis.set_major_locator(MaxNLocator(nbins=12, prune=None))
self._dynamic_ax.figure.canvas.draw()
def closeEvent(self, event):
# 判断是否点击了窗口的关闭按钮
if event.type() == event.Close:
# 停止定时器防止回调访问已销毁的canvas
if hasattr(self, '_timer'):
self._timer.stop()
self.hide()
super().closeEvent(event)
self.hide()
def showEvent(self, event):
self._timer.start()

@ -1,161 +1,701 @@
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QSize, Qt
from PyQt5.Qt import *
from PyQt5.QtCore import QSize, Qt, QTimer, QEvent
from PyQt5.QtGui import QPixmap, QIcon
from PyQt5.QtWidgets import QHBoxLayout, QAbstractItemView, QTableView, QVBoxLayout, QSplitter, \
QApplication, QGroupBox, QLabel, QGridLayout, QLineEdit, QComboBox, QTextEdit, QCheckBox,QVBoxLayout,QListView, QMainWindow
from PyQt5.QtWidgets import QListWidget, QStackedWidget, QFrame
from PyQt5.QtWidgets import QListWidgetItem, QSizePolicy
from PyQt5.QtWidgets import QWidget, QSpacerItem, QHeaderView
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWebEngineWidgets import QWebEnginePage
from utils.DBModels.ProtocolModel import InfluxMem
from PyQt5.QtWidgets import (QApplication, QGridLayout, QListWidget, QListWidgetItem,
QToolBar, QAction, QLabel, QWidget, QVBoxLayout, QHBoxLayout,
QSplitter, QGroupBox, QLineEdit, QComboBox, QTextEdit,
QCheckBox, QFrame, QSpacerItem, QSizePolicy, QHeaderView, QToolButton)
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
from utils import Globals
from model.HistoryDBModel.HistoryDBManage import HistoryDBManage
import datetime
import pyecharts.options as opts
from pyecharts.charts import Line
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import numpy as np
from Static import static
class HistoryTrend(object):
def __init__(self, view):
self.line = Line(init_opts= opts.InitOpts(width = str(view.size().width() - 20) + 'px', height = str(view.size().height() - 30) + 'px'))
self.setOpts()
def addXAxis(self, xAxis):
self.line.add_xaxis(xaxis_data=xAxis)
def addYAxis(self, lineName, yAxis):
self.line.add_yaxis(
series_name=lineName,
y_axis=yAxis,
markpoint_opts=opts.MarkPointOpts(
data=[
opts.MarkPointItem(type_="max", name="最大值"),
opts.MarkPointItem(type_="min", name="最小值"),
]
),
markline_opts=opts.MarkLineOpts(
data=[opts.MarkLineItem(type_="average", name="平均值")]
),
)
def setOpts(self):
self.line.set_global_opts(
title_opts=opts.TitleOpts(title="趋势图"),
tooltip_opts=opts.TooltipOpts(trigger="axis"),
toolbox_opts=opts.ToolboxOpts(is_show=True),
xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
datazoom_opts=[
opts.DataZoomOpts(xaxis_index=0, range_start=0, range_end=100),
],
)
@property
def html(self):
return self.line.render_embed()
import datetime
import sys
import pandas as pd
import bisect
from matplotlib.widgets import RectangleSelector
import matplotlib.patches as patches
from matplotlib.widgets import SpanSelector
from matplotlib.gridspec import GridSpec
# 配置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
class TrendWidgets(QWidget):
def __init__(self, parent=None):
super(TrendWidgets, self).__init__(parent)
# self.setAttribute(Qt.WA_StyledBackground, True) # 移除无效属性
self.gridLayout = QtWidgets.QGridLayout(self)
self.gridLayout.setObjectName("gridLayout")
def __init__(self):
super().__init__()
self.setWindowTitle("历史趋势浏览器")
self.setMinimumSize(120, 120)
# 设置窗口标志,禁用窗口拖拽
self.setWindowFlags(QtCore.Qt.WindowType.Window)
# 初始化数据
self.historyDB = None
self.currentVarName = None
self.xData = []
self.yData = []
# 多变量支持
self.multiVarData = {} # 存储多个变量的数据 {varName: {'x': [], 'y': []}}
self.selectedVars = [] # 当前选中的变量列表
self.varColors = {} # 存储变量颜色 {varName: color}
# 初始化历史缩放记录
self.history = []
self.historyIndex = 0
# 主布局
mainLayout = QHBoxLayout(self)
mainLayout.setSpacing(10)
mainLayout.setContentsMargins(10, 10, 10, 10)
# 左侧变量列表区域
self.createVariableListPanel()
mainLayout.addWidget(self.variableListGroup, 1) # 1份宽度
# 右侧趋势图区域
self.trendViewerGroup = QWidget()
self.createTrendViewerPanel()
mainLayout.addWidget(self.trendViewerGroup, 6) # 6份宽度
# 连接信号
self.connectSignals()
Globals.setValue('HistoryWidget', self)
def createVariableListPanel(self):
# 创建变量列表面板
self.variableListGroup = QGroupBox("变量列表")
layout = QVBoxLayout(self.variableListGroup)
# 搜索框单独一行
searchInputLayout = QHBoxLayout()
self.searchInput = QLineEdit()
self.searchInput.setPlaceholderText("搜索变量...")
self.searchInput.textChanged.connect(self.filterVarList)
self.searchInput.setMinimumWidth(220)
searchInputLayout.addWidget(self.searchInput)
layout.addLayout(searchInputLayout)
# 变量列表
self.varListWidget = QListWidget()
self.varListWidget.setObjectName("varListWidget")
self.varListWidget.itemDoubleClicked.connect(self.showVarTrend)
self.gridLayout.addWidget(self.varListWidget, 0, 0, 15, 4)
# 趋势图WebView
self.trendWebView = QWebEngineView()
self.trendWebView.setObjectName("trendWebView")
self.gridLayout.addWidget(self.trendWebView, 0, 4, 15, 22)
self.gridLayout.setSpacing(10)
self.gridLayout.setContentsMargins(20, 20, 20, 20)
Globals.setValue('HistoryWidget', self)
self.varListWidget.itemSelectionChanged.connect(self.onVarSelected)
layout.addWidget(self.varListWidget)
# 时间选择+便捷查询区域
searchLayout = QHBoxLayout()
from PyQt5.QtCore import QDateTime
self.startTimeEdit = QtWidgets.QDateTimeEdit()
self.startTimeEdit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
self.startTimeEdit.setCalendarPopup(True)
self.endTimeEdit = QtWidgets.QDateTimeEdit()
self.endTimeEdit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
self.endTimeEdit.setCalendarPopup(True)
now = QDateTime.currentDateTime()
self.startTimeEdit.setDateTime(now.addDays(-1))
self.endTimeEdit.setDateTime(now)
searchLayout.addWidget(self.startTimeEdit)
searchLayout.addWidget(self.endTimeEdit)
self.quickRangeCombo = QtWidgets.QComboBox()
self.quickRangeCombo.addItems(["最近一天", "最近6小时", "最近1小时", "最近30分钟", "自定义"])
self.quickRangeCombo.currentIndexChanged.connect(self.onQuickRangeChanged)
searchLayout.addWidget(self.quickRangeCombo)
self.queryBtn = QToolButton()
self.queryBtn.setText("查询")
self.queryBtn.setToolTip("按时间范围查询变量数据")
self.queryBtn.clicked.connect(self.onTimeRangeQuery)
searchLayout.addWidget(self.queryBtn)
layout.addLayout(searchLayout)
# 按钮区域
buttonLayout = QHBoxLayout()
# 刷新按钮
refreshBtn = QToolButton()
refreshBtn.setText("刷新列表")
refreshBtn.clicked.connect(self.refreshVarList)
buttonLayout.addWidget(refreshBtn)
# 添加到趋势图按钮
addToTrendBtn = QToolButton()
addToTrendBtn.setText("添加到趋势图")
addToTrendBtn.setToolTip("将选中的变量添加到趋势图")
addToTrendBtn.clicked.connect(self.addSelectedVarsToTrend)
buttonLayout.addWidget(addToTrendBtn)
# 清除所有变量按钮
clearAllBtn = QToolButton()
clearAllBtn.setText("清除所有")
clearAllBtn.setToolTip("清除趋势图中的所有变量")
clearAllBtn.clicked.connect(self.clearAllVars)
buttonLayout.addWidget(clearAllBtn)
layout.addLayout(buttonLayout)
self.variableListGroup.setLayout(layout)
# 记录当前时间范围
self._query_time_range = (self.startTimeEdit.dateTime().toPyDateTime(), self.endTimeEdit.dateTime().toPyDateTime())
def createTrendViewerPanel(self):
# 创建趋势图查看器面板
layout = QVBoxLayout(self.trendViewerGroup)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(5)
# 创建包含图表的组框
chartGroup = QGroupBox()
chartLayout = QVBoxLayout(chartGroup)
chartLayout.setContentsMargins(5, 5, 5, 5)
# 创建matplotlib图表设置主图和缩略图比例为6:1
self.figure = Figure(figsize=(12, 8), dpi=100)
gs = GridSpec(7, 1, figure=self.figure)
self.ax_main = self.figure.add_subplot(gs[:6, 0]) # 主图占6份
self.ax_overview = self.figure.add_subplot(gs[6, 0]) # 缩略图占1份
self.canvas = FigureCanvas(self.figure)
chartLayout.addWidget(self.canvas, 20)
# 底部信息栏(含状态显示)
infoLayout = QHBoxLayout()
infoLayout.setContentsMargins(5, 5, 5, 5)
self.varNameLabel = QLabel("当前变量: 无")
self.dataCountLabel = QLabel("数据点: 0")
self.timeRangeLabel = QLabel("时间范围: 无数据")
self.statusLabel = QLabel("就绪")
self.statusLabel.setFont(QtGui.QFont("Arial", 9))
self.statusLabel.setStyleSheet("color: #555; padding: 2px;")
infoLayout.addWidget(self.varNameLabel)
infoLayout.addStretch()
infoLayout.addWidget(self.dataCountLabel)
infoLayout.addStretch()
infoLayout.addWidget(self.timeRangeLabel)
infoLayout.addStretch()
infoLayout.addWidget(self.statusLabel)
chartLayout.addLayout(infoLayout, 1)
layout.addWidget(chartGroup)
# 悬浮信息气泡label
self.infoBubble = QLabel(self.trendViewerGroup)
self.infoBubble.setStyleSheet("background:rgba(255,255,220,0.95); border:1px solid #aaa; border-radius:4px; padding:4px; color:#222;")
self.infoBubble.setFont(QtGui.QFont("Consolas", 10))
self.infoBubble.setVisible(False)
# 连接鼠标事件,支持拖拽平移
self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
self.canvas.mpl_connect('axes_leave_event', self.on_mouse_leave)
self.canvas.mpl_connect('scroll_event', self.on_mouse_wheel)
self.canvas.mpl_connect('button_press_event', self.on_mouse_press)
self.canvas.mpl_connect('button_release_event', self.on_mouse_release)
# 拖拽状态
self._is_panning = False
self._pan_start_x = None
self._pan_start_xlim = None
# 初始化选择器
self.selector = None
self.is_selecting = False
self.zoom_start = None
# 添加SpanSelector用于区间选择
self.span = SpanSelector(
self.ax_overview, self.on_select, 'horizontal',
useblit=True, interactive=True, props=dict(alpha=0.3, facecolor='orange')
)
# 绑定双击事件恢复全局视图
self.canvas.mpl_connect('button_press_event', self.on_overview_double_click)
self._selected_range = None # 记录选中区间
# 设置下方缩略图x轴显示更多时间内容
import matplotlib.dates as mdates
from matplotlib.ticker import MaxNLocator
self.ax_overview.xaxis.set_major_locator(mdates.AutoDateLocator())
self.ax_overview.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
def connectSignals(self):
# 连接所有信号
self.ax_overview.callbacks.connect('xlim_changed', self.on_overview_xlim_changed)
def exchangeProject(self):
# 项目切换时调用
self.historyDB = Globals.getValue('historyDBManage')
self.refreshVarList()
def deleteMem(self):
pass # 历史mem相关功能已废弃
def itemCheckstate(self):
pass # 历史mem相关功能已废弃
@QtCore.pyqtSlot(str)
def on_lineEdit_textChanged(self, text):
pass # 历史mem相关功能已废弃
def listContext(self, position):
pass # 历史mem相关功能已废弃
def delVarRecard(self):
pass # 历史mem相关功能已废弃
def getMems(self):
pass # 历史mem相关功能已废弃
def refreshList(self, index):
pass # 历史mem相关功能已废弃
def createHtml(self, varNames):
pass # 历史mem相关功能已废弃
def refreshVarList(self):
# 显示进度条
# 刷新变量列表
self.varListWidget.clear()
varNames = []
try:
sql = f'SELECT DISTINCT("varName") FROM "{self.historyDB.table}"'
df = self.historyDB.client.query(sql, mode="pandas")
import pandas as pd
if isinstance(df, pd.DataFrame) and 'varName' in df.columns:
varNames = df['varName'].tolist()
except Exception as e:
print(f"获取变量名失败: {e}")
varNames = []
self.statusLabel.setText("正在加载变量列表...")
if not self.historyDB or not hasattr(self.historyDB, 'getAllVarNames'):
self.statusLabel.setText("未连接历史数据库,无法获取变量列表")
return
varNames = self.historyDB.getAllVarNames()
# print(varNames)
# 对变量名称进行排序
varNames.sort(key=lambda x: x.lower())
# 添加排序后的变量到列表
for name in varNames:
item = QListWidgetItem(str(name))
item = QListWidgetItem(name)
self.varListWidget.addItem(item)
def showVarTrend(self, item):
varName = item.text()
# 查询该变量历史数据
data, timeList = self.historyDB.queryVarHistory(varName)
if not data or not timeList:
self.trendWebView.setHtml('<h3>无历史数据</h3>')
self.statusLabel.setText(f"已加载 {len(varNames)} 个变量(已排序)")
def sortVarList(self):
# 手动排序变量列表
try:
varNames = []
for i in range(self.varListWidget.count()):
item = self.varListWidget.item(i)
if item and not item.isHidden():
varNames.append(item.text())
varNames.sort(key=lambda x: x.lower())
self.varListWidget.clear()
for name in varNames:
item = QListWidgetItem(name)
self.varListWidget.addItem(item)
self.statusLabel.setText(f"已排序 {len(varNames)} 个变量")
except Exception as e:
self.statusLabel.setText(f"排序失败: {str(e)}")
def addSelectedVarsToTrend(self):
# 将选中的变量添加到趋势图
selectedItems = self.varListWidget.selectedItems()
if not selectedItems:
self.statusLabel.setText("请先选择要添加的变量")
return
# 构建趋势图
line = Line(init_opts=opts.InitOpts(width='900px', height='500px'))
line.add_xaxis(timeList)
line.add_yaxis(varName, data)
line.set_global_opts(
title_opts=opts.TitleOpts(title=f"{varName} 历史趋势"),
tooltip_opts=opts.TooltipOpts(trigger="axis"),
xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
datazoom_opts=[opts.DataZoomOpts(xaxis_index=0, range_start=0, range_end=100)],
addedCount = 0
for item in selectedItems:
varName = item.text()
if varName not in self.selectedVars:
self.selectedVars.append(varName)
self.loadVarData(varName)
addedCount += 1
if addedCount > 0:
self.updateMultiVarChart()
self.statusLabel.setText(f"已添加 {addedCount} 个变量到趋势图")
else:
self.statusLabel.setText("选中的变量已在趋势图中")
def onTimeRangeQuery(self):
# 查询按钮点击,记录时间范围
self._query_time_range = (
self.startTimeEdit.dateTime().toPyDateTime(),
self.endTimeEdit.dateTime().toPyDateTime()
)
html = line.render_embed()
page = QWebEnginePage(self.trendWebView)
page.setHtml('<meta charset="utf-8">\n<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">\n' + html)
self.trendWebView.setPage(page)
self.trendWebView.reload()
import datetime
# 重新加载所有已选变量的数据
for varName in self.selectedVars:
start_time, end_time = self._query_time_range
if isinstance(start_time, datetime.datetime):
start_time = start_time.isoformat()
if isinstance(end_time, datetime.datetime):
end_time = end_time.isoformat()
if self.historyDB and hasattr(self.historyDB, 'queryVarHistory'):
data, timeList = self.historyDB.queryVarHistory(varName, start_time, end_time)
xData = []
for t in timeList:
dt = None
if isinstance(t, datetime.datetime):
dt = t
elif hasattr(t, 'to_pydatetime'):
dt = t.to_pydatetime()
elif isinstance(t, str):
try:
dt = datetime.datetime.fromisoformat(t)
except:
continue
if dt:
xData.append(dt)
self.multiVarData[varName] = {'x': xData, 'y': data}
self.updateMultiVarChart()
self.statusLabel.setText(f"已设置时间范围: {self._query_time_range[0].strftime('%Y-%m-%d %H:%M:%S')} ~ {self._query_time_range[1].strftime('%Y-%m-%d %H:%M:%S')}")
def loadVarData(self, varName):
# 加载单个变量的数据,按选定时间范围
try:
self.statusLabel.setText(f"加载 {varName} 数据...")
import datetime
if hasattr(self, '_query_time_range') and self._query_time_range:
start_time, end_time = self._query_time_range
else:
start_time = None
end_time = None
# 从数据库获取数据
if self.historyDB and hasattr(self.historyDB, 'queryVarHistory'):
print(start_time, end_time)
data, timeList = self.historyDB.queryVarHistory(varName, start_time, end_time)
# 直接使用数据库返回的时间,不做任何时区转换
xData = []
for t in timeList:
if isinstance(t, datetime.datetime):
xData.append(t)
elif isinstance(t, pd.Timestamp):
xData.append(t.to_pydatetime())
elif isinstance(t, str):
try:
# 直接按本地时间字符串解析
xData.append(datetime.datetime.fromisoformat(t))
except:
xData.append(datetime.datetime.now())
else:
xData.append(datetime.datetime.now())
# 存储数据
self.multiVarData[varName] = {'x': xData, 'y': data}
# 分配颜色
if varName not in self.varColors:
colors = ['blue', 'red', 'green', 'cyan', 'magenta', 'yellow', 'black']
colorIndex = len(self.varColors) % len(colors)
self.varColors[varName] = colors[colorIndex]
except Exception as e:
self.statusLabel.setText(f"加载 {varName} 数据失败: {str(e)}")
def updateMultiVarChart(self):
import matplotlib.dates as mdates
# 更新多变量图表
self.ax_main.clear()
self.ax_overview.clear()
# 设置网格
self.ax_main.grid(True, alpha=0.3)
self.ax_overview.grid(True, alpha=0.3)
# 复用数据处理逻辑,提升性能
for varName in self.selectedVars:
if varName in self.multiVarData:
data = self.multiVarData[varName]
if data['x'] and data['y']:
# 处理断线数据(复用逻辑)
x_processed, y_processed = self._process_break_line_data(data['x'], data['y'])
# 缩略图:显示全量数据
self.ax_overview.plot(x_processed, y_processed, color=self.varColors[varName], linewidth=1, alpha=0.7)
# 主图:根据选中区间显示数据
if self._selected_range is not None:
xlim = self._selected_range
# 筛选区间数据
x_num = [mdates.date2num(xx) for xx in x_processed if xx is not None]
x_filtered, y_filtered = [], []
for xx, yy in zip(x_processed, y_processed):
if xx is not None:
xn = mdates.date2num(xx)
if xlim[0] <= xn <= xlim[1]:
x_filtered.append(xx)
y_filtered.append(yy)
x_main, y_main = x_filtered, y_filtered
else:
# 没有选择范围时,主图显示全部数据
x_main, y_main = x_processed, y_processed
self.ax_main.plot(x_main, y_main, color=self.varColors[varName], linewidth=2, marker='o', markersize=4, label=varName)
# 设置时间格式
self.ax_main.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
# 增加主图和缩略图的坐标轴刻度数量,并显示完整日期时间
from matplotlib.ticker import MaxNLocator
self.ax_main.xaxis.set_major_locator(mdates.AutoDateLocator())
self.ax_main.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M:%S'))
self.ax_main.yaxis.set_major_locator(MaxNLocator(nbins=16, prune=None))
self.ax_overview.xaxis.set_major_locator(mdates.AutoDateLocator())
self.ax_overview.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M:%S'))
self.ax_overview.yaxis.set_major_locator(MaxNLocator(nbins=16, prune=None))
# 刻度数量更多
self.ax_main.xaxis.set_major_locator(MaxNLocator(nbins=16, prune=None))
self.ax_overview.xaxis.set_major_locator(MaxNLocator(nbins=16, prune=None))
# 设置标签
self.ax_main.set_title('历史趋势图', fontsize=12, fontweight='bold')
self.ax_main.set_ylabel('数值')
self.ax_overview.set_xlabel('时间')
self.ax_overview.set_ylabel('数值')
# 添加图例
if self.selectedVars:
self.ax_main.legend(loc='upper right')
# 自动调整布局
self.figure.tight_layout()
# 刷新画布
self.canvas.draw()
def _process_break_line_data(self, x_raw, y_raw):
"""处理断线数据间隔大于2分钟的点不连线"""
x, y = [], []
prev_time = None
for xi, yi in zip(x_raw, y_raw):
if prev_time is not None and (xi - prev_time).total_seconds() > 60:
# 断线时只在y中插入Nonex继续插入当前时间戳
x.append(xi)
y.append(None)
x.append(xi)
y.append(yi)
prev_time = xi
return x, y
def filterVarList(self, text):
# 本地过滤变量列表
for i in range(self.varListWidget.count()):
item = self.varListWidget.item(i)
if item is not None:
item.setHidden(text.lower() not in item.text().lower())
def onVarSelected(self):
# 当选中一个变量时(非双击)
selectedItems = self.varListWidget.selectedItems()
if selectedItems:
self.statusLabel.setText(f"已选择: {selectedItems[0].text()}")
def showVarTrend(self, item):
# 显示选中变量的趋势图(与多变量流程一致)
varName = item.text()
self.currentVarName = varName
self.statusLabel.setText(f"加载 {varName} 历史趋势...")
# 清空多变量数据,只保留当前变量
self.selectedVars = [varName]
self.multiVarData = {}
self.varColors = {}
self.loadVarData(varName)
# 更新UI信息
if varName in self.multiVarData:
data = self.multiVarData[varName]['y']
xdata = self.multiVarData[varName]['x']
else:
data = []
xdata = []
self.varNameLabel.setText(f"当前变量: {varName}")
self.dataCountLabel.setText(f"数据点: {len(data)}")
if xdata:
import datetime
import pandas as pd
def to_dt(t):
if isinstance(t, datetime.datetime):
return t
elif isinstance(t, pd.Timestamp):
return t.to_pydatetime()
elif isinstance(t, (int, float)):
return datetime.datetime.fromtimestamp(t)
else:
return datetime.datetime.now()
start_time = to_dt(xdata[0]).strftime('%Y-%m-%d %H:%M')
end_time = to_dt(xdata[-1]).strftime('%Y-%m-%d %H:%M')
self.timeRangeLabel.setText(f"时间范围: {start_time} - {end_time}")
else:
self.timeRangeLabel.setText("时间范围: 无数据")
# 刷新多变量趋势图
self.updateMultiVarChart()
self.resetZoom()
self.statusLabel.setText(f"{varName} 趋势已显示")
def updateChart(self):
# 用当前数据更新图表
self.ax_main.clear()
self.ax_overview.clear()
# 设置网格
self.ax_main.grid(True, alpha=0.3)
self.ax_overview.grid(True, alpha=0.3)
# 绘制数据
if self.xData and self.yData:
self.ax_main.plot(self.xData, self.yData,
color='blue',
linewidth=2,
marker='o',
markersize=4,
label=self.currentVarName)
# 概览图(降采样)
step = max(1, len(self.xData) // 10)
x_overview = self.xData[::step]
y_overview = self.yData[::step]
self.ax_overview.plot(x_overview, y_overview,
color='blue',
linewidth=1,
alpha=0.7)
# 设置时间格式
self.ax_main.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
self.ax_overview.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
# 设置标签
self.ax_main.set_title('历史趋势图', fontsize=12, fontweight='bold')
self.ax_main.set_ylabel('数值')
self.ax_overview.set_xlabel('时间')
self.ax_overview.set_ylabel('数值')
# 添加图例
if self.currentVarName:
self.ax_main.legend(loc='upper right')
# 自动调整布局
self.figure.tight_layout()
# 刷新画布
self.canvas.draw()
def on_mouse_press(self, event):
# 鼠标左键按下,准备平移
if event.inaxes == self.ax_main and event.button == 1 and event.xdata is not None:
self._is_panning = True
self._pan_start_x = event.xdata
self._pan_start_xlim = self.ax_main.get_xlim()
def on_mouse_release(self, event):
# 鼠标左键松开,结束平移
if self._is_panning:
self._is_panning = False
self._pan_start_x = None
self._pan_start_xlim = None
# 拖拽结束后再重绘
self.canvas.draw_idle()
def on_mouse_move(self, event):
# 鼠标移动事件,显示悬浮气泡+平移主图
if self._is_panning and event.inaxes == self.ax_main and event.xdata is not None and self._pan_start_x is not None:
dx = event.xdata - self._pan_start_x
xlim0, xlim1 = self._pan_start_xlim
self.ax_main.set_xlim(xlim0 - dx, xlim1 - dx)
# 拖拽时不重绘,提高流畅度
# self.canvas.draw_idle()
# 悬浮气泡逻辑(不变)
if event.inaxes == self.ax_main and event.xdata is not None and event.ydata is not None:
info_text = f"时间: {mdates.num2date(event.xdata).strftime('%Y-%m-%d %H:%M:%S')}<br>"
if self.selectedVars:
for varName in self.selectedVars:
if varName in self.multiVarData:
data = self.multiVarData[varName]
if data['x'] and data['y']:
closest_idx = self.find_closest_point(data['x'], event.xdata)
if closest_idx is not None and closest_idx < len(data['y']):
value = data['y'][closest_idx]
info_text += f"{varName}: {value:.3f}<br>"
self.infoBubble.setText(f"<span style='white-space:pre'>{info_text}</span>")
x = int(event.guiEvent.x())
y = int(event.guiEvent.y())
self.infoBubble.move(x + 16, y + 8)
self.infoBubble.adjustSize()
self.infoBubble.setVisible(True)
else:
self.infoBubble.setVisible(False)
# self.canvas.draw() # 性能优化:去除重绘
def on_mouse_leave(self, event):
# 鼠标离开主图时隐藏气泡
self.infoBubble.setVisible(False)
def find_closest_point(self, x_data, target_x):
# 找到最接近目标X值的数据点索引
if not x_data:
return None
# 将datetime转换为matplotlib日期
if isinstance(x_data[0], datetime.datetime):
x_nums = mdates.date2num(x_data)
else:
x_nums = x_data
# 使用二分查找
left = 0
right = len(x_nums) - 1
while left <= right:
mid = (left + right) // 2
if x_nums[mid] == target_x:
return mid
elif x_nums[mid] < target_x:
left = mid + 1
else:
right = mid - 1
# 找到最接近的点
if left >= len(x_nums):
return len(x_nums) - 1
elif right < 0:
return 0
else:
if abs(x_nums[left] - target_x) < abs(x_nums[right] - target_x):
return left
else:
return right
def on_overview_xlim_changed(self, event_ax):
# 当概览图范围变化时更新主图
if event_ax == self.ax_overview:
x1, x2 = self.ax_overview.get_xlim()
self.ax_main.set_xlim(x1)
self.canvas.draw()
def resetZoom(self):
# 重置到完整视图
if self.xData:
self.ax_main.set_xlim(min(self.xData), max(self.xData))
self.ax_overview.set_xlim(min(self.xData), max(self.xData))
self.canvas.draw()
def zoomBack(self):
# 退到上一个视图
if self.history and self.historyIndex > 0:
self.historyIndex -= 1
self.applyZoom(self.history[self.historyIndex])
self.backBtn.setEnabled(self.historyIndex > 0)
self.forwardBtn.setEnabled(True)
def zoomForward(self):
# 进到下一个视图
if self.history and self.historyIndex < len(self.history) - 1:
self.historyIndex += 1
self.applyZoom(self.history[self.historyIndex])
self.forwardBtn.setEnabled(self.historyIndex < len(self.history) - 1)
self.backBtn.setEnabled(True)
def applyZoom(self, zoom_range):
# 应用指定的缩放范围
x1, x2 = zoom_range
self.ax_main.set_xlim(x1, x2)
self.canvas.draw()
def enableRectZoom(self):
# 启用框选缩放模式
self.statusLabel.setText("模式: 框选缩放 - 在概览图中拖动选择区域")
def enablePanMode(self):
# 启用平移模式
self.statusLabel.setText("模式: 平移视图 - 在主图中拖动移动视图")
def onProjectChanged(self):
# 项目更改时处理
self.exchangeProject()
def clearAllVars(self):
# 清除趋势图中的所有变量
self.selectedVars = []
self.multiVarData = {}
self.varColors = {}
self.ax_main.clear()
self.ax_overview.clear()
self.ax_main.grid(True, alpha=0.3)
self.ax_overview.grid(True, alpha=0.3)
self.canvas.draw()
self.statusLabel.setText("趋势图已清除")
self.varNameLabel.setText("当前变量: 无")
self.dataCountLabel.setText("数据点: 0")
self.timeRangeLabel.setText("时间范围: 无数据")
self.resetZoom()
def on_select(self, xmin, xmax):
# SpanSelector回调记录选中区间并更新主趋势图
self._selected_range = (xmin, xmax)
self.updateMultiVarChart()
def on_overview_double_click(self, event):
# 双击下方缩略图恢复全局视图
if event.dblclick and event.inaxes == self.ax_overview:
self.ax_main.set_xlim(auto=True)
self.canvas.draw_idle()
def on_mouse_wheel(self, event):
# 鼠标滚轮缩放主趋势图X轴
if event.inaxes == self.ax_main and event.xdata is not None:
xlim = self.ax_main.get_xlim()
x_center = event.xdata
# 缩放因子,滚轮向上放大,向下缩小
scale_factor = 0.8 if event.button == 'up' else 1.25
x_left = x_center - (x_center - xlim[0]) * scale_factor
x_right = x_center + (xlim[1] - x_center) * scale_factor
self.ax_main.set_xlim(x_left, x_right)
self.canvas.draw_idle()
def onQuickRangeChanged(self, idx):
from PyQt5.QtCore import QDateTime
now = QDateTime.currentDateTime()
if idx == 0: # 最近一天
self.startTimeEdit.setDateTime(now.addDays(-1))
self.endTimeEdit.setDateTime(now)
elif idx == 1: # 最近6小时
self.startTimeEdit.setDateTime(now.addSecs(-6*3600))
self.endTimeEdit.setDateTime(now)
elif idx == 2: # 最近1小时
self.startTimeEdit.setDateTime(now.addSecs(-3600))
self.endTimeEdit.setDateTime(now)
elif idx == 3: # 最近30分钟
self.startTimeEdit.setDateTime(now.addSecs(-1800))
self.endTimeEdit.setDateTime(now)
# idx==4为自定义不做处理
# 自动触发查询
if idx != 4:
self.onTimeRangeQuery()
# 使用示例
if __name__ == "__main__":
app = QApplication(sys.argv)
window = TrendWidgets()
window.show()
sys.exit(app.exec_())

@ -107,6 +107,18 @@ class AnalogButtonDelegate(TcRtdButtonDelegate):
def __init__(self, parent=None):
super(AnalogButtonDelegate, self).__init__(parent)
self.trendWindows = {} # 保存所有打开的趋势窗口
def trend_action(self):
sender = self.sender()
model = self.parent().model
varName = model.datas[sender.index[0]][3]
# 以变量名为key避免重复打开
if varName not in self.trendWindows or self.trendWindows[varName] is None:
self.trendWindows[varName] = ActualTrend(varName=varName)
self.trendWindows[varName].show()
self.trendWindows[varName].raise_()
self.trendWindows[varName].activateWindow()
def edit_action(self):
sender = self.sender()

@ -6,6 +6,7 @@ from PyQt5.QtWidgets import QHBoxLayout, QWidget, QMessageBox, QComboBox
from protocol.TCP.TemToMv import temToMv
from model.ProjectModel.VarManage import *
from UI.VarManages.ModbusModel import *
from UI.TrendManage.ActualTrendWidget import ActualTrend
from utils import Globals
import re
@ -109,6 +110,7 @@ class TcRtdButtonDelegate(VarButtonDelegate):
def __init__(self, parent=None):
super(TcRtdButtonDelegate, self).__init__(parent)
self.trendWindows = {} # 保存所有打开的趋势窗口
def paint(self, painter, option, index):
if not self.parent().indexWidget(index):
@ -127,15 +129,24 @@ class TcRtdButtonDelegate(VarButtonDelegate):
clicked=self.edit_action
)
button3 = QPushButton(
qtawesome.icon('fa.line-chart', color='#393c4e'),
"",
self.parent(),
clicked=self.trend_action
)
button1.clicked.connect(self.start_action)
button2.clicked.connect(self.edit_action)
button3.clicked.connect(self.trend_action)
button2.oldName = False
button2.isEdit = True
button1.index = [index.row(), index.column()]
button2.index = [index.row(), index.column()]
button3.index = [index.row(), index.column()]
data = self.parent().model.datas[index.row()]
@ -149,6 +160,7 @@ class TcRtdButtonDelegate(VarButtonDelegate):
h_box_layout = QHBoxLayout()
h_box_layout.addWidget(button1)
h_box_layout.addWidget(button2)
h_box_layout.addWidget(button3)
h_box_layout.setContentsMargins(2, 0, 0, 2)
h_box_layout.setAlignment(Qt.AlignCenter)
widget = QWidget()
@ -262,6 +274,17 @@ class TcRtdButtonDelegate(VarButtonDelegate):
varMes.append('')
model.insert_data(varMes, rowIndex)
model.remove_row(rowIndex + 1)
def trend_action(self):
sender = self.sender()
model = self.parent().model
varName = model.datas[sender.index[0]][3]
# 以变量名为key避免重复打开
if varName not in self.trendWindows or self.trendWindows[varName] is None:
self.trendWindows[varName] = ActualTrend(varName=varName)
self.trendWindows[varName].show()
self.trendWindows[varName].raise_()
self.trendWindows[varName].activateWindow()
class TcRtdTypeDelegate(TcRtdButtonDelegate):
def __init__(self, parent=None):

@ -88,9 +88,8 @@ class HistoryDBManage:
pass
def writeVarValue(self, varName, value):
# 入队时记录当前时间
enqueue_time = datetime.now(timezone(timedelta(hours=8)))
# print(enqueue_time)
# 入队时记录中国时区时间
enqueue_time = datetime.now()
self.writeQueue.put((varName, value, enqueue_time))
def _writeWorker(self):
@ -105,39 +104,62 @@ class HistoryDBManage:
point = Point(self.table).tag("varName", varName).field("value", float(value)).time(enqueue_time)
self.client.write(point)
def queryVarHistory(self, varName, startTime=None, endTime=None, limit=1000):
def queryVarHistory(self, varName, startTime=None, endTime=None):
where = f'"varName" = \'{varName}\''
if startTime:
where += f" AND time >= '{startTime}'"
if endTime:
where += f" AND time <= '{endTime}'"
sql = f'SELECT time, value FROM "{self.table}" WHERE {where} ORDER BY time LIMIT {limit}'
# 关键用tz()函数将time字段转为中国时区
sql = f'SELECT tz(time, \'Asia/Shanghai\') AS time, value FROM "{self.table}" WHERE {where} ORDER BY time ASC LIMIT 100000'
# print(f"执行SQL查询: {sql}")
try:
df = self.client.query(sql, mode="pandas")
import pandas as pd
if isinstance(df, pd.DataFrame):
data = df["value"].tolist() if "value" in df.columns else []
timeList = df["time"].tolist() if "time" in df.columns else []
print(f"查询到 {len(data)} 个数据点")
else:
# print(f"查询结果不是DataFrame: {type(df)}")
data, timeList = [], []
except Exception as e:
print(f"查询结果处理失败: {e}")
data, timeList = [], []
return data, timeList
def getAllVarNames(self):
"""获取所有去重后的变量名列表"""
sql = f'SELECT DISTINCT("varName") FROM "{self.table}"'
try:
df = self.client.query(sql, mode="pandas")
import pandas as pd
if isinstance(df, pd.DataFrame) and 'varName' in df.columns:
return df['varName'].tolist()
else:
return []
except Exception as e:
print(f"获取变量名失败: {e}")
return []
@classmethod
def deleteTable(cls, table):
token = cls.getAPIToken()
host = 'http://localhost:8181'
url = f"{host.rstrip('/')}/api/v3/configure/table?db={'dcs'}&table={table}"
import datetime
# 获取当前UTC时间+1分钟去除微秒Z结尾
now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc, microsecond=0)
hardDeleteAt = (now + datetime.timedelta(minutes=1)).isoformat().replace('+00:00', 'Z')
url = f"{host.rstrip('/')}/api/v3/configure/table?db={'dcs'}&table={table}&hard_delete_at={hardDeleteAt}"
headers = {
'Authorization': f'Bearer {token}'
}
response = requests.delete(url, headers=headers)
print(hardDeleteAt)
if response.status_code != 200:
print(f"删除失败: {response.status_code} {response.text}")
else:
print(f"已删除表 {table} 的所有历史数据。")
print(f"删除表 {table} 的所有历史数据。")
def stopWriteThread(self):
self._stopWriteThread.set()

@ -107,8 +107,9 @@ class ProjectManage(object):
try:
if name == Globals.getValue('currentPro'):
self.closePopen()
Globals.getValue('protocolManage').shutdown()
Globals.getValue('currentProDB').close()
Globals.setValue('currentPro', None)
Globals.setValue('currentPro', None)
QTimer.singleShot(1000, lambda:shutil.rmtree(os.path.join('project', name)))
except OSError as e:
print(e)

@ -17,11 +17,10 @@ from protocol.ProtocolManage import ProtocolManage
# 获取ProtocolManage单例或全局对象假设为全局唯一实际项目可根据实际情况调整
def getProtocolManager():
def refreshCache():
protocolManagerInstance = Globals.getValue('protocolManage')
if protocolManagerInstance is None:
protocolManagerInstance = ProtocolManage()
return protocolManagerInstance
if protocolManagerInstance:
protocolManagerInstance.refreshVarCache()
class ModbusVarManage(object):
@ -55,7 +54,7 @@ class ModbusVarManage(object):
modbusVarType.createVar(varName = varName, varType = varType, des = des, address = address,
slaveID = slaveID, min = min, max = max, order = order, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
# def importModbusVar(self, path):
@ -162,7 +161,7 @@ class ModbusVarManage(object):
modbusVarType.createVar(varName = varName, varType = varType, des = des, address = address,
slaveID = slaveID, min = min, max = max, order = order, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
@ -172,7 +171,7 @@ class ModbusVarManage(object):
# print(name)
self.getVarClass(modbusType).deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
@ -201,7 +200,7 @@ class ModbusVarManage(object):
self.getVarClass(modbusType).update(varName = Nname, description = des, varType = varType, address = address, slaveID = slaveID,
min = min, max = max, order = order, varModel = varModel).where(self.getVarClass(modbusType).varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def editOrder(self, name, order, modbusType):
@ -209,7 +208,7 @@ class ModbusVarManage(object):
order = str(order)
self.getVarClass(modbusType).update(order = order).where(self.getVarClass(modbusType).varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def editVarModel(self, name, varModel, modbusType):
@ -217,7 +216,7 @@ class ModbusVarManage(object):
varModel = str(varModel)
self.getVarClass(modbusType).update(varModel = varModel).where(self.getVarClass(modbusType).varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def getAllVar(self, modbusType):
@ -255,7 +254,7 @@ class HartVarManage(object):
hartVarType = HartVar()
hartVarType.createVar(varName = varName, des = des, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def getAllVar(self):
@ -276,7 +275,7 @@ class HartVarManage(object):
# print(name)
HartVar.deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
@ -298,7 +297,7 @@ class HartVarManage(object):
else:
HartVar.update(varName = Nname, description = des, varModel = varModel).where(HartVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def editVarModel(self, name, varModel):
@ -306,7 +305,7 @@ class HartVarManage(object):
print('修改变量模型',name)
HartVar.update(varModel = str(varModel)).where(HartVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
@ -332,7 +331,7 @@ class HartVarManage(object):
hartVarType = HartVar()
hartVarType.createVar(varName = varName, des = des, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
class TcRtdManage(object):
@ -352,7 +351,7 @@ class TcRtdManage(object):
tcRtdVarType = TcRtdVar()
tcRtdVarType.createVar(varName = varName, channelNumber=channelNumber, des = des, varType = varType, min = min, max = max, compensationVar = compensationVar, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def getAllVar(self):
@ -373,7 +372,7 @@ class TcRtdManage(object):
# print(name)
TcRtdVar.deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
@ -399,7 +398,7 @@ class TcRtdManage(object):
else:
TcRtdVar.update(varName=Nname, channelNumber = channelNumber, description=des, varType=varType, min=min, max=max, compensationVar = compensationVar).where(TcRtdVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def getByName(self, name):
@ -425,14 +424,14 @@ class TcRtdManage(object):
name = str(name)
TcRtdVar.update(varType = str(varType)).where(TcRtdVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def editVarModel(self, name, varModel):
name = str(name)
TcRtdVar.update(varModel = str(varModel)).where(TcRtdVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def importVarForm(self, varName, channelNumber, varType, des, min, max, compensationVar, varModel):
@ -443,7 +442,7 @@ class TcRtdManage(object):
tcRtdVarType = TcRtdVar()
tcRtdVarType.createVar(varName = varName, channelNumber=channelNumber, des = des, varType = varType, min = min, max = max, compensationVar = compensationVar, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
class AnalogManage(object):
def __init__(self):
@ -462,7 +461,7 @@ class AnalogManage(object):
analogVarType = AnalogVar()
analogVarType.createVar(varName = varName, channelNumber = channelNumber, des = des, varType = varType, min = min, max = max, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def getAllVar(self):
@ -483,7 +482,7 @@ class AnalogManage(object):
# print(name)
AnalogVar.deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
@ -507,14 +506,14 @@ class AnalogManage(object):
else:
AnalogVar.update(varName=Nname, channelNumber =channelNumber, description=des, varType=varType, min=min, max=max).where(AnalogVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def editVarModel(self, name, varModel):
name = str(name)
AnalogVar.update(varModel = str(varModel)).where(AnalogVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def getByName(self, name):
# 查询指定变量信息
@ -565,7 +564,7 @@ class AnalogManage(object):
analogVarType = AnalogVar()
analogVarType.createVar(varName = varName, channelNumber = channelNumber, des = des, varType = varType, min = min, max = max, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@ -585,7 +584,7 @@ class HartSimulateVarManage(object):
hartSimulateVarType = HartSimulateVar()
hartSimulateVarType.createVar(varName = varName, des = des,varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def getAllVar(self):
@ -606,7 +605,7 @@ class HartSimulateVarManage(object):
# print(name)
HartSimulateVar.deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
@ -627,14 +626,14 @@ class HartSimulateVarManage(object):
else:
HartSimulateVar.update(varName = Nname, description = des, varModel = varModel).where(HartSimulateVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def editVarModel(self, name, varModel):
name = str(name)
HartSimulateVar.update(varModel = str(varModel)).where(HartSimulateVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
@classmethod
def getByName(self, name):
# 查询指定变量信息
@ -658,7 +657,7 @@ class HartSimulateVarManage(object):
hartSimulateVarType = HartSimulateVar()
hartSimulateVarType.createVar(varName = varName, des = des,varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
refreshCache()
class GlobalVarManager(object):

@ -229,6 +229,7 @@ class ProtocolManage(object):
def _readVariableValueOriginal(self, variableName):
# 完全保留原有读取逻辑
# print(12)
varInfo = self.lookupVariable(variableName)
value = None
if not varInfo:
@ -315,6 +316,10 @@ class ProtocolManage(object):
def shutdown(self):
self.tcpVarManager.shutdown()
# 关闭后台读取线程
if hasattr(self, 'readThreadStop') and hasattr(self, 'readThread'):
self.readThreadStop.set()
self.readThread.join(timeout=1)

Loading…
Cancel
Save