1212更新

main
DESKTOP-3D7M4SA\Hicent 1 month ago
parent 5a271dd374
commit be4f422da7

@ -1,281 +1,243 @@
/* ==================== Area 样式表 ==================== */
/* 统一项目样式风格 */
QPushButton#okBtn, QPushButton#delAreaBtn {
font-size: 22px;
/* ==================== 按钮样式 ==================== */
QPushButton#okBtn {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 18px;
border: none;
border-radius: 4px;
padding: 6px 12px;
min-height: 28px;
background-color: #DDEEFF;
color: #2277EF;
}
QPushButton#okBtn:hover, QPushButton#delAreaBtn:hover {
font-size: 22px;
font: bold;
QPushButton#delAreaBtn {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 18px;
border: none;
margin-bottom: -2px
border-radius: 4px;
padding: 6px 12px;
min-height: 28px;
background-color: #FFEEEE;
color: #EE1169;
}
QPushButton#wirteDIDOforceBtn{
font-size: 16px;
padding-left: 5px;
padding-right: 5px;
padding-top: 5px;
padding-bottom: 5px;
QPushButton#okBtn:hover {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 18px;
font-weight: bold;
border: none;
background-color: #EEFFFF;
color: #2277EF;
}
border: 1px solid black;
QPushButton#delAreaBtn:hover {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 18px;
font-weight: bold;
border: none;
background-color: #FFDDDD;
color: #EE1169;
}
QPushButton#wirteDIDOforceBtn {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 18px;
padding: 5px;
border: 1px solid #CCCCCC;
border-radius: 5px;
background-color: #FFFFFF;
color: #333333;
}
QPushButton#wirteDIDOforceBtn:hover{
font: bold;
QPushButton#wirteDIDOforceBtn:hover {
font-weight: bold;
background-color: #F5F5F5;
}
QPushButton#profibusForceBtn {
font-size: 22px;
padding-left: 5px;
padding-right: 5px;
padding-top: 6px;
padding-bottom: 5px;
border: 1px solid black;
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 18px;
padding: 5px;
border: 1px solid #CCCCCC;
border-radius: 5px;
/* background-color: #328ffc;*/
background-color: #FFFFFF;
color: #333333;
}
QPushButton#profibusForceBtn:hover{
font: bold;
QPushButton#profibusForceBtn:hover {
font-weight: bold;
background-color: #F5F5F5;
}
QPushButton#deviceAddButton{
color: #328ffc;
font-size: 20px;
QPushButton#deviceAddButton {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
color: #2277EF;
font-size: 14px;
padding-bottom: 5px;
border-top: none;
border-right: none;
border-left: none;
background-color: transparent;
}
}
QPushButton#addareabutton{
color: #328ffc;
font-size: 20px;
QPushButton#addareabutton {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
color: #2277EF;
font-size: 14px;
padding-top: 7px;
background-color: transparent;
border: none;
}
}
QPushButton#initAddDeviceButton, QPushButton#initAreaAddButton{
font-size: 45px;
color: #328ffc;
QPushButton#initAddDeviceButton,
QPushButton#initAreaAddButton {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 24px;
color: #2277EF;
background-color: rgba(255, 255, 255, 0);
border-radius: 5px;
}
QPushButton#initAddDeviceButton:hover, QPushButton#initAreaAddButton:hover, QPushButton#deviceAddButton:hover, QPushButton#addareabutton:hover{
color: #0f8ef0;
font: bold;
QPushButton#initAddDeviceButton:hover,
QPushButton#initAreaAddButton:hover,
QPushButton#deviceAddButton:hover,
QPushButton#addareabutton:hover {
color: #3787F7;
font-weight: bold;
border-radius: 3px;
margin-bottom: -4px
background-color: transparent;
}
QLabel#wirteDIDOareaMessLabel, QLabel#wirteDIDOareaValueLabel{
font-size: 16px;
}
QLabel#wirteDIDOareaValueLabel, QLabel#readDIDOareaValueLabel{
color: #0160aa;
/* ==================== 标签样式 ==================== */
QLabel#wirteDIDOareaMessLabel,
QLabel#wirteDIDOareaValueLabel {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 14px;
}
QLabel#areaMessLabel, QLabel#areaValueLabel{
font-size: 28px;
QLabel#wirteDIDOareaValueLabel,
QLabel#readDIDOareaValueLabel {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
color: #2277EF;
}
QLabel#massesageLabel{
font-size: 23px;
QLabel#areaMessLabel,
QLabel#areaValueLabel {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 18px;
}
QLabel#areaValueLabel{
color: #0160aa;
QLabel#massesageLabel {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 16px;
}
QLabel#readDIDOareaMessLabel, QLabel#readDIDOareaValueLabel{
font-size: 23px;
QLabel#areaValueLabel {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
color: #2277EF;
}
QLabel#dataTypeLabel, QLabel#dataOrderLabel, QLabel#byteLineLabel{
font-size: 24px;
QLabel#readDIDOareaMessLabel,
QLabel#readDIDOareaValueLabel {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 16px;
}
QLabel#pvUpperLimit, QLabel#pvLowerLimit, QLabel#pvUnit{
font-size: 24px;
QLabel#dataTypeLabel,
QLabel#dataOrderLabel,
QLabel#byteLineLabel {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 16px;
}
QLabel#areaValueLabel{
color: #0160aa;
QLabel#pvUpperLimit,
QLabel#pvLowerLimit,
QLabel#pvUnit {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 16px;
}
QLabel#deviceNameLabel, QLabel#pvUpperLimitLabel, QLabel#pvLowerLimitLabel, QLabel#pvUnitLabel{
font:28px;
QLabel#deviceNameLabel,
QLabel#pvUpperLimitLabel,
QLabel#pvLowerLimitLabel,
QLabel#pvUnitLabel {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 16px;
}
QLineEdit#byteLineEdit{
font-size: 20px;
/* ==================== 输入框样式 ==================== */
QLineEdit#byteLineEdit {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 16px;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 2px solid gary;
border-bottom: 2px solid #CCCCCC;
background-color: transparent;
}
QLineEdit#areaLineEdit {
font-size: 28px;
}
QLineEdit#wirteDIDOareaLineEdit {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 16px;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 2px solid gary;
border-bottom: 2px solid #CCCCCC;
background-color: transparent;
}
QLineEdit#areaLineEdit {
font-size: 28px;
QLineEdit#wirteDIDOareaLineEdit {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 14px;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 2px solid gary;
border-bottom: 2px solid #CCCCCC;
background-color: transparent;
}
QLineEdit#deviceName, QLineEdit#pvUpperLimit, QLineEdit#pvLowerLimit, QLineEdit#pvUnit{
font: 28px;
QLineEdit#deviceName,
QLineEdit#pvUpperLimit,
QLineEdit#pvLowerLimit,
QLineEdit#pvUnit {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 16px;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 2px solid gary;
border-bottom: 2px solid #CCCCCC;
background-color: transparent;
}
QComboBox#dataTypeCombox, QComboBox#orderCombox{
font-size: 20px;
/* border: 1px solid black;*/
/* border-radius: 5px;*/
/* ==================== 下拉框样式 ==================== */
QComboBox#dataTypeCombox,
QComboBox#orderCombox {
font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif;
font-size: 14px;
border: none;
background-color: #FFFFFF;
height: 35px;
padding: 5px 30px 5px 5px;
border-radius: 5px;
min-height: 35px;
max-height: 35px;
}
QComboBox#dataTypeCombox::drop-down,
QComboBox#orderCombox::drop-down {
image: url(Static/down.png);
subcontrol-origin: padding;
subcontrol-position: top right;
background-color: #FFFFFF;
width: 20px;
border: none;
border-radius: 5px;
}
QComboBox#dataTypeCombox::item,
QComboBox#orderCombox::item {
padding: 8px 5px;
}

