Files
wire_controlsystem/EMV/EMV_test.py

473 lines
19 KiB
Python
Raw Normal View History

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
# @Time : 2026/1/8 16:52
# @Author : reenrr
# @File : EMV_test.py
# @Desc : 网络继电器控制输入、输出设备测试程序
'''
import socket
import binascii
import time
from threading import Event, Lock
import threading
import logging
# 网络继电器的 IP 和端口
HOST = '192.168.5.18'
PORT = 50000
# 控件命名映射
SOLENOID_VALVE1 = 'solenoid_valve1' # 控制排料机构NG时的电磁阀1
SOLENOID_VALVE2 = 'solenoid_valve2' # 控制排料机构NG时的电磁阀2
ABSORB_SOLENOID_VALVE1 = 'absorb_solenoid_valve1' # 控制吸取设备的电磁阀1
ABSORB_SOLENOID_VALVE2 = 'absorb_solenoid_valve2' # 控制吸取设备的电磁阀2
ABSORB_SOLENOID_VALVE3 = 'absorb_solenoid_valve3' # 控制吸取设备的电磁阀3
ABSORB_SOLENOID_VALVE4 = 'absorb_solenoid_valve4' # 控制吸取设备的电磁阀4
ABSORB_SOLENOID_VALVE5 = 'absorb_solenoid_valve5' # 控制吸取设备的电磁阀5
# 传感器命名映射
CONVEYOR1_SENSOR = 'conveyor1_sensor' # 传送带1的行程开关
CONVEYOR2_SENSOR = 'conveyor2_sensor' # 传送带2的行程开关
PRESS_SENSOR1 = 'press_sensor1' # 传送带1旁边的按压开关1
PRESS_SENSOR2 = 'press_sensor2' # 传送带1旁边的按压开关2
FIBER_SENSOR = 'fiber_sensor' # 传送带1旁边的光纤传感器
# 控件控制报文
valve_commands = {
SOLENOID_VALVE1: {
'open': '00000000000601050000FF00',
'close': '000000000006010500000000',
},
SOLENOID_VALVE2: {
'open': '00000000000601050001FF00',
'close': '000000000006010500010000',
},
ABSORB_SOLENOID_VALVE1: {
'open': '00000000000601050002FF00',
'close': '000000000006010500020000',
},
ABSORB_SOLENOID_VALVE2: {
'open': '00000000000601050002FF00',
'close': '000000000006010500030000',
},
ABSORB_SOLENOID_VALVE3: {
'open': '00000000000601050002FF00',
'close': '000000000006010500040000',
},
ABSORB_SOLENOID_VALVE4: {
'open': '00000000000601050002FF00',
'close': '000000000006010500050000',
},
ABSORB_SOLENOID_VALVE5: {
'open': '00000000000601050002FF00',
'close': '000000000006010500060000',
}
}
# 读取状态命令
read_status_command = {
'devices': '000000000006010100000008',
'sensors': '000000000006010200000008'
}
# 控件对应 DO 位(从低到高)
device_bit_map = {
SOLENOID_VALVE1: 0,
SOLENOID_VALVE2: 1,
ABSORB_SOLENOID_VALVE1: 2,
ABSORB_SOLENOID_VALVE2: 3,
ABSORB_SOLENOID_VALVE3: 4,
ABSORB_SOLENOID_VALVE4: 5,
ABSORB_SOLENOID_VALVE5: 6
}
device_name_map = {
SOLENOID_VALVE1: "电磁阀1",
SOLENOID_VALVE2: "电磁阀2",
ABSORB_SOLENOID_VALVE1: "吸取装置电磁阀1",
ABSORB_SOLENOID_VALVE2: "吸取装置电磁阀2",
ABSORB_SOLENOID_VALVE3: "吸取装置电磁阀3",
ABSORB_SOLENOID_VALVE4: "吸取装置电磁阀4",
ABSORB_SOLENOID_VALVE5: "吸取装置电磁阀5",
}
# 传感器对应位(从低到高)
sensor_bit_map = {
FIBER_SENSOR: 0,
PRESS_SENSOR1: 1,
PRESS_SENSOR2: 2,
CONVEYOR1_SENSOR: 4,
CONVEYOR2_SENSOR: 3,
# 根据你继电器的配置,继续添加更多传感器
}
sensor_name_map = {
FIBER_SENSOR: '光纤传感器',
PRESS_SENSOR1: '按压开关1',
PRESS_SENSOR2: '按压开关2',
CONVEYOR1_SENSOR: '传送带1开关',
CONVEYOR2_SENSOR: '传送带2开关'
}
# -------------全局事件-------------
press_sensors_triggered = Event()
fiber_triggered = Event() # 光纤传感器触发事件
fiber_lock = Lock() # 线程锁,保护共享变量
valve1_open_flag = False # 电磁阀1打开标志
class RelayController:
def __init__(self):
"""初始化继电器控制器"""
self.socket = None
# 线程相关状态
self.fiber_thread = None # 保存光纤传感器线程对象
self.fiber_monitor_running = False # 光纤传感器监听线程运行标志
self.press_sensors_thread = None # 保存按压开关线程对象
self.press_sensors_monitor_running = False # 按压传感器监听线程运行标志
self.last_press_sensor_status = False # 记录传感器上一次状态,初始为无信号(用于上升沿检测)
def send_command(self, command):
"""
将十六进制字符串转换为字节数据并发送
:param command: 十六进制字符串
:return: 响应字节数据 / False
"""
try:
byte_data = binascii.unhexlify(command)
# 创建套接字并连接到继电器
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT))
sock.send(byte_data)
# 接收响应
response = sock.recv(1024)
# logging.info(f"收到响应: {binascii.hexlify(response)}")
# 校验响应
return response
except Exception as e:
logging.info(f"通信错误: {e}")
return False
def get_all_device_status(self, command_type='devices'):
"""
获取所有设备/传感器状态
:param command_type: 'devices'控件 / 'sensors'传感器
:return: 状态字典 {设备名: 状态(bool)}
"""
command = read_status_command.get(command_type)
if not command:
logging.info(f"未知的读取类型: {command_type}")
return {}
response = self.send_command(command)
status_dict = {}
if response and len(response) >= 10:
status_byte = response[9] # 状态在第10字节
status_bin = f"{status_byte:08b}"[::-1]
if command_type == 'devices':
bit_map = device_bit_map
name_map = device_name_map
elif command_type == 'sensors':
bit_map = sensor_bit_map
name_map = sensor_name_map
else:
logging.info("不支持的映射类型")
return{}
for key, bit_index in bit_map.items():
state = status_bin[bit_index] == '1'
status_dict[key] = state
# readable = "开启" if state else "关闭"
# logging.info(f"{device.capitalize()} 状态: {readable}")
else:
logging.info("读取状态失败或响应无效")
return status_dict
def get_device_status(self, device_name, command_type='devices'):
"""
获取单个控件/传感器状态
:param device_name:设备名称
:param command_type: 'devices'/'sensors'
:return:True(开启) / False(关闭) / None(无法读取)
"""
status = self.get_all_device_status(command_type)
return status.get(device_name, None)
def open(self, solenoid_valve1=False, solenoid_valve2=False, absorb_solenoid_valve1=False,
absorb_solenoid_valve2=False, absorb_solenoid_valve3=False, absorb_solenoid_valve4=False,
absorb_solenoid_valve5=False):
"""
根据状态决定是否执行开操作
:param solenoid_valve1:是否打开电磁阀1
:param solenoid_valve2:是否打开电磁阀2
:param absorb_solenoid_valve1:是否打开吸取装置电磁阀1
:param absorb_solenoid_valve2:是否打开吸取装置电磁阀2
:param absorb_solenoid_valve3:是否打开吸取装置电磁阀3
:param absorb_solenoid_valve4:是否打开吸取装置电磁阀4
:param absorb_solenoid_valve5:是否打开吸取装置电磁阀5
:return:
"""
global valve1_open_time, valve1_open_flag
status = self.get_all_device_status()
if solenoid_valve1 and not status.get(SOLENOID_VALVE1, False):
print("打开电磁阀1")
self.send_command(valve_commands[SOLENOID_VALVE1]['open'])
# 记录电磁阀1打开时的时间戳和标志
with fiber_lock:
valve1_open_time = time.time()
valve1_open_flag = True
if solenoid_valve2 and not status.get(SOLENOID_VALVE2, False):
print("打开电磁阀2")
self.send_command(valve_commands[SOLENOID_VALVE2]['open'])
if absorb_solenoid_valve1 and not status.get(ABSORB_SOLENOID_VALVE1, False):
print("打开吸取装置电磁阀1")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE1]['open'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
if absorb_solenoid_valve2 and not status.get(ABSORB_SOLENOID_VALVE2, False):
print("打开吸取装置电磁阀2")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE2]['open'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
if absorb_solenoid_valve3 and not status.get(ABSORB_SOLENOID_VALVE3, False):
print("打开吸取装置电磁阀3")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE3]['open'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
if absorb_solenoid_valve4 and not status.get(ABSORB_SOLENOID_VALVE4, False):
print("打开吸取装置电磁阀4")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE4]['open'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
if absorb_solenoid_valve5 and not status.get(ABSORB_SOLENOID_VALVE5, False):
print("打开吸取装置电磁阀5")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE5]['open'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
# 根据状态决定是否执行关操作
def close(self, solenoid_valve1=False, solenoid_valve2=False, absorb_solenoid_valve1=False,
absorb_solenoid_valve2=False, absorb_solenoid_valve3=False, absorb_solenoid_valve4=False,
absorb_solenoid_valve5=False):
"""
根据状态决定是否执行关操作
:param solenoid_valve1:是否关闭电磁阀1
:param solenoid_valve2:是否关闭电磁阀2
:param absorb_solenoid_valve1:是否关闭吸取电磁阀1
:param absorb_solenoid_valve2:是否关闭吸取电磁阀2
:param absorb_solenoid_valve3:是否关闭吸取电磁阀3
:param absorb_solenoid_valve4:是否关闭吸取电磁阀4
:param absorb_solenoid_valve5:是否关闭吸取电磁阀5
:return:
"""
global valve1_open_flag
status = self.get_all_device_status()
if solenoid_valve1 and status.get(SOLENOID_VALVE1, True):
print("关闭电磁阀1")
self.send_command(valve_commands[SOLENOID_VALVE1]['close'])
# 重置电磁阀1打开标志
with fiber_lock:
valve1_open_flag = False
if solenoid_valve2 and status.get(SOLENOID_VALVE2, True):
print("关闭电磁阀2")
self.send_command(valve_commands[SOLENOID_VALVE2]['close'])
if absorb_solenoid_valve1 and status.get(ABSORB_SOLENOID_VALVE1, True):
print("关闭吸取装置电磁阀1")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE1]['close'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
if absorb_solenoid_valve2 and status.get(ABSORB_SOLENOID_VALVE2, True):
print("关闭吸取装置电磁阀2")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE2]['close'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
if absorb_solenoid_valve3 and status.get(ABSORB_SOLENOID_VALVE3, True):
print("关闭吸取装置电磁阀3")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE3]['close'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
if absorb_solenoid_valve4 and status.get(ABSORB_SOLENOID_VALVE4, True):
print("关闭吸取装置电磁阀4")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE4]['close'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
if absorb_solenoid_valve5 and status.get(ABSORB_SOLENOID_VALVE5, True):
print("关闭吸取装置电磁阀5")
self.send_command(valve_commands[ABSORB_SOLENOID_VALVE5]['close'])
time.sleep(1) # 实际测试需要考虑这个延时是否合适
def fiber_sensor_monitor(self):
"""
光纤传感器监听线程专门检测电磁阀打开后的触发状态
"""
global fiber_triggered, valve1_open_flag
logging.info("光纤传感器监听线程已启动")
while self.fiber_monitor_running:
try:
# 增加短休眠降低CPU占用避免错过信号
time.sleep(0.005)
# 获取光纤传感器状态
fiber_status = self.get_device_status(FIBER_SENSOR, 'sensors')
# 检测是否检测到信号
if fiber_status:
with fiber_lock:
# 检查电磁阀1是否处于打开状态
if valve1_open_flag:
fiber_triggered.set()
# 防止重复触发
time.sleep(0.1)
fiber_triggered.clear()
except Exception as e:
logging.info(f"光纤传感器监听异常:{e}")
time.sleep(0.1) # 异常时增加休眠
def start_fiber_monitor(self):
"""启动光纤传感器监听线程"""
# 检查线程是否已在运行
if self.fiber_monitor_running or (self.fiber_thread and self.fiber_thread.is_alive()):
logging.warning("光纤传感器监听线程已在运行,无需重复启动")
return
# 启动线程
self.fiber_monitor_running = True
self.fiber_thread = threading.Thread(
target=self.fiber_sensor_monitor,
daemon=True
)
self.fiber_thread.start()
logging.info("光纤传感器监听线程启动成功")
def stop_fiber_monitor(self):
"""停止光纤传感器监听线程"""
self.fiber_monitor_running = False
# 等待线程完全退出
if self.fiber_thread and self.fiber_thread.is_alive():
self.fiber_thread.join(timeout=1)
logging.info("光纤传感器监听线程已停止")
def press_sensors_monitor(self, check_interval=0.1):
"""
双压传感器监听线程
:param check_interval: 检测间隔
:return:
"""
global press_sensors_triggered
logging.info("双压传感器监听线程已启动")
while self.press_sensors_monitor_running:
try:
# 检测两个传感器任意一个是否触发
press_sensor1_status = self.get_device_status(PRESS_SENSOR1, 'sensors')
press_sensor2_status = self.get_device_status(PRESS_SENSOR2, 'sensors')
current_sensor_state = press_sensor1_status or press_sensor2_status
# 上升沿触发(仅从无信号-->有信号时,才触发事件)
if current_sensor_state and not self.last_press_sensor_status:
press_sensors_triggered.set() # 触发事件,通知主线程
logging.info("双压传感器触发:线条已落到传送带")
# 更新上一次传感器状态,为下一次上升沿检测做准备
self.last_press_sensor_status = current_sensor_state
# 传感器检测间隔
time.sleep(check_interval)
except Exception as e:
logging.info(f"双压传感器监听异常: {e}")
time.sleep(0.1) # 异常时增加休眠
def start_press_sensors_monitor(self):
"""启动双压传感器监听线程"""
# 检查线程是否已在运行
if self.press_sensors_monitor_running or (self.press_sensors_thread and self.press_sensors_thread.is_alive()):
logging.warning("双压传感器监听线程已在运行,无需重复启动")
return
# 启动线程
self.press_sensors_monitor_running = True
self.press_sensors_thread = threading.Thread(
target=self.press_sensors_monitor,
daemon=True
)
self.press_sensors_thread.start()
logging.info("双压传感器监听线程启动成功")
def stop_press_sensors_monitor(self):
"""停止光纤传感器监听线程"""
self.press_sensors_monitor_running = False
# 等待线程完全退出
if self.press_sensors_thread and self.press_sensors_thread.is_alive():
self.press_sensors_thread.join(timeout=1)
logging.info("双压传感器监听线程已停止")
# 全局继电器实例
global_relay = RelayController()
# ------------对外接口----------
def control_solenoid():
"""
线条不合格场景专用控制电磁阀+监听光纤传感器
执行流程:启动监听-->打开电磁阀-->等待检测-->关闭电磁阀-->停止监听
"""
global fiber_triggered, global_relay
try:
# 启动光纤传感器监听线程
global_relay.start_fiber_monitor()
# 重置光纤传感器触发事件,准备检测
fiber_triggered.clear()
# 同时打开电磁阀1、2
global_relay.open(solenoid_valve1=True, solenoid_valve2=True)
logging.info("电磁阀1、2已打开")
# 等待线条掉落最多等待2秒
timeout = 3.0
start_time = time.time()
fiber_detected = False
# 等待光纤传感器触发或超时
while time.time() - start_time < timeout:
if fiber_triggered.is_set():
fiber_detected = True
logging.info("该NG线条掉入费料区")
break
time.sleep(0.01) # 降低CPU空转
if not fiber_detected:
logging.info("出问题!!!,红外传感器未检测到线条")
# 关闭电磁阀1、2
time.sleep(0.2) # 等待线条掉落
global_relay.close(solenoid_valve1=True, solenoid_valve2=True)
logging.info("电磁阀1、2已关闭")
# 停止光纤传感器监听线程
global_relay.stop_fiber_monitor()
except Exception as e:
logging.info(f"操作电磁阀失败:{str(e)}")
# 异常时也要停止线程,避免残留
global_relay.stop_fiber_monitor()
# ------------测试接口-------------
if __name__ == '__main__':
control_solenoid()