利用命名管道实现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

 

posted @ 2024-05-28 18:02  碳酸钾K2CO3  阅读(15)  评论(0编辑  收藏  举报