Python连接FactoryIO仿真控制练习

Python连接FactoryIO仿真控制练习

概述:

今天下午没什么事,又看了一下FactoryIO仿真软件,以前做这种控制总是用PLC去做,使用PLC做逻辑控制的时候,定时器、计数器、脉冲检测等一般都为既有的功能块,直接调用就可以了,相对逻辑实现不用考虑那么多底层逻辑的细节,最近正在学习python,差不多有三个月了,所以找了个简单的控制例程,打算用python写一下控制逻辑,思考的过程挺有意思,和大家分享一下。使用的例程是FactoryIO自带的一个简单例程“Sort by Weight”,即使用称重皮带检测重量,通过转向盘进行分拣物品。

在这里插入图片描述

一、通讯连接
FactoryIO作为一款自动化应用类的仿真教学软件,提供了多种连接各类品牌的控制器的连接方式,可以连接真实PLC也可以连接部分品牌的PLC仿真软件。对于未列出的型号的控制器,也可以通过OPC Clinet和ModbusTCP进行连接。我在百度上查阅了一些例程,python实现OPC UA、OPC DA和socker server,client等都有相应的库,因此在通讯连接上没有耽误太多时间。这里选择了使用Modbus TCP/IP Client的连接方式,使用Factory IO作为ModbusTCP Server,Python作为Client主动读写数据。
在这里插入图片描述
二、 界面UI
Pyqt刚开始学习,还比较生疏,所以选择用tkinter做了个简单的界面。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200815230427546.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA1NDk5MTY=,size_16,color_FFFFFF,t_70#pic_center

界面上构成组件不是很多,但是手动调整还是挺耽误时间的,因此使用了Visual TK网站上提供的在线拖拽构建界面的方式,网址是:https://www.visualtk.com/,可以实现和pyqt的qtcerater类似的功能,但是只能支持tkinter的部分组件,生成后的代码需要手动调整一下组件命名。

生成后的UI文件如下:

# _*_ coding:utf-8 _*_
""" --------------------------------------------------
文件名称: PythonWithFactoryIO-ui_mian-qsp34
日期时间: 2020-08-15-17:54
文件备注: 
---------------------------------------------------"""
import tkinter as tk
import tkinter.font as tkFont


