『7x24小时有问必答』
 
    本项目实战通过常见的打螺丝机器来跟大家分享怎么从零开始做一个项目,本项目会基于主框架不变,大家易理解的原则来呈现内容。
    基于三菱、威纶通触摸屏、基恩士扫码枪与C上位机,实现自动化打螺丝追溯系统,记录每台手机螺丝的装配时间和装配结果数据。
整个项目分5章展开,今天来讲第(二)节:
(一)电气前期设计:方案、需求、I/O表、选型、PLC & 上位机交互协议
(二)上位机设计:开发C WinForms 软件,实现扫码枪 SN 码读取、PLC 状态交互及生产数据存储查询;
(三)PLC 程序设计:使用GX Works2编写梯形图;
(四)触摸屏设计:使用EasyBuilder Pro编写触摸屏界面;
(五)调试和总结:上位机连接实物PLC,连接虚拟扫码枪(可用
串口模拟)进行我们的动作调试。

一、界面设计
    初学我们先不要求界面美化,能看得过去就行,主要把功能实现。界面美化有很多UI库可以实现。
正常我们应该如下分层去做:
├─ 1. 界面层(UI):用户交互(窗体、控件)
├─ 2. 业务逻辑层(BLL):核心流程控制(调度设备、处理数据)
├─ 3. 设备通讯层(Device):硬件交互(PLC、扫码枪)
└─ 4. 工具层(Utils):通用功能(日志、常量、辅助方法)
但是我们入门这样会太慢,复杂,我们这里从最简单的做起,先搞懂,上手一个小小项目,然后再慢慢标准化。

1,我们按照步骤直接如下图画出各个文本框、按钮、列表等。
如下图,我们通过之前介绍的一篇文章:
里面有详细的软件下载、怎么画界面的介绍,这里就不重复介绍基础功能,忘记了的朋友可以回去看一下。

2,命名ID:其中有一些需要修改Name,有一些不需要修改Name。
Lable:全部不用修改Name,如下图一个个从工具箱拖过来修改"Text"属性即可。
GroupBox(状态、通讯参数):不用修改Name,先拖放GroupBox,再把对应控件拖进去即可。
单行TextBox(Lable文字后的TextBox属性Name,以下不重复介绍):
手机SN:textBoxSN
螺丝1状态:textBoxOne
螺丝2状态:textBoxTwo
螺丝3状态:textBoxThree
螺丝4状态:textBoxFour
总产量:textBoxAll
Button:
PLC-连接:buttonPLC
扫码枪-连接:buttonScanner
产量清零:buttonClear
ComboBox:
PLC:comboBoxPLC
扫码枪:comboBoxScanner
ListView:

数据列表:listViewData
多行TextBox(TextBox选中后在弹出框勾选MultiLIne可切换为多行):
日志:textBoxLog

至此UI设计完成,下面进行代码的编写。
二、代码编写
1,先初始化ComboBox和ListView。
在窗体空白处双击,会进入private void Form1_Load(object sender, EventArgs e)方法。
运行起来之后:
源代码:
2,编写串口通讯代码
(1),PLC通讯:使用上一次我们写的通讯库,实际项目可查找第三方或自行开发库,此处我们为了介绍详细库如何通讯,使用自行开发的库讲解,会在下面这个库上进行完善加入读写M。

