工业4.0 智能工厂简单学习项目

随着社会的发展,工厂智能化的步骤越来越快,信息和数据越来越重要,MES,SCADA等智能化系统逐渐成为工厂管理的标配,于是数据采集与维护也逐渐成为了工厂工作的重点,本人把工作中学习的项目分享给大家,希望能抛砖引玉,得到大家的指点和帮助!
项目预览与概述:

预览

电脑测试预览

手机测试预览

设计思路

概述
项目分为3个部分,硬件基础(数据收集),数据采集上传,信息展示(微信小程序)
一.硬件基础 西门子S71500 PLC 
1.设备选型,组态
2.数据块结构设计
3.OPC UA服务开启
4.仿真环境搭建
二.数据采集上传 OPC客户端
1.OPC UA 通信连接
2.数据采集(采用订阅方式)
3.数据处理或上传微信云数据库
三.信息展示(微信小程序)
1.选择云开发,云模板
2.修改界面
2.修改云函数和数据展示逻辑

 项目实施:
一.硬件基础 西门子S71500 PLC 
1.设备选型,组态


2.数据块结构设计

为了方便数据数据采集,数据块一定要设计合理,尽量模块化


3.OPC UA服务开启

西门子1500PLC配置OPC相当简单,选择好授权激活OPC服务器功能即可,具体几个容易出错的小细节见图片

尽量启用访客认证,这样方便测试


4.仿真环境搭建

我为了方便测试,电脑上安装了VM虚拟机,虚拟机里安装博途V15.1和模拟器S7-PLCSIM Advanced V3.0,如果大家也和我一样那么要注意IP设置,否则OPC客户端在本机无法采集数据,详细见下图

 仿真时一定要选上支持仿真的功能,许可证类型要选上

 二.数据采集上传 OPC客户端
1.OPC UA 通信连接

1.引用OPC官方的Opc.Ua.Client.dll,Opc.Ua.Core.dll

2.引用开源的OPCHelper(编译后引用)

网上搜索的帮助类,自己修改了一下,具体代码如图

OPC UA详细使用方法见下面这位大牛的文章

牛奶咖啡13

C#使用OpcUaHelper开源库开发客户端实现读取、订阅OPC UA服务器节点信息_牛奶咖啡13的博客-CSDN博客_c# opc ua客户端

//声明对象
private OpcHleperTest opctest;
//实例化对象
opctest = new OpcHleperTest();

//连接