class UiSetup:
    """--"""
    def __init__(self, root):
        # setting title
        self.root = root
        self.root.title()
        # setting window size
        width = 440
        height = 380
        screenwidth = self.root.winfo_screenwidth()
        screenheight = self.root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)

        self.root.geometry(alignstr)
        self.root.resizable(width=False, height=False)

        self.button_connect_factory = tk.Button(self.root)
        self.button_connect_factory["bg"] = "#dddddd"
        ft = tkFont.Font(family='Times', size=10)
        self.button_connect_factory["font"] = ft
        self.button_connect_factory["fg"] = "#000000"
        self.button_connect_factory["justify"] = "center"
        self.button_connect_factory["text"] = "连接/断开"
        self.button_connect_factory.place(x=80, y=310, width=79, height=30)
        self.button_connect_factory["command"] = self.button_connect_factory_command

        self.button_start_control = tk.Button(self.root)
        self.button_start_control["bg"] = "#dddddd"
        ft = tkFont.Font(family='Times', size=10)
        self.button_start_control["font"] = ft
        self.button_start_control["fg"] = "#000000"
        self.button_start_control["justify"] = "center"
        self.button_start_control["text"] = "启动/停止"
        self.button_start_control.place(x=320, y=310, width=70, height=30)
        self.button_start_control["command"] = self.button_start_control_command

        self.entry_show_di = tk.Entry(self.root)
        self.entry_show_di["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times', size=10)
        self.entry_show_di["font"] = ft
        self.entry_show_di["fg"] = "#333333"
        self.entry_show_di["justify"] = "center"
        self.entry_show_di["text"] = "Entry"
        self.entry_show_di.place(x=80, y=110, width=312, height=30)

        self.entry_show_do = tk.Entry(self.root)
        self.entry_show_do["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times', size=10)
        self.entry_show_do["font"] = ft
        self.entry_show_do["fg"] = "#333333"
        self.entry_show_do["justify"] = "center"
        self.entry_show_do["text"] = "Entry"
        self.entry_show_do.place(x=80, y=190, width=312, height=30)

        GLabel_901 = tk.Label(self.root)
        ft = tkFont.Font(family='Times', size=10)
        GLabel_901["font"] = ft
        GLabel_901["fg"] = "#333333"
        GLabel_901["justify"] = "center"
        GLabel_901["text"] = "DI"
        GLabel_901.place(x=0, y=110, width=70, height=25)

        GLabel_3 = tk.Label(self.root)
        ft = tkFont.Font(family='Times', size=10)
        GLabel_3["font"] = ft
        GLabel_3["fg"] = "#333333"
        GLabel_3["justify"] = "center"
        GLabel_3["text"] = "DO"
        GLabel_3.place(x=0, y=190, width=70, height=25)

        GLabel_339 = tk.Label(self.root)
        ft = tkFont.Font(family='Times', size=10)
        GLabel_339["font"] = ft
        GLabel_339["fg"] = "#333333"
        GLabel_339["justify"] = "center"
        GLabel_339["text"] = "执行周期"
        GLabel_339.place(x=0, y=50, width=70, height=25)

        self.entry_show_cycle_time = tk.Entry(self.root)
        self.entry_show_cycle_time["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times', size=10)
        self.entry_show_cycle_time["font"] = ft
        self.entry_show_cycle_time["fg"] = "#333333"
        self.entry_show_cycle_time["justify"] = "center"
        self.entry_show_cycle_time["text"] = "Entry"
        self.entry_show_cycle_time.place(x=80, y=50, width=71, height=30)

        GLabel_738 = tk.Label(self.root)
        ft = tkFont.Font(family='Times', size=10)
        GLabel_738["font"] = ft
        GLabel_738["fg"] = "#333333"
        GLabel_738["justify"] = "center"
        GLabel_738["text"] = "AI"
        GLabel_738.place(x=0, y=150, width=70, height=25)

        GLabel_538 = tk.Label(self.root)
        ft = tkFont.Font(family='Times', size=10)
        GLabel_538["font"] = ft
        GLabel_538["fg"] = "#333333"
        GLabel_538["justify"] = "center"
        GLabel_538["text"] = "AO"
        GLabel_538.place(x=20, y=230, width=32, height=30)

        self.entry_show_ai = tk.Entry(self.root)
        self.entry_show_ai["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times', size=10)
        self.entry_show_ai["font"] = ft
        self.entry_show_ai["fg"] = "#333333"
        self.entry_show_ai["justify"] = "center"
        self.entry_show_ai["text"] = "Entry"
        self.entry_show_ai.place(x=80, y=150, width=313, height=30)

        self.entry_show_ao = tk.Entry(self.root)
        self.entry_show_ao["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times', size=10)
        self.entry_show_ao["font"] = ft
        self.entry_show_ao["fg"] = "#333333"
        self.entry_show_ao["justify"] = "center"
        self.entry_show_ao["text"] = "Entry"
        self.entry_show_ao.place(x=80, y=230, width=314, height=30)

        GLabel_203 = tk.Label(self.root)
        ft = tkFont.Font(family='Times', size=10)
        GLabel_203["font"] = ft
        GLabel_203["fg"] = "#333333"
        GLabel_203["justify"] = "center"
        GLabel_203["text"] = "通讯周期"
        GLabel_203.place(x=180, y=50, width=70, height=25)

        self.entry_show_connect_time = tk.Entry(self.root)
        self.entry_show_connect_time["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times', size=10)
        self.entry_show_connect_time["font"] = ft
        self.entry_show_connect_time["fg"] = "#333333"
        self.entry_show_connect_time["justify"] = "center"
        self.entry_show_connect_time["text"] = "Entry"
        self.entry_show_connect_time.place(x=250, y=50, width=79, height=30)

        self.entry_show_counter_left = tk.Entry(root)
        self.entry_show_counter_left["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times', size=10)
        self.entry_show_counter_left["font"] = ft
        self.entry_show_counter_left["fg"] = "#333333"
        self.entry_show_counter_left["justify"] = "center"
        self.entry_show_counter_left["text"] = "Entry"
        self.entry_show_counter_left.place(x=80, y=270, width=70, height=25)

        self.entry_show_counter_right = tk.Entry(root)
        self.entry_show_counter_right["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times', size=10)
        self.entry_show_counter_right["font"] = ft
        self.entry_show_counter_right["fg"] = "#333333"
        self.entry_show_counter_right["justify"] = "center"
        self.entry_show_counter_right["text"] = "Entry"
        self.entry_show_counter_right.place(x=200, y=270, width=70, height=25)

        self.entry_show_counter_front = tk.Entry(root)
        self.entry_show_counter_front["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times', size=10)
        self.entry_show_counter_front["font"] = ft
        self.entry_show_counter_front["fg"] = "#333333"
        self.entry_show_counter_front["justify"] = "center"
        self.entry_show_counter_front["text"] = "Entry"
        self.entry_show_counter_front.place(x=320, y=270, width=70, height=25)

        GLabel_281 = tk.Label(root)
        ft = tkFont.Font(family='Times', size=10)
        GLabel_281["font"] = ft
        GLabel_281["fg"] = "#333333"
        GLabel_281["justify"] = "center"
        GLabel_281["text"] = "左"
        GLabel_281.place(x=0, y=270, width=70, height=25)

        GLabel_761 = tk.Label(root)
        ft = tkFont.Font(family='Times', size=10)
        GLabel_761["font"] = ft
        GLabel_761["fg"] = "#333333"
        GLabel_761["justify"] = "center"
        GLabel_761["text"] = "右"
        GLabel_761.place(x=140, y=270, width=70, height=25)

        GLabel_633 = tk.Label(root)
        ft = tkFont.Font(family='Times', size=10)
        GLabel_633["font"] = ft
        GLabel_633["fg"] = "#333333"
        GLabel_633["justify"] = "center"
        GLabel_633["text"] = "前"
        GLabel_633.place(x=260, y=270, width=70, height=25)

    def button_connect_factory_command(self):
        print("command")

    def button_start_control_command(self):
        print("command")


if __name__ == "__main__":
    root = tk.Tk()
    app = UiSetup(root, 'MY WINDOWS')
    root.mainloop()

三、 控制逻辑
实现的代码还是比较混乱,但是只是作为练习使用,有空再细调一下,目前可实现基本的控制功能,存在的问题有:
通讯过程没有进行单独的类实现,所以代码有些繁琐;
定时器类没有实现,逻辑暂时没有想好;
传送带类仅实现了控制、计数功能,保护,超时停机没有实现;
报警、急停点暂时未考虑。
代码如下,实现逻辑比较简单:

# _*_ coding:utf-8 _*_
""" --------------------------------------------------
文件名称: PythonWithFactoryIO-qqq-qsp34
日期时间: 2020-08-15-13:17
文件备注: Python做ModbusTCP Client连接FactoryIO
---------------------------------------------------"""
import modbus_tk.modbus_tcp as mt
import modbus_tk.defines as md
import tkinter as tk
import ui_mian
import time


class PyController(ui_mian.UiSetup):
    """--"""
    def __init__(self, root):
        super().__init__(root)
        self.left_conveyor = ConveyorControl()
        self.right_conveyor = ConveyorControl()
        self.front_conveyor = ConveyorControl()

        self.system_run = int(0)
        self.system_stop = int(1)
        self.InputBool = [0 for i in range(14)]
        self.InputBoolPreCycle = [0 for i in range(14)]
        self.InputBoolTRing = [0 for i in range(14)]
        self.InputRegister = int(0)
        self.OutputBool = [0 for i in range(12)]
        self.HoldingRegister = [0 for i in range(4)]
        self.bili = 0
        self.bala = 0
        self.weight = 0
        self.left_counter = 0
        self.right_counter = 0
        self.front_counter = 0
        
        self.cycle_time = 50
        self.master = mt.TcpMaster(host="192.168.29.50")  # host="192.168.29.50", port=502, timeout_in_sec=5.0
        self.master.set_timeout(5.0)

        self.entry_var_show_di_value = tk.StringVar()
        self.entry_var_show_do_value = tk.StringVar()
        self.entry_var_show_ai_value = tk.StringVar()
        self.entry_var_show_ao_value = tk.StringVar()
        self.entry_var_show_cycle_time = tk.StringVar()
        self.entry_var_show_connect_time = tk.StringVar()
        self.entry_var_show_counter_left = tk.StringVar()
        self.entry_var_show_counter_right = tk.StringVar()
        self.entry_var_show_counter_front = tk.StringVar()

        self.entry_var_show_di_value.set(self.InputBool)
        self.entry_var_show_do_value.set(self.OutputBool)
        self.entry_var_show_ai_value.set(self.InputRegister)
        self.entry_var_show_ao_value.set(self.HoldingRegister)
        self.entry_var_show_cycle_time.set(50)
        self.entry_var_show_connect_time.set(0)
        
        self.entry_show_di['textvariable'] = self.entry_var_show_di_value
        self.entry_show_do['textvariable'] = self.entry_var_show_do_value
        self.entry_show_ai['textvariable'] = self.entry_var_show_ai_value
        self.entry_show_ao['textvariable'] = self.entry_var_show_ao_value
        self.entry_show_cycle_time['textvariable'] = self.entry_var_show_cycle_time
        self.entry_show_connect_time['textvariable'] = self.entry_var_show_connect_time
        self.entry_show_counter_left['textvariable'] = self.entry_var_show_counter_left
        self.entry_show_counter_right['textvariable'] = self.entry_var_show_counter_right
        self.entry_show_counter_front['textvariable'] = self.entry_var_show_counter_front

        self.button_connect_factory['command'] = self.start_connect
        self.button_start_control['command'] = self.start_control

    def read_data(self):
        """--"""
        """master.execute(slave=255, function_code=md.READ_HOLDING_REGISTERS, starting_address=1, quantity_of_x=1,
                                output_value=1)"""
        # 读输入线圈
        self.InputBool = self.master.execute(255, md.READ_DISCRETE_INPUTS, 0, 14)
        self.entry_var_show_di_value.set(self.InputBool)
        # 读输入寄存器
        self.InputRegister = self.master.execute(255, md.READ_INPUT_REGISTERS, 0, 1, output_value=1)
        self.entry_var_show_ai_value.set(self.InputRegister)
        
        self.root.update()

    def write_data(self):
        """--"""    
        # 写多个线圈
        output_coils_bool = self.master.execute(255, md.WRITE_MULTIPLE_COILS, 0, output_value=self.OutputBool)
        output_holding_register = self.master.execute(255, md.WRITE_MULTIPLE_REGISTERS, 10, output_value=self.HoldingRegister)
        self.root.update()

        # 延时
        delay_time_str = self.entry_var_show_cycle_time.get()
        delay_time_int = int(10 if delay_time_str == '' else delay_time_str)
        self.root.after(delay_time_int)

    def control(self):
        """--"""
        # 更新变量
        at_scale_entry = self.InputBool[0]
        at_scale = self.InputBool[1]
        at_scale_exit = self.InputBool[2]
        at_left_entry = self.InputBool[3]
        at_exit_left = self.InputBool[4]
        at_forward_entry = self.InputBool[5]
        at_exit_front = self.InputBool[6]
        at_right_entry = self.InputBool[7]
        at_exit_right = self.InputBool[8]
        start = self.InputBool[9]
        reset = self.InputBool[10]
        stop = self.InputBool[11]
        auto = self.InputBool[12]
        factory_io_running = self.InputBool[13]

        # 获取脉冲并以脉冲进行计数
        for i in range(len(self.InputBool)):
            self.InputBoolTRing[i] = self.InputBool[i] and (not self.InputBoolPreCycle[i])
        self.InputBoolPreCycle = self.InputBool
        at_forward_entry = self.InputBoolTRing[5]
        at_left_entry = self.InputBoolTRing[3]
        at_right_entry = self.InputBoolTRing[7]
        self.left_counter += at_left_entry
        self.right_counter += at_right_entry
        self.front_counter += at_forward_entry

        # 判断左、右、前行
        if at_scale:
            self.weight = int(self.InputRegister[0])
            # print(weight)
    	# 起保停
        self.system_run = (start or self.system_run) and stop and self.system_stop
    
        entry_conveyor = self.system_run
        start_light = self.system_run
        weight_conveyor = self.system_run
        send_forward = self.system_run
    
        if 700 < self.weight:
            send_left = 1
            send_right = 0
            send_front = 0
        elif 350 <= self.weight < 700:
            send_left = 0
            send_right = 1
            send_front = 0
        else:
            send_left = 0
            send_right = 0
            send_front = 1

        # 传送带控制 计数大于1运行,等于0停止
        self.left_conveyor.enter_signal = send_left and at_scale_exit   # at_left_entry
        self.left_conveyor.out_signal = at_exit_left
        left_conveyor = self.left_conveyor.control()
        self.entry_var_show_counter_left.set(self.left_conveyor.counter_on_conveyor)

        self.right_conveyor.enter_signal = send_right and at_scale_exit  # at_right_entry
        self.right_conveyor.out_signal = at_exit_right
        right_conveyor = self.right_conveyor.control()
        self.entry_var_show_counter_right.set(self.right_conveyor.counter_on_conveyor)

        self.front_conveyor.enter_signal = send_front and at_scale_exit  # at_forward_entry
        self.front_conveyor.out_signal = at_exit_front
        front_conveyor = self.front_conveyor.control()
        self.entry_var_show_counter_front.set(self.front_conveyor.counter_on_conveyor)

        # 更新输出
        self.OutputBool[0] = entry_conveyor
        # OutputBool[1] = load_scale
        self.OutputBool[2] = send_left
        self.OutputBool[3] = left_conveyor
        self.OutputBool[4] = send_right
        self.OutputBool[5] = right_conveyor
        self.OutputBool[6] = send_forward
        self.OutputBool[7] = front_conveyor
        self.OutputBool[8] = start_light
        # OutputBool[9] = reset_light
        # OutputBool[10] = stop_light
        self.OutputBool[11] = weight_conveyor
        self.entry_var_show_do_value.set(self.OutputBool)
        self.HoldingRegister[0] = self.left_counter
        self.HoldingRegister[1] = self.front_counter
        self.HoldingRegister[2] = self.right_counter
        self.HoldingRegister[3] = self.weight
        self.entry_var_show_ao_value.set(self.HoldingRegister)
        root.update()

    def start_connect(self):
        """--"""
        self.bili = ~self.bili
        self.button_connect_factory['text'] = "断开" if self.bili else '连接'
        while self.bili:
            start_time = int(time.perf_counter()*1000)
            self.read_data()
            if self.bala:
                self.control()
                self.write_data()
            end_time = int(time.perf_counter()*1000)
            self.entry_var_show_connect_time.set(end_time-start_time)

    def start_control(self):
        """--"""
        self.bala = ~self.bala
        self.button_start_control['text'] = "停止" if self.bala else '启动'


class ConveyorControl:
    """--"""
    def __init__(self):
        self.run_control = 0
        self.counter_on_conveyor = 0
        self.counter_enter = 1
        self.counter_out = 0
        self.enter_signal = 0
        self.enter_signal_pre = 0
        self.enter_signal_tr = 0
        self.out_signal = 0
        self.out_signal_pre = 0
        self.out_signal_tr = 0

    def control(self):
        """--"""
        self.counter()
        self.run_control = 1 if self.counter_on_conveyor > 0 else 0
        return self.run_control

    def counter(self):
        """--"""
        self.enter_signal_tr = self.enter_signal and (not self.enter_signal_pre)
        if self.out_signal == 1 and self.out_signal_pre == 0:
            self.out_signal_tr = 1
        else:
            self.out_signal_tr = 0
        self.out_signal_tr = self.out_signal and (not self.out_signal_pre)
        self.counter_enter += self.enter_signal_tr
        self.counter_out += self.out_signal_tr
        self.enter_signal_pre = self.enter_signal
        self.out_signal_pre = self.out_signal
        self.counter_on_conveyor = self.counter_enter-self.counter_out


class Timer:
    """--"""
    def __init__(self, cycle, my_time):
        self.TimerOn = 0
        self.time = my_time
        self.count = 0

    def timing(self):
        """--"""
        if self.count < self.time:
            self.count += self.time
            self.TimerOn = 0
        else:
            self.TimerOn = 1

    def reset_time(self):
        """--"""
        self.count = 0
        self.TimerOn = 0


if __name__ == "__main__":
    root = tk.Tk()
    app = PyController(root)
    root.mainloop()

在这里插入图片描述

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值