三菱FX系列PLC通讯库代码请通过以下文章查看(直接复制粘贴即可用)
(2),基恩士扫码枪通讯库(直接复制粘贴即可用)
扫码枪常用总共两个指令:
开启扫描命令:   "LON\r\n";
关闭扫描命令:  "LOFF\r\n"; 
源代码请通过以下文章查看(直接复制粘贴即可用)
(3),调用库函数初始化串口。
PLC:
扫码枪:
(4),实现与PLC交互逻辑功能
(1),写个定时器,循环读取PLCM100~M106的状态,当它们发生变化时,添加日志,执行对应动作。
从工具栏拖一个timer到Form1界面,如下:
设置其属性:Enabled=True; Interval=500;
双击timer1图标进入响应函数:
具体逻辑实现:
private void timer1_Tick(object sender, EventArgs e)
{
    if (fxPLC != null)
    {
        string binaryStr;
        fxPLC.ReadM(out binaryStr);

        List<string> changeMessages = new List<string>();

        // 解析当前位状态(从左到右为第1到第7位)
        bool[] currentBitStates = new bool[7];
        for (int i = 0; i < 7; i++)
        {
            currentBitStates = binaryStr == '1'// '1'对应true,'0'对应false
        }

        // 对比当前状态与上一次状态,检测"0→1"的变化
        for (int i = 0; i < 7; i++)
        {
            int bitPosition = i + 1// 位编号(1-7)
            bool previous = previousBitStates;
            bool current = currentBitStates;
            previousBitStates = currentBitStates;
            columnNames[0] = (listViewData.Items.Count + 1).ToString();//序号,当前数量+1
            // 仅当从0(false)变为1(true)时提示
            if (!previous && current)
            {
                switch (i)
                {
                    case 0://M100-启动
                        columnNames[1] = DateTime.Now.ToString();
                        textBoxOne.Text = "";
                        textBoxTwo.Text = "";
                        textBoxThree.Text = "";
                        textBoxFour.Text = "";
                        textBoxLog.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "接收到启动信号\r\n";
                        break;
                    case 1//M101-扫码
                        keyenceScan.OpenScan();
                        if (keyenceScan.ReadExisting() == 0)
                        {
                            //扫码成功
                            fxPLC.WriteM("M0120"true);
                            columnNames[2] = keyenceScan.BufferString;
                        }
                        else
                        {
                            //扫码失败
                            fxPLC.WriteM("M0121"true);
                            columnNames[2] = "扫码失败";
                        }
                        textBoxSN.Text = columnNames[2];
                        textBoxLog.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "接收到启动信号,扫码结果:" + columnNames[2] + "\r\n";
                        break;
                    case 2//M102-第1个螺丝
                        columnNames[3] = DateTime.Now.ToString();
                        textBoxOne.Text = "OK";
                        textBoxLog.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "打第1个螺丝\r\n";
                        break;
                    case 3://M103-第2个螺丝
                        columnNames[4] = DateTime.Now.ToString();
                        textBoxTwo.Text = "OK";
                        textBoxLog.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "打第2个螺丝\r\n";
                        break;
                    case 4://M104-第3个螺丝
                        columnNames[5] = DateTime.Now.ToString();
                        textBoxThree.Text = "OK";
                        textBoxLog.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "打第3个螺丝\r\n";
                        break;
                    case 5://M105-第4个螺丝
                        columnNames[6] = DateTime.Now.ToString();
                        textBoxLog.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "打第4个螺丝\r\n";
                        textBoxFour.Text = "OK";
                        break;
                    case 6://M106-结束
                        columnNames[7] = DateTime.Now.ToString();

                        // 1. 创建一行数据(对应列头顺序)
                        ListViewItem item = new ListViewItem();
                        item.Text = columnNames[0]; 
                        item.SubItems.Add(columnNames[1]); 
                        item.SubItems.Add(columnNames[2]);
                        item.SubItems.Add(columnNames[3]); 
                        item.SubItems.Add(columnNames[4]);
                        item.SubItems.Add(columnNames[5]);
                        item.SubItems.Add(columnNames[6]);
                        item.SubItems.Add(columnNames[7]);
                        // 2. 把行添加到ListView
                        listViewData.Items.Add(item);

                        // 3. 自动滚动到最新行(可选)
                        listViewData.EnsureVisible(listViewData.Items.Count - 1);
                        textBoxLog.Text += DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "完成\r\n";
                        break;
                }
            }
        }

    }
}
当前窗口全局变量:
三、初步模拟调试:
实现了以下功能:
1,PLC串口连接,并实时扫描M100~M106的状态,判断其上升沿。
2,扫码枪串口连接。
3,状态显示、数据列表显示、日志显示。

    因为主要目的是分享给刚学习的朋友,代码没有太多的框架化编写,这样大家更加容易理解。
今天我们的分享上位机编程到此结束。
下一节我们就先开始编写PLC程序。大家如果有什么疑问或需要交流的欢迎留言讨论,喜欢的点个赞或收藏起来吧。
以下我的其他技术文章看可不可以给大家提供帮助:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

上一主题上一主题         下一主题下一主题
QQ手机版小黑屋粤ICP备17165530号

关于我们·投诉举报· 用户帮助· 联系我们 · 本站服务 · 版权声明· 隐私政策 · 投搞指南

法律保护:PLC技术网,plcjs.com,plcjs.net等字样
Copyright 2010-2030. All rights reserved. 


微信公众号二维码 抖音二维码 百家号二维码 今日头条二维码哔哩哔哩二维码