try
            {
                //判断连接状态后连接
                if (!opctest.ConnectStatus)
            {
//注意连接地址
                opctest.OpenConnectOfAnonymous("opc.tcp://192.168.40.8:4840");
            }
            //判断连接状态后按钮改变颜色
            if (opctest.ConnectStatus)
            {
                    button1.BackColor = Color.YellowGreen;
                    button1.Text = "已连接";
                    label9.BackColor = Color.Yellow;
            }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

2.数据采集(采用订阅方式),数据处理

订阅

//定义需要订阅的数据
            string str1 = "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"speed\"";
            string str2 = "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"state\"";
            string str3 = "ns=3;s=\"opctest\".\"station1\".\"operator\".\"name\"";
            string str4 = "ns=3;s=\"opctest\".\"station1\".\"operator\".\"state\"";
//加入数组
            string[] nolds = new string[] { str1, str2 , str3 , str4 };
//批量订阅
            opctest.BatchNodeIdDatasSubscription("monitor", nolds, SubCallback);


3.数据处理或上传微信云数据库

//数据订阅的回调函数
        private void SubCallback(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>(SubCallback), key, monitoredItem, args);
                return;
            }

            // 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
            MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
            if (notification != null)
            {
                //运行速度
                if(monitoredItem.StartNodeId.ToString()== "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"speed\"")
                {
//取出订阅返回的值
                    textBox2.Text = notification.Value.WrappedValue.Value.ToString();
//数据上传到微信云数据库
                    UpData("Speed_Station1", textBox2.Text);
                }
                //设备状态
                if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"state\"")
                {
//判断取回的值
                    if(notification.Value.WrappedValue.Value.ToString()=="0")
                    {
//改变标签的颜色
                        label6.BackColor = Color.Yellow;
                        label5.BackColor = Control.DefaultBackColor;
                        label7.BackColor = Control.DefaultBackColor;
//数据上传到微信云数据库
                        UpData("State_Station1", "0");
                    }
                    if (notification.Value.WrappedValue.Value.ToString() == "1")
                    {
                        label5.BackColor = Color.YellowGreen;
                        label6.BackColor = Control.DefaultBackColor;
                        label7.BackColor = Control.DefaultBackColor;
                        UpData("State_Station1", "1");
                    }
                    if (notification.Value.WrappedValue.Value.ToString() == "3")
                    {
                        label7.BackColor = Color.Red;
                        label5.BackColor = Control.DefaultBackColor;
                        label6.BackColor = Control.DefaultBackColor;
                        UpData("State_Station1", "3");
                    }
                }
                //工号
                if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"operator\".\"name\"")
                {
                    textBox1.Text = notification.Value.WrappedValue.Value.ToString();
                    UpData("Code_OP", textBox1.Text);
                }
                //人员状态
                if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"operator\".\"state\"")
                {
                    if (notification.Value.WrappedValue.Value.ToString()== "True")
                    {
                        label3.BackColor = Color.YellowGreen;
                        label8.BackColor = Control.DefaultBackColor;
                        UpData("State_OP", "true");
                    }
                    if (notification.Value.WrappedValue.Value.ToString() == "False")
                    {
                        label8.BackColor = Color.Red;
                        label3.BackColor = Control.DefaultBackColor;
                        UpData("State_OP", "false");
                    }
                }
            }

        }

3.微信云数据库修改

获取修改数据库的官方获取测试地址

微信公众平台接口调试工具

// 通过Get请求获取access_token
            HttpWebResponse httpWebResponse = GetRequest("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="你的appid"&secret="你的secret"");
            string resultJson = HttpWebResponseToString(httpWebResponse);
            var resultObj = DesAnonymousType(resultJson, new { access_token = "", expires_in = "" });
            return resultObj.access_token;

 修改微信云数据库

​
​
 string accessToken = GetAccessToken();
            string accessTokenqueryString = $"{{\"env\":\"你的环境名称\", \"query\": \"db.collection(\\\"你的数据库名\\\").doc(\\\"你的数据库ID\\\")" + ".update({data:" + "{" + name + ":" + value + "}})\"}";
            HttpWebResponse httpWebResponse = PostRequest("https://api.weixin.qq.com/tcb/databaseupdate?access_token=" + accessToken, accessTokenqueryString);
            string data = HttpWebResponseToString(httpWebResponse);
            return data;

​

​