@ -67,7 +67,7 @@ QPushButton#procedureMag,
QPushButton#varMag,
QPushButton#trendMag {
border: none;
font-size: 16px;
font-size: 20px;
text-align: left;
font-family: "PingFangSC-Medium", "Microsoft YaHei", sans-serif;
color: #6B7280;

@ -11,6 +11,7 @@ from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QSize
from model.ProjectModel.DeviceManage import Device, DevicesManange
from UI.ProfibusWidgets.RightAreaWidget import RightAreaWidgets
@ -141,7 +142,7 @@ class AreaWidget(QWidget):
self.state = True
self.devicesManange = self.areaTabWidget.devicesManange
self.rightAreaWidgetState = False
self.initUI()
self.initUI()
def initUI(self):
self.mainLayout = QHBoxLayout()
@ -198,9 +199,11 @@ class AreaWidget(QWidget):
self.okBtnValue = True
self.delAreaBtn = QPushButton('删除')
self.delAreaBtn.setIcon(QIcon('./Static/delete.png'))
# self.delAreaBtn.setIcon(QIcon('./Static/delete.png'))
self.delAreaBtn.setIcon(qtawesome.icon('fa.trash-o', color='#EE1169'))
self.delAreaBtn.setObjectName('delAreaBtn')
self.delAreaBtn.clicked.connect(self.removeAreaTab)
hLayout = QHBoxLayout()
hLayout.addWidget(QSplitter())

@ -74,8 +74,19 @@ class ProfibusWidgets(QWidget):
super().__init__()
InitParameterDB()
self.setObjectName("MainWindow")
self.devicesManange = DevicesManange()
self.protocolManage = Globals.getValue('protocolManage')
self.usingSharedProfibus = False
self.devicesManange = None
if self.protocolManage and self.protocolManage.hasProfibusSupport():
sharedManager = self.protocolManage.getSharedProfibusManager()
if sharedManager:
self.devicesManange = sharedManager
self.usingSharedProfibus = True
if self.devicesManange is None:
print('没有找到共享的Profibus')
self.devicesManange = DevicesManange()
# self.batteryManange = BatteryManange()
self.dpv1Master = DPV1Master('192.168.4.38', 502)
self.blockParameterManageWidget = BlockParameterManageWidget()
self.process = None
@ -228,9 +239,10 @@ class ProfibusWidgets(QWidget):
self.refreshProgressBar()
self.devicesManange.connect()
self._connectProfibusBackend()
self.setWindowFlags(Qt.FramelessWindowHint)
# self.resize(800, 600)
# self.showMaximized()
@ -241,16 +253,20 @@ class ProfibusWidgets(QWidget):
self.startProtocolBtn.setText('停止通讯')
self.startProtocolBtn.setIcon(QIcon('./Static/pause.png'))
self.startProtocolBtn.setIconSize(QSize(22, 22))
self._connectProfibusBackend()
self.protocolTimer.start(500)
else:
self.startProtocolBtn.setText('开始通讯')
self.startProtocolBtn.setIcon(QIcon('./Static/start.png'))
self.protocolTimer.stop()
def readValues(self):
self.devicesManange.readAreas()
if not self._updateProfibusAreasForUI():
return
dockWidgets = self.findChildren(QDockWidget) #找到四个dockWidget窗口
for dockWidget in dockWidgets:
if dockWidget.widget().currentWidget().objectName() == 'initWidget':
# print(dockWidget.widget().currentWidget().objectName())
@ -280,8 +296,24 @@ class ProfibusWidgets(QWidget):
# except Exception as e:
# print(e)
def _connectProfibusBackend(self):
if self.usingSharedProfibus and self.protocolManage:
sharedManager = self.protocolManage.getSharedProfibusManager()
if sharedManager:
self.devicesManange = sharedManager
self.protocolManage.connectProfibus()
else:
self.devicesManange.connect()
def _updateProfibusAreasForUI(self):
if self.usingSharedProfibus and self.protocolManage:
self.protocolManage.updateProfibusAreas(force=True)
return True
self.devicesManange.readAreas()
return True
def refreshProgressBar(self):
# self.temp = temp / 10 if temp > -3276.8 else float('nan') # 假设温度值需要除以10得到实际℃数
# self.current = current # mA
# self.volt = volt # mV

