空开有效,需要调灯
parent
8c57551006
commit
acca3cfaf7
|
|
@ -7,6 +7,11 @@ gps:
|
|||
port: /dev/ttyLP4
|
||||
baudrate: 9600
|
||||
timeout: 50
|
||||
# indicator(指示灯和蜂鸣器)配置
|
||||
indicator:
|
||||
port: /dev/ttyUSB1
|
||||
baudrate: 9600
|
||||
timeout: 50
|
||||
# breaker(断路器)配置
|
||||
breaker:
|
||||
port: /dev/ttyUSB0
|
||||
|
|
|
|||
|
|
@ -723,6 +723,110 @@ class LSDAQ:
|
|||
self.close()
|
||||
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:
|
||||
def __init__(self, config:dict, logger):
|
||||
# 加载配置参数
|
||||
|
|
@ -744,13 +848,21 @@ class Breaker:
|
|||
self.task_start_threshold = config.get('task_start_threshold', 2000)
|
||||
self.task_stop_threshold = config.get('task_stop_threshold', 2000)
|
||||
self.locked = 0
|
||||
self.closed = 0x0F # 0x0F:分闸 0xF0:合闸
|
||||
self.reasonForLastOpen = 15 # F:无 1:过流 2:漏电 3:过温 4:过载 5:过压 6:欠压 7:远程 8:模组 9:失压 A:锁扣 B:限电 0: 本地
|
||||
self.closed = 0x0F
|
||||
self.reasonForLastOpen = 15
|
||||
self.active_powers = []
|
||||
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)
|
||||
UVV = config.get('UVV', 150)
|
||||
|
|
@ -773,22 +885,15 @@ class Breaker:
|
|||
'active_power': 0,
|
||||
'load_status': 0
|
||||
}
|
||||
# 构建指令集
|
||||
# 构建指令集(仅断路器指令)
|
||||
self.cmdList = {
|
||||
# 指令格式:指令描述,指令字符串,回复长度,超时时间,发送校验标志,接收校验标志,重试次数
|
||||
'readAllDatas': ['', f"0204 0000 0027", 83, 300, 1, 1, 3],
|
||||
'readOverLimitValues': ['', f"0203 0002 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],
|
||||
'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],
|
||||
'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]
|
||||
'openBreaker': ['', f"0205 0001 0000", 8, 100, 1, 1, 3]
|
||||
}
|
||||
|
||||
self.optFlag = 0
|
||||
|
|
@ -797,51 +902,12 @@ class Breaker:
|
|||
def update_config(self):
|
||||
pass
|
||||
|
||||
def exeCmd(self, cmdName) -> list: # type: ignore
|
||||
try:
|
||||
info = ''
|
||||
self.logger.info("==-==")
|
||||
self.logger.info(cmdName)
|
||||
def exeCmd(self, cmdName) -> list:
|
||||
cmd = self.cmdList.get(cmdName, None)
|
||||
if cmd is None:
|
||||
return [False, None, f"Command {cmdName} not found in cmdList."]
|
||||
retry = 0
|
||||
data = bytearray().fromhex(cmd[1])
|
||||
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]
|
||||
self.logger.info(f"==-=={cmdName}")
|
||||
return self.client.exeCmd(cmd)
|
||||
|
||||
def parseData(self, cmdName, rawData):
|
||||
try:
|
||||
|
|
@ -865,7 +931,7 @@ class Breaker:
|
|||
self.reg_values['locked'] = self.locked
|
||||
self.reg_values['closed'] = self.closed
|
||||
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['load_status'] = self.load_status
|
||||
|
||||
|
|
@ -898,61 +964,44 @@ class Breaker:
|
|||
|
||||
def alarming(self):
|
||||
"""报警时:红灯亮+蜂鸣器响,绿灯灭"""
|
||||
if not self.alarm and self.closed & 0xFF == 0xF0:
|
||||
self.exeCmd('turnOffGreen')
|
||||
self.exeCmd('turnOnRed')
|
||||
self.exeCmd('turnOnAlarm')
|
||||
self.alarm = 1 # 设置报警标志
|
||||
if self.indicator:
|
||||
self.indicator.alarming(self.closed & 0xFF)
|
||||
|
||||
def unalarming(self):
|
||||
"""解除报警:根据合闸状态控制指示灯"""
|
||||
if self.alarm:
|
||||
self.exeCmd('turnOffRed')
|
||||
self.exeCmd('turnOffAlarm')
|
||||
# 如果是合闸状态,恢复绿灯
|
||||
if self.closed & 0xFF == 0xF0:
|
||||
self.exeCmd('turnOnGreen')
|
||||
else:
|
||||
self.exeCmd('turnOffGreen')
|
||||
self.alarm = 0 # 清除报警标志
|
||||
if self.indicator:
|
||||
self.indicator.unalarming(self.closed & 0xFF)
|
||||
|
||||
def open(self):
|
||||
"""打开串口连接"""
|
||||
self.serial = serial.Serial(self.port, self.baudrate, timeout=self.timeout)
|
||||
if not self.serial.is_open:
|
||||
self.errorCode = 0x0001
|
||||
return -1
|
||||
else:
|
||||
if self.client.open():
|
||||
self.errorCode = 0
|
||||
return 0
|
||||
else:
|
||||
self.errorCode = 0x0001
|
||||
return -1
|
||||
|
||||
def close(self):
|
||||
if self.serial.is_open:
|
||||
self.serial.close()
|
||||
self.client.close()
|
||||
|
||||
def run(self):
|
||||
"""主运行循环"""
|
||||
try:
|
||||
while True:
|
||||
# self.logger.info(f"optFlag={self.optFlag}")
|
||||
match self.optFlag:
|
||||
case 0:
|
||||
if self.open() == 0:
|
||||
ret0 = self.exeCmd('openBreaker')
|
||||
# 初始化时关闭所有指示灯
|
||||
self.exeCmd('turnOffGreen')
|
||||
self.exeCmd('turnOffRed')
|
||||
self.exeCmd('turnOffAlarm')
|
||||
# self.logger.info(f"setOverLimitValues ret: {ret0}")
|
||||
if self.indicator:
|
||||
self.indicator.turnOffAll()
|
||||
ret1 = self.exeCmd('setOverLimitValues')
|
||||
# self.logger.info(f"setOverLimitValues ret: {ret1}")
|
||||
ret2 = self.exeCmd('readOverLimitValues')
|
||||
self.logger.info(f"readOverLimitValues ret: {ret2}")
|
||||
ret3 = self.exeCmd('setOverLimitActionTime')
|
||||
# self.logger.info(f"setOverLimitValues ret: {ret3}")
|
||||
ret4 = self.exeCmd('readOverLimitActionTime')
|
||||
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
|
||||
continue
|
||||
self.optFlag = -1
|
||||
|
|
@ -970,10 +1019,8 @@ class Breaker:
|
|||
ret = self.exeCmd('openBreaker')
|
||||
if ret[0]:
|
||||
# 分闸成功后,关闭所有指示灯
|
||||
self.exeCmd('turnOffGreen')
|
||||
self.exeCmd('turnOffRed')
|
||||
self.exeCmd('turnOffAlarm')
|
||||
self.alarm = 0 # 清除报警标志
|
||||
if self.indicator:
|
||||
self.indicator.turnOffAll()
|
||||
self.optFlag = 1
|
||||
continue
|
||||
self.optFlag = -1
|
||||
|
|
@ -981,9 +1028,9 @@ class Breaker:
|
|||
case 3:
|
||||
ret = self.exeCmd('closeBreaker')
|
||||
if ret[0]:
|
||||
# 合闸成功后,点亮绿灯(如果没有报警)
|
||||
# if not self.alarm:
|
||||
self.exeCmd('turnOnGreen')
|
||||
# 合闸成功后,点亮绿灯
|
||||
if self.indicator:
|
||||
self.indicator.turnOnGreen()
|
||||
self.optFlag = 1
|
||||
continue
|
||||
self.optFlag = -1
|
||||
|
|
@ -1700,7 +1747,13 @@ class ModbusGateway:
|
|||
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.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.hsdaq = HSDAQ(self.config_manager.config['hsdaq'], self.logger)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue