空开有效,需要调灯
parent
8c57551006
commit
acca3cfaf7
|
|
@ -7,6 +7,11 @@ gps:
|
||||||
port: /dev/ttyLP4
|
port: /dev/ttyLP4
|
||||||
baudrate: 9600
|
baudrate: 9600
|
||||||
timeout: 50
|
timeout: 50
|
||||||
|
# indicator(指示灯和蜂鸣器)配置
|
||||||
|
indicator:
|
||||||
|
port: /dev/ttyUSB1
|
||||||
|
baudrate: 9600
|
||||||
|
timeout: 50
|
||||||
# breaker(断路器)配置
|
# breaker(断路器)配置
|
||||||
breaker:
|
breaker:
|
||||||
port: /dev/ttyUSB0
|
port: /dev/ttyUSB0
|
||||||
|
|
|
||||||
|
|
@ -723,6 +723,110 @@ class LSDAQ:
|
||||||
self.close()
|
self.close()
|
||||||
self.logger.info("Modbus Serial TCP Client stopped.")
|
self.logger.info("Modbus Serial TCP Client stopped.")
|
||||||
|
|
||||||
|
class SerialClient:
|
||||||
|
"""串口通信基础类"""
|
||||||
|
def __init__(self, port, baudrate, timeout, logger):
|
||||||
|
self.port = port
|
||||||
|
self.baudrate = baudrate
|
||||||
|
self.timeout = timeout
|
||||||
|
self.logger = logger
|
||||||
|
self.serial = None
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
self.serial = serial.Serial(self.port, self.baudrate, timeout=self.timeout)
|
||||||
|
return self.serial.is_open
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.serial and self.serial.is_open:
|
||||||
|
self.serial.close()
|
||||||
|
|
||||||
|
def exeCmd(self, cmd):
|
||||||
|
try:
|
||||||
|
info = ''
|
||||||
|
data = bytearray().fromhex(cmd[1])
|
||||||
|
if cmd[4] == 1:
|
||||||
|
data += bytearray(checkValue(data).to_bytes(2, 'big'))
|
||||||
|
|
||||||
|
retry = 0
|
||||||
|
RETRYTIMES = int(cmd[6]) if len(cmd) >= 7 else 1
|
||||||
|
|
||||||
|
while retry < RETRYTIMES:
|
||||||
|
info += f"[{nowStr()}] Sent:{wordData2HexStr(data)}\n"
|
||||||
|
self.serial.write(data)
|
||||||
|
time.sleep(int(cmd[3])/1000.0)
|
||||||
|
recvData = self.serial.read(int(cmd[2]))
|
||||||
|
info += f"[{nowStr()}] Echo:{wordData2HexStr(recvData)}\n"
|
||||||
|
|
||||||
|
rspLen = int(cmd[2])
|
||||||
|
if len(recvData) >= rspLen:
|
||||||
|
if recvData[0:2] == bytearray().fromhex(cmd[1][0:4]):
|
||||||
|
recvData = recvData[0:rspLen]
|
||||||
|
if cmd[5] == 1:
|
||||||
|
crc = int.from_bytes(recvData[rspLen-2:rspLen], byteorder='big')
|
||||||
|
calc_value = checkValue(recvData[0:rspLen-2])
|
||||||
|
if crc == calc_value:
|
||||||
|
return [True, recvData, info]
|
||||||
|
else:
|
||||||
|
return [True, recvData, info]
|
||||||
|
retry += 1
|
||||||
|
return [False, None, info]
|
||||||
|
except Exception as e:
|
||||||
|
info += f"[{nowStr()}] Error in exeCmd: {str(e)}\n"
|
||||||
|
return [False, None, info]
|
||||||
|
|
||||||
|
class IndicatorController:
|
||||||
|
"""指示灯和蜂鸣器控制器(独立串口)"""
|
||||||
|
def __init__(self, config, logger):
|
||||||
|
self.logger = logger
|
||||||
|
self.client = SerialClient(
|
||||||
|
config.get('port', '/dev/ttyUSB1'),
|
||||||
|
config.get('baudrate', 9600),
|
||||||
|
config.get('timeout', 50)/1000.0,
|
||||||
|
logger
|
||||||
|
)
|
||||||
|
self.cmdList = {
|
||||||
|
'turnOnGreen': ['', "0105 0002 FF00", 8, 100, 1, 1, 3],
|
||||||
|
'turnOffGreen': ['', "0105 0002 0000", 8, 100, 1, 1, 3],
|
||||||
|
'turnOnRed': ['', "0105 0008 FF00", 8, 100, 1, 1, 3],
|
||||||
|
'turnOffRed': ['', "0105 0000 0000", 8, 100, 1, 1, 3],
|
||||||
|
'turnOnAlarm': ['', "0105 00A1 FF00", 8, 100, 1, 1, 3],
|
||||||
|
'turnOffAlarm': ['', "0105 00A1 0000", 8, 100, 1, 1, 3],
|
||||||
|
}
|
||||||
|
self.alarm = 0
|
||||||
|
self.client.open()
|
||||||
|
|
||||||
|
def exe(self, name):
|
||||||
|
return self.client.exeCmd(self.cmdList[name])
|
||||||
|
|
||||||
|
def alarming(self, closed):
|
||||||
|
"""报警时:红灯亮+蜂鸣器响,绿灯灭"""
|
||||||
|
if not self.alarm and closed == 0xF0:
|
||||||
|
self.exe('turnOffGreen')
|
||||||
|
self.exe('turnOnRed')
|
||||||
|
self.exe('turnOnAlarm')
|
||||||
|
self.alarm = 1
|
||||||
|
|
||||||
|
def unalarming(self, closed):
|
||||||
|
"""解除报警:根据合闸状态控制指示灯"""
|
||||||
|
if self.alarm:
|
||||||
|
self.exe('turnOffRed')
|
||||||
|
self.exe('turnOffAlarm')
|
||||||
|
if closed == 0xF0:
|
||||||
|
self.exe('turnOnGreen')
|
||||||
|
else:
|
||||||
|
self.exe('turnOffGreen')
|
||||||
|
self.alarm = 0
|
||||||
|
|
||||||
|
def turnOffAll(self):
|
||||||
|
"""关闭所有指示灯和蜂鸣器"""
|
||||||
|
self.exe('turnOffGreen')
|
||||||
|
self.exe('turnOffRed')
|
||||||
|
self.exe('turnOffAlarm')
|
||||||
|
self.alarm = 0
|
||||||
|
|
||||||
|
def turnOnGreen(self):
|
||||||
|
self.exe('turnOnGreen')
|
||||||
|
|
||||||
class Breaker:
|
class Breaker:
|
||||||
def __init__(self, config:dict, logger):
|
def __init__(self, config:dict, logger):
|
||||||
# 加载配置参数
|
# 加载配置参数
|
||||||
|
|
@ -744,13 +848,21 @@ class Breaker:
|
||||||
self.task_start_threshold = config.get('task_start_threshold', 2000)
|
self.task_start_threshold = config.get('task_start_threshold', 2000)
|
||||||
self.task_stop_threshold = config.get('task_stop_threshold', 2000)
|
self.task_stop_threshold = config.get('task_stop_threshold', 2000)
|
||||||
self.locked = 0
|
self.locked = 0
|
||||||
self.closed = 0x0F # 0x0F:分闸 0xF0:合闸
|
self.closed = 0x0F
|
||||||
self.reasonForLastOpen = 15 # F:无 1:过流 2:漏电 3:过温 4:过载 5:过压 6:欠压 7:远程 8:模组 9:失压 A:锁扣 B:限电 0: 本地
|
self.reasonForLastOpen = 15
|
||||||
self.active_powers = []
|
self.active_powers = []
|
||||||
self.duration = config.get('duration', 5)
|
self.duration = config.get('duration', 5)
|
||||||
self.alarm = 0
|
self.active_power = 0
|
||||||
|
|
||||||
self.active_power = 0 # 有功功率,单位:W
|
# 创建独立的串口客户端
|
||||||
|
self.client = SerialClient(self.port, self.baudrate, self.timeout, logger)
|
||||||
|
|
||||||
|
# 从配置中创建指示灯控制器(如果配置存在)
|
||||||
|
indicator_config = config.get('indicator', None)
|
||||||
|
if indicator_config:
|
||||||
|
self.indicator = IndicatorController(indicator_config, logger)
|
||||||
|
else:
|
||||||
|
self.indicator = None
|
||||||
|
|
||||||
OVV = config.get('OVV', 275)
|
OVV = config.get('OVV', 275)
|
||||||
UVV = config.get('UVV', 150)
|
UVV = config.get('UVV', 150)
|
||||||
|
|
@ -773,22 +885,15 @@ class Breaker:
|
||||||
'active_power': 0,
|
'active_power': 0,
|
||||||
'load_status': 0
|
'load_status': 0
|
||||||
}
|
}
|
||||||
# 构建指令集
|
# 构建指令集(仅断路器指令)
|
||||||
self.cmdList = {
|
self.cmdList = {
|
||||||
# 指令格式:指令描述,指令字符串,回复长度,超时时间,发送校验标志,接收校验标志,重试次数
|
|
||||||
'readAllDatas': ['', f"0204 0000 0027", 83, 300, 1, 1, 3],
|
'readAllDatas': ['', f"0204 0000 0027", 83, 300, 1, 1, 3],
|
||||||
'readOverLimitValues': ['', f"0203 0002 0006", 17, 200, 1, 1, 3],
|
'readOverLimitValues': ['', f"0203 0002 0006", 17, 200, 1, 1, 3],
|
||||||
'readOverLimitActionTime': ['', f"0203 0010 0006", 17, 200, 1, 1, 3],
|
'readOverLimitActionTime': ['', f"0203 0010 0006", 17, 200, 1, 1, 3],
|
||||||
'setOverLimitValues': ['', f"0210 0002 0006 0C {OVV:04X} {UVV:04X} {OCV:04X} {LCV:04X} {OTV:04X} {OPV:04X}", 8, 100, 1, 1, 3],
|
'setOverLimitValues': ['', f"0210 0002 0006 0C {OVV:04X} {UVV:04X} {OCV:04X} {LCV:04X} {OTV:04X} {OPV:04X}", 8, 100, 1, 1, 3],
|
||||||
'setOverLimitActionTime': ['', f"0210 0010 0006 0C {OVT:04X} {UVT:04X} {OCT:04X} {LCT:04X} {OTT:04X} {OPT:04X}", 8, 100, 1, 1, 3],
|
'setOverLimitActionTime': ['', f"0210 0010 0006 0C {OVT:04X} {UVT:04X} {OCT:04X} {LCT:04X} {OTT:04X} {OPT:04X}", 8, 100, 1, 1, 3],
|
||||||
'closeBreaker': ['', f"0205 0001 ff00", 8, 100, 1, 1, 3],
|
'closeBreaker': ['', f"0205 0001 ff00", 8, 100, 1, 1, 3],
|
||||||
'openBreaker': ['', f"0205 0001 0000", 8, 100, 1, 1, 3],
|
'openBreaker': ['', f"0205 0001 0000", 8, 100, 1, 1, 3]
|
||||||
'turnOnGreen': ['', f"0105 0002 FF00", 8, 100, 1, 1, 3],
|
|
||||||
'turnOffGreen': ['', f"0105 0002 0000", 8, 100, 1, 1, 3],
|
|
||||||
'turnOnRed': ['', f"0105 0008 FF00", 8, 100, 1, 1, 3],
|
|
||||||
'turnOffRed': ['', f"0105 0000 0000", 8, 100, 1, 1, 3],
|
|
||||||
'turnOnAlarm': ['', f"0105 00A1 FF00", 8, 100, 1, 1, 3],
|
|
||||||
'turnOffAlarm': ['', f"0105 00A1 0000", 8, 100, 1, 1, 3]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.optFlag = 0
|
self.optFlag = 0
|
||||||
|
|
@ -797,51 +902,12 @@ class Breaker:
|
||||||
def update_config(self):
|
def update_config(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def exeCmd(self, cmdName) -> list: # type: ignore
|
def exeCmd(self, cmdName) -> list:
|
||||||
try:
|
|
||||||
info = ''
|
|
||||||
self.logger.info("==-==")
|
|
||||||
self.logger.info(cmdName)
|
|
||||||
cmd = self.cmdList.get(cmdName, None)
|
cmd = self.cmdList.get(cmdName, None)
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
return [False, None, f"Command {cmdName} not found in cmdList."]
|
return [False, None, f"Command {cmdName} not found in cmdList."]
|
||||||
retry = 0
|
self.logger.info(f"==-=={cmdName}")
|
||||||
data = bytearray().fromhex(cmd[1])
|
return self.client.exeCmd(cmd)
|
||||||
if (cmd[4] == 1):
|
|
||||||
data += bytearray(checkValue(data[0:]).to_bytes(2, 'big'))
|
|
||||||
if len(cmd) >= 7:
|
|
||||||
RETRYTIMES = int(cmd[6])
|
|
||||||
else:
|
|
||||||
RETRYTIMES = 1
|
|
||||||
while (retry < RETRYTIMES):
|
|
||||||
info += f"[{nowStr()}] Sent:{wordData2HexStr(data)}\n"
|
|
||||||
recvData = bytearray()
|
|
||||||
self.serial.write(data)
|
|
||||||
time.sleep(int(cmd[3])/1000.0)
|
|
||||||
recvData = self.serial.read(int(cmd[2]))
|
|
||||||
info += (f"[{nowStr()}] Echo:{wordData2HexStr(recvData)}\n")
|
|
||||||
rspLen = int(cmd[2])
|
|
||||||
if len(recvData) >= rspLen:
|
|
||||||
if recvData[0:2] == bytearray().fromhex(cmd[1][0:4]):
|
|
||||||
# info += f"[{nowStr()}] Echo:{wordData2HexStr(recvData[0:rspLen])}\n"
|
|
||||||
recvData = recvData[0:rspLen]
|
|
||||||
if (cmd[5] == 1):
|
|
||||||
crc = int.from_bytes(recvData[rspLen-2:rspLen], byteorder='big')
|
|
||||||
calc_value = checkValue(recvData[0:rspLen-2])
|
|
||||||
# info += f"{crc:04X}, {calc_value:04X}\n"
|
|
||||||
if crc == calc_value:
|
|
||||||
# self.logger.info(info)
|
|
||||||
return [True, recvData, info]
|
|
||||||
else:
|
|
||||||
# self.logger.info(info)
|
|
||||||
return [True, recvData, info]
|
|
||||||
retry += 1
|
|
||||||
# self.logger.info(info)
|
|
||||||
return [False, None, info]
|
|
||||||
except Exception as e:
|
|
||||||
info += f"[{nowStr()}] Error in exeCmd({cmd}): {str(e)}\n" # type: ignore
|
|
||||||
# self.logger.info(info)
|
|
||||||
return [False, None, info]
|
|
||||||
|
|
||||||
def parseData(self, cmdName, rawData):
|
def parseData(self, cmdName, rawData):
|
||||||
try:
|
try:
|
||||||
|
|
@ -865,7 +931,7 @@ class Breaker:
|
||||||
self.reg_values['locked'] = self.locked
|
self.reg_values['locked'] = self.locked
|
||||||
self.reg_values['closed'] = self.closed
|
self.reg_values['closed'] = self.closed
|
||||||
self.reg_values['reasonForLastOpen'] = self.reasonForLastOpen
|
self.reg_values['reasonForLastOpen'] = self.reasonForLastOpen
|
||||||
self.reg_values['alarm'] = self.alarm
|
self.reg_values['alarm'] = self.indicator.alarm if self.indicator else 0
|
||||||
self.reg_values['active_power'] = self.active_power
|
self.reg_values['active_power'] = self.active_power
|
||||||
self.reg_values['load_status'] = self.load_status
|
self.reg_values['load_status'] = self.load_status
|
||||||
|
|
||||||
|
|
@ -898,61 +964,44 @@ class Breaker:
|
||||||
|
|
||||||
def alarming(self):
|
def alarming(self):
|
||||||
"""报警时:红灯亮+蜂鸣器响,绿灯灭"""
|
"""报警时:红灯亮+蜂鸣器响,绿灯灭"""
|
||||||
if not self.alarm and self.closed & 0xFF == 0xF0:
|
if self.indicator:
|
||||||
self.exeCmd('turnOffGreen')
|
self.indicator.alarming(self.closed & 0xFF)
|
||||||
self.exeCmd('turnOnRed')
|
|
||||||
self.exeCmd('turnOnAlarm')
|
|
||||||
self.alarm = 1 # 设置报警标志
|
|
||||||
|
|
||||||
def unalarming(self):
|
def unalarming(self):
|
||||||
"""解除报警:根据合闸状态控制指示灯"""
|
"""解除报警:根据合闸状态控制指示灯"""
|
||||||
if self.alarm:
|
if self.indicator:
|
||||||
self.exeCmd('turnOffRed')
|
self.indicator.unalarming(self.closed & 0xFF)
|
||||||
self.exeCmd('turnOffAlarm')
|
|
||||||
# 如果是合闸状态,恢复绿灯
|
|
||||||
if self.closed & 0xFF == 0xF0:
|
|
||||||
self.exeCmd('turnOnGreen')
|
|
||||||
else:
|
|
||||||
self.exeCmd('turnOffGreen')
|
|
||||||
self.alarm = 0 # 清除报警标志
|
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
"""打开串口连接"""
|
"""打开串口连接"""
|
||||||
self.serial = serial.Serial(self.port, self.baudrate, timeout=self.timeout)
|
if self.client.open():
|
||||||
if not self.serial.is_open:
|
|
||||||
self.errorCode = 0x0001
|
|
||||||
return -1
|
|
||||||
else:
|
|
||||||
self.errorCode = 0
|
self.errorCode = 0
|
||||||
return 0
|
return 0
|
||||||
|
else:
|
||||||
|
self.errorCode = 0x0001
|
||||||
|
return -1
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self.serial.is_open:
|
self.client.close()
|
||||||
self.serial.close()
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""主运行循环"""
|
"""主运行循环"""
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
# self.logger.info(f"optFlag={self.optFlag}")
|
|
||||||
match self.optFlag:
|
match self.optFlag:
|
||||||
case 0:
|
case 0:
|
||||||
if self.open() == 0:
|
if self.open() == 0:
|
||||||
ret0 = self.exeCmd('openBreaker')
|
ret0 = self.exeCmd('openBreaker')
|
||||||
# 初始化时关闭所有指示灯
|
# 初始化时关闭所有指示灯
|
||||||
self.exeCmd('turnOffGreen')
|
if self.indicator:
|
||||||
self.exeCmd('turnOffRed')
|
self.indicator.turnOffAll()
|
||||||
self.exeCmd('turnOffAlarm')
|
|
||||||
# self.logger.info(f"setOverLimitValues ret: {ret0}")
|
|
||||||
ret1 = self.exeCmd('setOverLimitValues')
|
ret1 = self.exeCmd('setOverLimitValues')
|
||||||
# self.logger.info(f"setOverLimitValues ret: {ret1}")
|
|
||||||
ret2 = self.exeCmd('readOverLimitValues')
|
ret2 = self.exeCmd('readOverLimitValues')
|
||||||
self.logger.info(f"readOverLimitValues ret: {ret2}")
|
self.logger.info(f"readOverLimitValues ret: {ret2}")
|
||||||
ret3 = self.exeCmd('setOverLimitActionTime')
|
ret3 = self.exeCmd('setOverLimitActionTime')
|
||||||
# self.logger.info(f"setOverLimitValues ret: {ret3}")
|
|
||||||
ret4 = self.exeCmd('readOverLimitActionTime')
|
ret4 = self.exeCmd('readOverLimitActionTime')
|
||||||
self.logger.info(f"readOverLimitActionTime ret: {ret4}")
|
self.logger.info(f"readOverLimitActionTime ret: {ret4}")
|
||||||
if ret0[0] and ret1[0]:# and ret3[0]
|
if ret0[0] and ret1[0]:
|
||||||
self.optFlag = 1
|
self.optFlag = 1
|
||||||
continue
|
continue
|
||||||
self.optFlag = -1
|
self.optFlag = -1
|
||||||
|
|
@ -970,10 +1019,8 @@ class Breaker:
|
||||||
ret = self.exeCmd('openBreaker')
|
ret = self.exeCmd('openBreaker')
|
||||||
if ret[0]:
|
if ret[0]:
|
||||||
# 分闸成功后,关闭所有指示灯
|
# 分闸成功后,关闭所有指示灯
|
||||||
self.exeCmd('turnOffGreen')
|
if self.indicator:
|
||||||
self.exeCmd('turnOffRed')
|
self.indicator.turnOffAll()
|
||||||
self.exeCmd('turnOffAlarm')
|
|
||||||
self.alarm = 0 # 清除报警标志
|
|
||||||
self.optFlag = 1
|
self.optFlag = 1
|
||||||
continue
|
continue
|
||||||
self.optFlag = -1
|
self.optFlag = -1
|
||||||
|
|
@ -981,9 +1028,9 @@ class Breaker:
|
||||||
case 3:
|
case 3:
|
||||||
ret = self.exeCmd('closeBreaker')
|
ret = self.exeCmd('closeBreaker')
|
||||||
if ret[0]:
|
if ret[0]:
|
||||||
# 合闸成功后,点亮绿灯(如果没有报警)
|
# 合闸成功后,点亮绿灯
|
||||||
# if not self.alarm:
|
if self.indicator:
|
||||||
self.exeCmd('turnOnGreen')
|
self.indicator.turnOnGreen()
|
||||||
self.optFlag = 1
|
self.optFlag = 1
|
||||||
continue
|
continue
|
||||||
self.optFlag = -1
|
self.optFlag = -1
|
||||||
|
|
@ -1700,7 +1747,13 @@ class ModbusGateway:
|
||||||
self.logger.info(f"Local modbusTCP service starts, IP={self.host}, port={self.port}")
|
self.logger.info(f"Local modbusTCP service starts, IP={self.host}, port={self.port}")
|
||||||
|
|
||||||
self.gps = GPS(self.config_manager.config['gps'], self.logger)
|
self.gps = GPS(self.config_manager.config['gps'], self.logger)
|
||||||
self.breaker = Breaker(self.config_manager.config['breaker'], self.logger)
|
|
||||||
|
# 将 indicator 配置添加到 breaker 配置中
|
||||||
|
breaker_config = self.config_manager.config['breaker'].copy()
|
||||||
|
if 'indicator' in self.config_manager.config:
|
||||||
|
breaker_config['indicator'] = self.config_manager.config['indicator']
|
||||||
|
self.breaker = Breaker(breaker_config, self.logger)
|
||||||
|
|
||||||
self.lsdaq = LSDAQ(self.config_manager.config['lsdaq'], self.logger)
|
self.lsdaq = LSDAQ(self.config_manager.config['lsdaq'], self.logger)
|
||||||
self.hsdaq = HSDAQ(self.config_manager.config['hsdaq'], self.logger)
|
self.hsdaq = HSDAQ(self.config_manager.config['hsdaq'], self.logger)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue