利用命名管道实现IT和OT的融合
1 命名管道读写操作的第一次尝试
1. 首先在OpenPLC editor中构建如下PLC程序,具体可参考博客:https://www.cnblogs.com/Daemon17/p/17923612.html
这个程序的目的是:当红外传感器监测到目标后(PB1输出1),PB2的变量会为1,从而利用命名管道将信息传递给另一读取该信息的IT程序进程。
2. 在OpenPLC的硬件初始化文件webserver/core/psm/main.py中有关命令管道写入的代码为:
# 定义FIFO文件路径
fifo = "../../sensor_fifo"
fifo_path = os.path.normpath(os.path.dirname(os.path.abspath(__file__))+fifo)
……
……
while (not psm.should_quit()):
# 更新输入
……
# 更新输出
a = psm.get_var("QX0.0")
# 打开FIFO进行写入
if (a==True)
with open(fifo_path, "w") as fifo:
# 写入数据到FIFO
fifo.write(f"{a}\n")
fifo.flush() # 确保数据立即写入管道
……
sleep(0.1) # 调整循环时间
3. 在有关的IT程序中读写FIFO管道的内容
# 定义FIFO文件路径
fifo = "/../sensor_fifo"
fifo_path = os.path.normpath(os.path.dirname(os.path.abspath(__file__))+fifo)
with open(fifo_path, "r") as fifo:
while True:
message = fifo.readline().strip()
if message == "True":
cap = cv2.VideoCapture(0)
2. 遇到的问题及解决方案
1. 由于使用的嵌入式评估板TL62x没有GPIO,故外接了一块RM10x0用来进行USB转GPIO;另一方面,本项目需要用到工业摄像机,也是USB接口的;然而TL62x只有一个USB接口。所以购买了一块USB一转四的拓展坞。
在使用时发现,尽管可以正常使用GPIO,但摄像机一直无法连接,在规定时间内总是无法找到摄像机,像是因为循环的psm程序一直占用了拓展坞的通道导致的。后来发现,在IT程序中加入0.1s的延时即可基于拓展坞同时使用GPIO和工业摄像机,如下:
with open(fifo_path, "r") as fifo:
while True:
sleep(0.1)
message = fifo.readline().strip()
if message == "True":
cap = cv2.VideoCapture(0)
2. 我们设定的场景是:当红外传感器检测到传送带上有绿色小物块时,TL62x上运行的PLC程序就会停止传送带,并将True写入FIFO管道,IT程序读到FIFO管道中的数据后,会开启摄像机,并调用颜色检测、机械臂调用等函数。
然后在实际运行中,我们发现,由于机械臂接收信号、夹取物块,需要一定时间,导致PLC程序写入了过多的True,导致对于同一物块,颜色检测、机械臂调用等函数被反复调用。注意:在使用FIFO进行读写操作时,写操作是可以一直进行的;但如果读操作没有数据可读取,readline将阻塞,直到有数据可用。
所以我们引入全局变量,使得每个物块的检测只写入一次true到FIFO管道中,且颜色检测、机械臂调用等函数只被true调用一次(i.e.,即使有多个连续的true,也只调用一次)。
代码修改如下:
(1)在OpenPLC的硬件初始化文件webserver/core/psm/main.py中有关命令管道写入的代码为:
num_true = 0
while (not psm.should_quit()):
# 更新输入
……
# 更新输出
a = psm.get_var("QX0.0")
# 打开FIFO进行写入
if (a==True and num_true==0)
num_true = num_true + 1
with open(fifo_path, "w") as fifo:
# 写入数据到FIFO
fifo.write(f"{a}\n")
fifo.flush() # 确保数据立即写入管道
elif (a==False)
num_true = 0
with open(fifo_path, "w") as fifo:
# 写入数据到FIFO
fifo.write(f"{a}\n")
fifo.flush() # 确保数据立即写入管道
sleep(0.1) # 调整循环时间
注意:当有线程要读取这些fifo语句时,这种有选择性地写入可能会导致线程阻塞。
例如:有一个线程需要一次执行如下功能:读取fifo文件,开启摄像头获取图像(with lock),进行图像识别,调用机械臂(with lock),如果是上述写入fifo文件的逻辑(即只写入一次true,直到有false才可能写入第二次true),很可能出现,因为机械臂被其他线程占用(先有了lock),因此这个线程被机械臂被机械臂阻塞,所以这个线程的fifo文件一直无法读取。长时间fifo文件没有读取,导致上述psm文件也被阻塞。另一方面,因为这个线程处理的物块一直没有被机械臂拿走,所以上述逻辑导致psm无法写入信息到fifo文件,导致这个线程在第一步读取fifo文件被阻塞,最终导致机械臂无法被调用,所以这个线程“死了”。
简单来说,就是①写入fifo文件等着机械臂拿走物块,然而②机械臂拿走物块又在等着从fifo文件读取信息,然而③从fifo文件读取信息等待着psm写入fifo文件。这三个事情互相等待,造成了死锁。
其中最大的疑惑是:为什么这个线程的机械臂动作被另一个线程阻塞后不会自动恢复。。
(2)在有关的IT程序中读写FIFO管道的内容
num_true = 0
with open(fifo_path, "r") as fifo:
while True:
sleep(0.1)
message = fifo.readline().strip()
if message == "True" and num_true == 0:
cap = cv2.VideoCapture(0)
num_true = num_true + 1
elif message == "False":
num_true = 0