@ -338,7 +338,7 @@ class DevicesManange():
if bytesNums == 0:
continue
intValues = modbusM.readHoldingRegisters(slaveId = 1, startAddress = 0, varNums = intNums, order = 'Profibus')
print(intValues, index)
# print(intValues, index)
if intValues == 'error':
self.connect()
return

@ -3,6 +3,7 @@ from utils.DBModels.ProtocolModel import (
HartVar, TcRtdVar, AnalogVar, HartSimulateVar
)
from model.ProjectModel.GlobalConfigManager import GlobalConfigManager
from model.ProjectModel.DeviceManage import DevicesManange
from protocol.HartRtuSlaveManager import HartRtuSlaveManager
from protocol.TCP.TCPVarManage import *
@ -13,6 +14,10 @@ from protocol.ModBus.ModbusManager import ModbusManager
from utils import Globals
import threading
import time
import json
from typing import Dict, Any, Optional
class ProtocolManage(object):
"""通讯变量查找类,用于根据变量名在数据库模型中检索变量信息"""
@ -37,9 +42,20 @@ class ProtocolManage(object):
self.varInfoCache = {} # 保持驼峰命名
self.historyDBManage = Globals.getValue('historyDBManage')
self.variableValueCache = {} # {varName: value}
self.profibusManager = None
self.profibusVarMap = {}
self.profibusDeviceMeta = {}
self.profibusEnabled = GlobalConfigManager.isModuleEnabled('profibusModule')
self._profibusConnected = False
self._profibusLastUpdate = 0
self.profibusLock = threading.Lock()
if self.profibusEnabled:
# print('yeyeye')
self._initializeProfibusSupport()
# Modbus 管理器
self.modbusManager = ModbusManager()
# self.modbusManager.setVariableCache(self.variableValueCache, None, self.varInfoCache)
# HART模拟RTU从站管理器
@ -85,6 +101,9 @@ class ProtocolManage(object):
}
except Exception as e:
print(f"刷新缓存时出错: {modelClass.__name__}: {e}")
if self.profibusEnabled and self.profibusManager:
self._refreshProfibusVarCache()
def lookupVariable(self, variableName):
"""
@ -229,8 +248,16 @@ class ProtocolManage(object):
trigger = FPGATrigger if trigger else trigger
self.tcpVarManager.writeValue(varType, channel, value, trigger=trigger, model=model, timeoutMS=timeoutMS)
# PROFIBUS变量处理
elif modelType == 'ProfibusVar':
success, normalizedValue = self._writeProfibusVariable(info, value)
if not success:
return False
value = normalizedValue
# HART模拟变量处理
elif modelType == 'HartSimulateVar':
if not self.hartRtuSlaveManager:
print("HART RTU从站管理器未初始化无法处理HartSimulateVar变量")
return False
@ -253,9 +280,17 @@ class ProtocolManage(object):
print(f"HART模拟变量 {variableName} 写入失败")
return False
rpcValue = self._prepareExternalValue(value)
if self.RpcClient:
self.RpcClient.setVarContent(variableName, value, info['min'], info['max'], info['varType'])
self.RpcClient.setVarContent(
variableName,
rpcValue,
info.get('min'),
info.get('max'),
info.get('varType')
)
return True
except Exception as e:
print(f"写入变量值失败: {str(e)}")
return False
@ -265,7 +300,10 @@ class ProtocolManage(object):
while not self.readThreadStop.is_set():
try:
allVarNames = list(self.getAllVariableNames())
if self.profibusEnabled and self.profibusManager:
self._updateProfibusAreas(force=True)
for varName in allVarNames:
if self.readThreadStop.is_set():
break
try:
@ -283,9 +321,360 @@ class ProtocolManage(object):
print(f"后台读取线程异常: {e}")
time.sleep(5) # 异常时等待更长时间
# ==================== PROFIBUS 变量管理 ====================
def _initializeProfibusSupport(self):
try:
self.profibusManager = DevicesManange()
self._loadProfibusDevicesFromDB()
self.profibusManager.recalculateAddress()
except Exception as e:
print(f"初始化PROFIBUS管理器失败: {e}")
self.profibusManager = None
self.profibusEnabled = False
def _loadProfibusDevicesFromDB(self):
self.profibusDeviceMeta = {}
allDevices = DevicesManange.getAllDevice()
if not allDevices or isinstance(allDevices, str):
return
for deviceRow in allDevices:
if not deviceRow:
continue
try:
deviceName = deviceRow[0]
except (IndexError, TypeError):
continue
proType = deviceRow[1] if len(deviceRow) > 1 else None
masterSlaveModel = deviceRow[2] if len(deviceRow) > 2 else None
areaJson = deviceRow[3] if len(deviceRow) > 3 else None
pvUpper = deviceRow[4] if len(deviceRow) > 4 else None
pvLower = deviceRow[5] if len(deviceRow) > 5 else None
pvUnit = deviceRow[6] if len(deviceRow) > 6 else None
self.profibusDeviceMeta[deviceName] = {
'pvUpperLimit': pvUpper,
'pvLowerLimit': pvLower,
'pvUnit': pvUnit
}
try:
self.profibusManager.addDevice(
proType=proType,
masterSlaveModel=masterSlaveModel,
deviceName=deviceName
)
except Exception as e:
print(f"初始化设备 {deviceName} 失败: {e}")
continue
self._loadAreasForDevice(deviceName, areaJson)
def _loadAreasForDevice(self, deviceName, areaJson):
if not areaJson:
return
try:
areas = json.loads(areaJson) if isinstance(areaJson, str) else areaJson
except Exception as e:
print(f"解析设备 {deviceName} 的区域配置失败: {e}")
return
device = self.profibusManager.getDevice(deviceName)
if not device or not areas:
return
for areaConfig in areas:
if not isinstance(areaConfig, dict):
continue
dataType = areaConfig.get('type')
valueName = areaConfig.get('valueName')
if not dataType or not valueName:
continue
order = areaConfig.get('order', 'ABCD')
bytesCount = areaConfig.get('bytes', 0)
try:
bytesCount = int(bytesCount)
except (TypeError, ValueError):
bytesCount = 0
try:
device.addArea(
type=dataType,
nums=1,
bytes=bytesCount,
order=order,
valueName=valueName
)
except Exception as e:
print(f"添加设备 {deviceName} 的区域 {valueName} 失败: {e}")
continue
def _refreshProfibusVarCache(self):
self.profibusVarMap.clear()
if not self.profibusEnabled:
return
with self.profibusLock:
try:
self.profibusManager = DevicesManange()
self._loadProfibusDevicesFromDB()
self.profibusManager.recalculateAddress()
self._profibusConnected = False
self._profibusLastUpdate = 0
except Exception as e:
print(f"刷新PROFIBUS设备失败: {e}")
return
deviceGroups = [
self.profibusManager.dpMasterDevices,
self.profibusManager.dpSlaveDevices,
self.profibusManager.paMasterDevices,
self.profibusManager.paSlaveDevices
]
snapshot = []
for devicesDict in deviceGroups:
for deviceName, device in devicesDict.items():
deviceMeta = self.profibusDeviceMeta.get(deviceName, {})
for areaIndex, area in enumerate(device.areas):
valueName = getattr(area, 'valueName', None)
if not valueName:
continue
areaInfo = {
'deviceName': deviceName,
'areaIndex': areaIndex,
'areaType': area.type,
'bytes': area.bytes,
'order': area.order,
'valueName': valueName,
'proType': device.type,
'masterSlaveModel': device.masterOrSlave,
'min': deviceMeta.get('pvLowerLimit'),
'max': deviceMeta.get('pvUpperLimit'),
'unit': deviceMeta.get('pvUnit'),
'varType': area.type
}
snapshot.append((valueName, areaInfo))
for valueName, areaInfo in snapshot:
self.profibusVarMap[valueName] = areaInfo
self.varInfoCache[valueName] = {
'modelType': 'ProfibusVar',
'variableData': areaInfo
}
def _ensureProfibusConnected(self):
if not self.profibusManager:
return False
if self._profibusConnected:
return True
try:
self.profibusManager.connect()
self._profibusConnected = True
except Exception as e:
print(f"连接PROFIBUS失败: {e}")
self._profibusConnected = False
return False
return True
def _updateProfibusAreas(self, force=False):
if not self.profibusManager:
return
now = time.time()
if not force and (now - self._profibusLastUpdate) < 0.5:
return
if not self._ensureProfibusConnected():
return
with self.profibusLock:
try:
self.profibusManager.readAreas()
self._profibusLastUpdate = now
except Exception as e:
print(f"读取PROFIBUS区域失败: {e}")
def _writeProfibusVariable(self, info, rawValue):
if not self.profibusManager:
return False, None
if not self._ensureProfibusConnected():
return False, None
device = self.profibusManager.getDevice(info['deviceName'])
if not device:
return False, None
areaIndex = info['areaIndex']
if areaIndex >= len(device.areas):
return False, None
area = device.areas[areaIndex]
if area.type in ['AI', 'AO']:
analogValue, qualityList = self._formatAnalogValue(rawValue)
if analogValue is None:
return False, None
valuesToWrite = [analogValue]
normalizedValue = analogValue
else:
valuesToWrite = self._formatDiscreteValues(area, rawValue)
if valuesToWrite is None:
return False, None
qualityList = None
normalizedValue = valuesToWrite[:]
with self.profibusLock:
try:
if qualityList is not None:
self.profibusManager.writeAreas(
deviceName=info['deviceName'],
areaIndex=areaIndex,
values=valuesToWrite,
qualityValueList=qualityList
)
else:
self.profibusManager.writeAreas(
deviceName=info['deviceName'],
areaIndex=areaIndex,
values=valuesToWrite
)
if area.type in ['AI', 'AO']:
area.currentValue = [normalizedValue]
if qualityList:
area.qualityValueList = qualityList
else:
area.currentValue = normalizedValue[:]
except Exception as e:
print(f"写入PROFIBUS变量 {info['valueName']} 失败: {e}")
return False, None
with self.cacheLock:
self.variableValueCache[info['valueName']] = normalizedValue
return True, normalizedValue
def _formatAnalogValue(self, rawValue):
qualityValue = None
targetValue = rawValue
if isinstance(rawValue, dict):
targetValue = rawValue.get('value')
qualityValue = rawValue.get('quality') or rawValue.get('qualityValue')
try:
analogValue = float(targetValue)
except (TypeError, ValueError):
return None, None
qualityList = None
if qualityValue is not None:
normalizedQuality = self._normalizeQualityValue(qualityValue)
if normalizedQuality is None:
return None, None
qualityList = [normalizedQuality]
return analogValue, qualityList
def _normalizeQualityValue(self, qualityValue):
try:
if isinstance(qualityValue, str):
qualityValue = qualityValue.strip()
if qualityValue.lower().startswith('0x'):
qualityInt = int(qualityValue, 16)
else:
qualityInt = int(qualityValue)
else:
qualityInt = int(qualityValue)
if qualityInt < 0 or qualityInt > 255:
return None
return f"0x{qualityInt:02X}"
except (TypeError, ValueError):
return None
def _formatDiscreteValues(self, area, rawValue):
bitLength = (area.bytes or 0) * 8
if bitLength <= 0:
bitLength = 16
if isinstance(getattr(area, 'currentValue', None), list) and area.currentValue:
baseValues = list(area.currentValue)
elif isinstance(getattr(area, 'forceValue', None), list) and area.forceValue:
baseValues = list(area.forceValue)
else:
baseValues = [0] * bitLength
if len(baseValues) < bitLength:
baseValues += [0] * (bitLength - len(baseValues))
else:
baseValues = baseValues[:bitLength]
if isinstance(rawValue, dict) and 'index' in rawValue:
try:
index = int(rawValue['index'])
except (TypeError, ValueError):
return None
if index < 0 or index >= bitLength:
return None
baseValues[index] = 1 if rawValue.get('value') in [1, True, '1', 'on', 'ON'] else 0
return baseValues
if isinstance(rawValue, (list, tuple)):
normalized = [1 if item in [1, True, '1', 'on', 'ON'] else 0 for item in rawValue]
if len(normalized) < bitLength:
normalized += [0] * (bitLength - len(normalized))
return normalized[:bitLength]
baseValues[0] = 1 if rawValue in [1, True, '1', 'on', 'ON'] else 0
return baseValues
def _readProfibusVariable(self, info):
if not self.profibusManager:
return None
self._updateProfibusAreas()
device = self.profibusManager.getDevice(info['deviceName'])
if not device:
return None
areaIndex = info['areaIndex']
if areaIndex >= len(device.areas):
return None
area = device.areas[areaIndex]
if area.type in ['AI', 'AO']:
if isinstance(area.currentValue, list) and area.currentValue:
try:
return float(area.currentValue[0])
except (TypeError, ValueError):
return area.currentValue[0]
return None
if isinstance(area.currentValue, list) and area.currentValue:
bitLength = (area.bytes or 0) * 8
if bitLength <= 0:
bitLength = len(area.currentValue)
return area.currentValue[:bitLength]
return None
# ==================== 对外共享的 PROFIBUS 接口 ====================
def hasProfibusSupport(self) -> bool:
return self.profibusEnabled and self.profibusManager is not None
def getSharedProfibusManager(self):
return self.profibusManager
def connectProfibus(self) -> bool:
if not self.hasProfibusSupport():
return False
return self._ensureProfibusConnected()
def refreshProfibusVariables(self):
if self.profibusEnabled:
self._refreshProfibusVarCache()
def updateProfibusAreas(self, force: bool = False) -> bool:
if not self.hasProfibusSupport():
return False
self._updateProfibusAreas(force=force)
return True
def listProfibusVariables(self) -> Dict[str, Dict[str, Any]]:
return {name: dict(info) for name, info in self.profibusVarMap.items()}
def getProfibusVariableInfo(self, variableName: str) -> Optional[Dict[str, Any]]:
return self.profibusVarMap.get(variableName)
def readProfibusValue(self, variableName: str, useCache: bool = True):
if not self.hasProfibusSupport():
return None
if useCache:
with self.cacheLock:
if variableName in self.variableValueCache:
return self.variableValueCache[variableName]
value = self.readVariableValue(variableName)
with self.cacheLock:
self.variableValueCache[variableName] = value
return value
def writeProfibusValue(self, variableName: str, value) -> bool:
if not self.hasProfibusSupport():
return False
return bool(self.writeVariableValue(variableName, value))
def getAllVariableNames(self):
return list(self.varInfoCache.keys())
def _readVariableValueOriginal(self, variableName):
varInfo = self.lookupVariable(variableName)
value = None
@ -359,7 +748,10 @@ class ProtocolManage(object):
value = self.tcpVarManager.readValue(varType, channel, model=model)
if varType in ['AI', 'AO']:
value = self.getRealAI(value, info['max'], info['min'])
elif modelType == 'ProfibusVar':
value = self._readProfibusVariable(info)
elif modelType == 'HartSimulateVar':
if not self.hartRtuSlaveManager:
return None
@ -377,18 +769,36 @@ class ProtocolManage(object):
# 默认读取主变量
value = self.hartRtuSlaveManager.readVariable('primaryVariable')
if value is not None and value != 'error':
externalValue = self._prepareExternalValue(value)
if self.RpcClient:
self.RpcClient.setVarContent(variableName, value, info['min'], info['max'], info['varType'])
self.historyDBManage.writeVarValue(variableName, value)
self.RpcClient.setVarContent(
variableName,
externalValue,
info.get('min'),
info.get('max'),
info.get('varType')
)
if self.historyDBManage:
self.historyDBManage.writeVarValue(variableName, externalValue)
return value
else:
return None
except Exception as e:
print(f"读取变量值失败: {str(e)}")
return None
def _prepareExternalValue(self, value):
if isinstance(value, (list, dict)):
try:
return json.dumps(value, ensure_ascii=False)
except Exception:
return str(value)
return value
def readVariableValue(self, variableName):
with self.cacheLock:
if variableName in self.variableValueCache:
return self.variableValueCache[variableName]
return self._readVariableValueOriginal(variableName)

Loading…
Cancel
Save