全部代码

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SimChart
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            //实例化对象
            opctest = new OpcHleperTest();
        }
        //声明对象
        private OpcHleperTest opctest;
        private void Button1_Click(object sender, EventArgs e)
        {
            //云数据库的accessToken
            accessToken = GetAccessToken();
            try
            {
                //判断连接状态后连接
                if (!opctest.ConnectStatus)
            {
                opctest.OpenConnectOfAnonymous("opc.tcp://192.168.40.8:4840");
            }
            //判断连接状态后按钮改变颜色
            if (opctest.ConnectStatus)
            {
                    button1.BackColor = Color.YellowGreen;
                    button1.Text = "已连接";
                    label9.BackColor = Color.Yellow;
            }
            string str1 = "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"speed\"";
            string str2 = "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"state\"";
            string str3 = "ns=3;s=\"opctest\".\"station1\".\"operator\".\"name\"";
            string str4 = "ns=3;s=\"opctest\".\"station1\".\"operator\".\"state\"";
            string[] nolds = new string[] { str1, str2 , str3 , str4 };
            opctest.BatchNodeIdDatasSubscription("monitor", nolds, SubCallback);
            //opctest.SingleNodeIdDatasSubscription("opctest888", str1, SubCallback2);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        //单节点数据订阅的回调函数
        private void SubCallback2(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>(SubCallback2), key, monitoredItem, args);
                return;
            }

            // 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
            MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
            if (notification != null)
            {
                textBox1.Text = notification.Value.WrappedValue.Value.ToString();
            }

        }
        //数据订阅的回调函数
        private void SubCallback(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>(SubCallback), key, monitoredItem, args);
                return;
            }

            // 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
            MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
            if (notification != null)
            {
                //运行速度
                if(monitoredItem.StartNodeId.ToString()== "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"speed\"")
                {
                    textBox2.Text = notification.Value.WrappedValue.Value.ToString();
                    UpData("Speed_Station1", textBox2.Text);
                }
                //设备状态
                if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"state\"")
                {
                    if(notification.Value.WrappedValue.Value.ToString()=="0")
                    {
                        label6.BackColor = Color.Yellow;
                        label5.BackColor = Control.DefaultBackColor;
                        label7.BackColor = Control.DefaultBackColor;
                        UpData("State_Station1", "0");
                    }
                    if (notification.Value.WrappedValue.Value.ToString() == "1")
                    {
                        label5.BackColor = Color.YellowGreen;
                        label6.BackColor = Control.DefaultBackColor;
                        label7.BackColor = Control.DefaultBackColor;
                        UpData("State_Station1", "1");
                    }
                    if (notification.Value.WrappedValue.Value.ToString() == "3")
                    {
                        label7.BackColor = Color.Red;
                        label5.BackColor = Control.DefaultBackColor;
                        label6.BackColor = Control.DefaultBackColor;
                        UpData("State_Station1", "3");
                    }
                }
                //工号
                if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"operator\".\"name\"")
                {
                    textBox1.Text = notification.Value.WrappedValue.Value.ToString();
                    UpData("Code_OP", textBox1.Text);
                }
                //人员状态
                if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"operator\".\"state\"")
                {
                    if (notification.Value.WrappedValue.Value.ToString()== "True")
                    {
                        label3.BackColor = Color.YellowGreen;
                        label8.BackColor = Control.DefaultBackColor;
                        UpData("State_OP", "true");
                    }
                    if (notification.Value.WrappedValue.Value.ToString() == "False")
                    {
                        label8.BackColor = Color.Red;
                        label3.BackColor = Control.DefaultBackColor;
                        UpData("State_OP", "false");
                    }
                }
            }

        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            //关闭连接
            opctest.CloseConnect();
        }
        
        ///云数据库相关
        
        ///
        /// <summary>
        /// 发送http Get请求
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static HttpWebResponse GetRequest(string url)
        {
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            request.Method = "GET";
            request.ContentType = "application/x-www-form-urlencoded";//链接类型
            return request.GetResponse() as HttpWebResponse;
        }

        /// <summary>
        /// 发送http Post请求
        /// </summary>
        /// <returns></returns>
        public static HttpWebResponse PostRequest(string url, string messsage)
        {
            byte[] byteData = Encoding.UTF8.GetBytes(messsage);
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
            webRequest.Method = "POST";
            webRequest.ContentType = "application/json;charset=UTF-8";
            webRequest.ContentLength = byteData.Length;
            using (Stream stream = webRequest.GetRequestStream())
            {
                stream.Write(byteData, 0, byteData.Length);
            }
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
            return response;
        }

        /// <summary>
        /// 从HttpWebResponse对象中提取响应的数据转换为字符串
        /// </summary>
        /// <param name="webresponse"></param>
        /// <returns></returns>
        public static string HttpWebResponseToString(HttpWebResponse webresponse)
        {
            using (Stream s = webresponse.GetResponseStream())
            {
                StreamReader reader = new StreamReader(s, Encoding.UTF8);
                return reader.ReadToEnd();
            }
        }

        /// <summary>
        /// Json字符串转为匿名对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="json"></param>
        /// <param name="anonymousTypeObject"></param>
        /// <returns></returns>
        public static T DesAnonymousType<T>(string json, T anonymousTypeObject)
        {
            return JsonConvert.DeserializeAnonymousType(json, anonymousTypeObject);
        }
        /// <summary>
        /// 获取access_token
        /// </summary>
        /// <returns></returns>
        public static string GetAccessToken()
        {
            // 通过Get请求获取access_token
            HttpWebResponse httpWebResponse = GetRequest("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx017496cac8389210&secret=51c000bc2fa6d32fa8969e9cb8f71072");
            string resultJson = HttpWebResponseToString(httpWebResponse);
            var resultObj = DesAnonymousType(resultJson, new { access_token = "", expires_in = "" });
            return resultObj.access_token;
        }

        /// <summary>
        ///  查询数据
        /// </summary>
        /// <param name="queryString">形如: $"{{\"env\":\"小程序环境id\", \"query\": \"db.collecti(\\\"数据集合名称\\\").where({{集合中字段:\\\"集合中字段值\\\"}}).limit(10).get()\"}}"</param>
        /// <returns></returns>
        //public static string GetData(string queryString)
        public static string GetData()
        {
            string accessToken = GetAccessToken();
            string accessTokenqueryString = $"{{\"env\":\"zunzhi-5gthrfdrd0504463\", \"query\": \"db.collection(\\\"Test\\\").where({{_id:\\\"TestByClient\\\"}}).limit(10).get()\"}}";
            HttpWebResponse httpWebResponse = PostRequest("https://api.weixin.qq.com/tcb/databasequery?access_token=" + accessToken, accessTokenqueryString);
            string data = HttpWebResponseToString(httpWebResponse);
            return data;
        }
       static string accessToken = "";
        /// <summary>
        ///  更新数据
        /// </summary>
        /// <param name="queryString">形如: $"{{\"env\":\"小程序环境id\", \"query\": \"db.collecti(\\\"数据集合名称\\\").where({{集合中字段:\\\"集合中字段值\\\"}}).update({data:{集合中字段:\\\"集合中字段值\\\"})"</param>
        /// <returns></returns>
        //public static string GetData(string queryString)
        public static string UpData(string name, string value)
        {
            //string accessToken = GetAccessToken();
            string accessTokenqueryString = $"{{\"env\":\"zunzhi-5gthrfdrd0504463\", \"query\": \"db.collection(\\\"Test\\\").doc(\\\"TestByClient\\\")" + ".update({data:" + "{" + name + ":" + value + "}})\"}";
            HttpWebResponse httpWebResponse = PostRequest("https://api.weixin.qq.com/tcb/databaseupdate?access_token=" + accessToken, accessTokenqueryString);
            string data = HttpWebResponseToString(httpWebResponse);
            return data;
        }

        /// <summary>
        /// 通过FileId获取文件下载URL
        /// </summary>
        /// <param name="queryString">形如:$"{{\"env\": \"环境ID\",\"file_list\": [{{\"fileid\":文件ID\",\"max_age\":7200 }}]}}"</param>
        /// <returns></returns>
        public static string GetDownFileUrl(string queryString)
        {
            string accessToken = GetAccessToken();
            string url = $"https://api.weixin.qq.com/tcb/batchdownloadfile?access_token={accessToken}";
            HttpWebResponse httpWebResponse = PostRequest(url, queryString);
            string downFileUrl = HttpWebResponseToString(httpWebResponse);
            return downFileUrl;
        }
        //提取数据
        public static string GetValue(string JsonString, string Jsonvalue)
        {
            JObject JsonTemp = JObject.Parse(JsonString);
            string str1 = JsonTemp["data"].ToString().Replace("[", "").Replace("]", "").Trim();
            str1 = str1.Substring(1, str1.Length - 2).Replace(@"\", "");
            JObject JsonTemp2 = JObject.Parse(str1);
            string str2 = JsonTemp2[Jsonvalue].ToString();
            return str2;
        }
    }
}

 正常后返回的数据见下图

三.信息展示(微信小程序)

1.选择云开发,云模板

打开微信的官方开发软件,建立项目,选择云开发,选择官方的云模板

 2.简单修改一下界面

把模板中不需要的注释掉,加入自己的代码

<view class="title">XX车间状态</view>

  <view class="top_tip">努力工作!好好学习!</view>
  <view class="app">
    员工
    <view class="horizontal">
      <view style="color: lightsalmon;">员工工号</view>
      <view style="border-style:solid; border-color: darkorchid; height: 38px;width: 200px;color: olivedrab;text-align: center;">{{record[0]["Code_OP"]}}</view>
    </view>
    <view class="horizontal">
      <text wx:if="{{record[0]['State_OP']}}" style="border-style:solid;border-color: turquoise; background-color: rgb(30, 235, 23);">在岗</text>
      <text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">在岗</text>
      <text wx:if="{{!record[0]['State_OP']}}"style="border-style:solid;border-color: turquoise; background-color:red;">离岗</text>
      <text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">离岗</text>
    </view>
  </view>
  <view class="app">
    设备
    <view class="horizontal">
      <view style="color: lightsalmon;">运行速度</view>
      <view style="border-style:solid; border-color: darkorchid; height: 38px;width: 200px;color: olivedrab;text-align: center;">{{record[0]["Speed_Station1"]}}</view>
    </view>
    <view class="horizontal">
      <text wx:if="{{record[0]['State_Station1']==1}}" style="border-style:solid;border-color: turquoise; background-color: rgb(30, 235, 91);">运行</text>
      <text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">运行</text>
      <text wx:if="{{record[0]['State_Station1']==0}}" style="border-style:solid;border-color: turquoise; background-color: yellow;">停止</text>
      <text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">停止</text>
      <text wx:if="{{record[0]['State_Station1']==3}}" style="border-style:solid;border-color: turquoise; background-color:red;">故障</text>
      <text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">故障</text>
    </view>
  </view>
  <button style="border-style: solid; border-color: springgreen;background-color: palegreen;margin-top: 30px;" bindtap="getRecord">刷新</button>

 3..修改云函数和数据展示逻辑

添加云数据库信息

修改微信云函数,修改后一定上传部署

在页面的js文件中加入查询云数据库的方法

//查询数据
getRecord() {
  wx.showLoading({
    title: '',
  });
 wx.cloud.callFunction({
    name: 'quickstartFunctions',
    config: {
      env: this.data.envId
    },
    data: {
//注意这个命令,不要选错
      type: 'selectRecord'
    }
  }).then((resp) => {
    this.setData({
      haveGetRecord: true,
      record: resp.result.data
    });
   wx.hideLoading();
 }).catch((e) => {
    console.log(e);
    this.setData({
      showUploadTip: true
    });
   wx.hideLoading();
 });
},

 修改后保存编译,模拟器就会显示查询的结果,是不是很惊喜?整个项目比较简单,稍微有些编程知识即可入手

我在页面中加入了定时器可以实现数据实时更新,但很快就把我的数据库测试次数用完了,所以就注释掉了,大家打开注释用定时器刷新页面时一定要注意,每天的次数用完微信官方就不允许访问数据库了!页面写满了,无法再写了,代码见附件吧,我已分类打包,完整上传!最后谢谢大家的观看,大家互相交流,共同进步,谢谢!

项目资源下载

工业4.0智能工厂简单学习项目-C#文档类资源-CSDN下载

  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页
评论

打赏作者

尊治

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值