视频演示链接:https://www.bilibili.com/video/av97314651?pop_share=1
前提条件:
1、电脑需要与PLC能通讯上;
2、手机要与电脑能通讯上(比如手机和电脑都在同一个局域网下或同一个WiFi下)。
主要思路:
1、利用S7.net实现上位机对西门子PLC数据的读写功能;
2、利用Socket实现上位机服务器与手机客户端的通讯,将上位机读取的PLC数据发送给手机客户端,以及将手机写入的信号写入到PLC。

服务器窗体简单画面如下:

服务器端的完整代码如下:

using S7.Net;
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.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;namespace 读写库位信息
{public partial class Form1 : Form{public Form1(){InitializeComponent();Control.CheckForIllegalCrossThreadCalls = false;//取消跨线程的访问try{//创建一个负责监听IP地址跟端口号的socketSocket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ip = IPAddress.Any;IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(text_Port.Text));//创建端口号对象socketWatch.Bind(point);//绑定端口号,其中端口号包含IP地址信息ShowMsg(DateTime.Now.ToString() + "  " + "监听成功");AddLogText(DateTime.Now.ToString() + "  " + "监听成功");socketWatch.Listen(10);//设置监听队列,在同一时间点内所能连接的最多客户量,如果超过了10个,则排队等待Thread th = new Thread(Listen);//创建新线程运行Listen()函数th.IsBackground = true;//将线程设置为后台线程th.Start(socketWatch);//把socketWatch传到线程的Start()函数,标记该线程可以被CPU执行了}catch{ }}Socket socketsend;//创建跟客户端通讯的socketDictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();string ip;/// <summary>/// 等待客户端的连接,并创建一个与之通信用个Socket(通过循环等待客户端连接,否则线程会一直停在Accept()函数处等待)/// </summary>/// <param name="o"></param>void Listen(object o)//被线程所执行的函数,如果有参数,必须是object型{Socket socketWatch = o as Socket;//把Listen()函数的object型的参数强转为Socket型            try{while (true){socketsend = socketWatch.Accept();dicSocket.Add(socketsend.RemoteEndPoint.ToString(), socketsend);//获取远程客户端的IP地址和端口号connected_Client.Items.Add(socketsend.RemoteEndPoint.ToString());ShowMsg(DateTime.Now.ToString() + "\r\n" + socketsend.RemoteEndPoint.ToString() + " : " + "连接成功");AddLogText(DateTime.Now.ToString() + "  " + socketsend.RemoteEndPoint.ToString() + " : " + "连接成功" );byte[] buffer = System.Text.Encoding.UTF8.GetBytes(socketsend.RemoteEndPoint.ToString());ip = socketsend.RemoteEndPoint.ToString();                   Thread th = new Thread(Recieve);//开启一个新的线程不停地接收客户端发送过来的消息th.IsBackground = true;//将线程设置为后台线程,这样主线程关闭时,新线程也会关闭(前台线程:只有所有的前台线程都关闭才能完成程序关闭;后台线程:只要所有的前台程序结束,后台线程自动结束)th.Start(socketsend); //把socketsend传到线程的Start()函数,标记该线程可以被CPU执行了}}catch (Exception ex){MessageBox.Show(ex.Message.ToString()); //抛出异常信息}}/// <summary>/// 服务器端不停地接收客户端发送过来的消息/// </summary>/// <param name="o"></param>void Recieve(object o){Socket socketSend = o as Socket;while (true){try{byte[] buffer = new byte[1024 * 1024 * 2];//创建一个2M的字节数组型变量(1024byte=1kb,1024*1024byte=1024*1kb=1M)int r = socketSend.Receive(buffer);//将Receive()函数返回的int型的实际接收到的字节数赋值给rif (r == 0){break;//如果接收到的数据为空,则跳出循环}string str = Encoding.UTF8.GetString(buffer, 0, r);//将字节数组buffer[]中的0到r位解码为字符串,并赋值给str  string[] sArray = str.Split('_'); //将字符串str按“_”分割,并存到字符串数组sArray中。ShowMsg(DateTime.Now.ToString() + "\r\n" + socketSend.RemoteEndPoint + " : " + str);//调用ShowMsg()函数,将远程客户端的IP地址、端口号及接收到的信息显示到txtLog文本框AddLogText(DateTime.Now.ToString() + "  " + socketSend.RemoteEndPoint + " : " + str );string readOrWrite = sArray[0];//取字符串数组索引号为0的元素,判断是读还是写,0为读,1为写string storage = sArray[1];//取字符串数组索引号为1的元素,获取库位号int storageNumble = Convert.ToInt32(storage.Substring(2, storage.Length - 2));//截取库位号,并将库位号转换为整数型变量,存入storageNumble中int DBAddress = (storageNumble - 1) * 2;string[] signal1 = { "0", "0", "0", "0", "0", "0", "0", "0" };//定义用于存储读到的DB20中的数据的字符串数组string[] signal2 = { "0", "0", "0", "0" };//定义用于存储读到的DB21中的数据的字符串数组if (readOrWrite == "0")  //如果读到的数据为0,表示需要读取DB20和DB21中的数据{                                       for (int i = 0; i < 8; i++)//读DB20的数据,该数据为AGV发送给库位的信号{string readAddress1 = "DB20.DBX" + DBAddress.ToString() + "." + i.ToString();bool test1 = (bool)plc.Read(readAddress1);                           signal1[i] = test1.ToString();                                                                           }for (int i = 0; i < 4; i++)//读DB21的数据,该数据为AGV接收库位发送的信号{                            string readAddress2 = "DB21.DBX" + DBAddress.ToString() + "." + i.ToString();                            bool test2 = (bool)plc.Read(readAddress2);                            signal2[i] = test2.ToString();                            }string readResult = signal1[0];//新建字符串变量,用于存储读到的所有PLC信号for (int i = 1; i < 8; i++){                            readResult = readResult + "_" + signal1[i];//将在DB20数据块中读到的数据存放到readResult字符串中                            }for (int i = 0; i < 4; i++){                           readResult = readResult + "_" + signal2[i]; //将在DB21数据块中读到的数据存放到readResult字符串中}dicSocket[ip].Send(System.Text.Encoding.UTF8.GetBytes(readResult));ShowMsg(readResult);AddLogText(readResult);}if (readOrWrite == "1") //如果读到的数据为1,表示需要在DB20中写入数据{                        string[] writeData = sArray[2].Split('-');//取接收到的客户端发过送的字符串数组索引号为2的元素,获取写入的数据,并将写入按“-”分割后存入writeData中for (int i = 0; i < 8; i++)//读DB20的数据,该数据为AGV发送给库位的信号{                                                     string writeAddress = "DB20.DBX" + DBAddress.ToString() + "." + i.ToString();plc.Write(writeAddress, bool.Parse(writeData[i])); //将写入的数据转换为bool量,并写入到相应的DB块中。                                                                         }}}catch (Exception ex){MessageBox.Show(ex.Message.ToString()+"(请检查是否连接PLC!)"); //抛出异常信息}}}/// <summary>/// 将字符串追加到文本框txtLog中/// </summary>/// <param name="v">追加的字符串数据</param>private void ShowMsg(string v){txtLog.AppendText(v + "\r\n");}string strPath = System.Environment.CurrentDirectory + "/";//获取和设置当前目录(即该进程从中启动的目录)的完全限定路径。string fileName = DateTime.Now.ToLongDateString().ToString() + ".txt"; //获取系统日期,并将"日期.txt"赋值给文件名变量fileName /// <summary>/// 将客户端的操作数据追加到文本日志文件中/// </summary>/// <param name="a">客户端的操作数据字符串</param>private void AddLogText(string a){                        File.AppendAllText(strPath + fileName, a + "\r\n");  //将参数a的值追加到特定路径的fileName文本中,如果fileName不存在,则创建新文件后再将a追加到文本文件中。        }static string Plc_ip = "0.0.0.0";public Plc plc = new Plc(CpuType.S71200, Plc_ip, 0, 1);  //定义新的PLC对象  private void Btn_connect_Click(object sender, EventArgs e){try{Plc_ip = txt_IP.Text;//将输入的IP地址赋值给Plc_ipplc = new Plc(CpuType.S71200, Plc_ip, 0, 1);//重新实例化plc对象plc.Open();//连接PLCif (plc.IsConnected){MessageBox.Show("连接成功!", "连接提示", MessageBoxButtons.OK);}else{MessageBox.Show("连接失败!", "连接提示", MessageBoxButtons.OK);}}catch (Exception ex){MessageBox.Show(ex.Message.ToString() + "(请检查PLC的IP地址是否正确!)");}}private void Btn_disconnect_Click(object sender, EventArgs e){plc.Close();//断开PLC}private void Btn_Clear_Click(object sender, EventArgs e){txtLog.Text = null;//清空消息框}private void Btn_LogView_Click(object sender, EventArgs e){System.Diagnostics.Process.Start(strPath + fileName);//查看日志}}
}

安卓手机客户端画面如下:

安卓手机客户端是利用E4A中文安卓编程软件制作的APP,完整代码如下:
登录页面代码:

变量 账号 为 文本型
变量 密码 为 文本型     事件 主窗口.创建完毕()保存窗口("窗口1",创建 窗口1)
结束 事件事件 按钮登录.被单击()账号 = 编辑框账号.内容密码 = 密码编辑框1.内容如果 账号 = "admin" 且 密码 = "123" 则切换窗口(读取窗口("窗口1"))否则弹出提示("账号或密码错误,请重新输入!")结束 如果结束 事件事件 按钮取消.被单击()结束程序()
结束 事件

主页面代码

事件 窗口1.创建完毕()单选列表框1.背景颜色 = 白色   单选列表框1.清空项目()   变量 计次 为 整数型计次=1判断循环首 计次< 105单选列表框1.添加项目("库位" & 计次)单选列表框1.置项目状态(计次,假)      计次 = 计次 + 1判断循环尾结束 事件事件 按钮连接.被单击()客户1.连接服务器(IP.内容,到整数(PORT.内容),3000)  '连接服务器
结束 事件事件 按钮断开.被单击()客户1.断开连接()
结束 事件事件 客户1.连接断开()弹出提示("连接已断开!")
结束 事件变量 是否连接服务器 为 逻辑型
事件 客户1.连接完毕(连接结果 为 逻辑型)是否连接服务器 = 连接结果如果 连接结果 = 真 则弹出提示("连接成功!")      否则 信息框("连接提示","连接服务器失败!请检查IP地址或端口号是否正确!","确定")结束 如果
结束 事件变量 库位号 为 文本型= "0"
事件 单选列表框1.表项被单击(项目索引 为 整数型) 库位号=单选列表框1.取项目内容(项目索引)     客户1.发送数据(文本到字节(0 & "_" & 库位号,"UTF-8")) '将库位号发送给服务器
结束 事件事件 客户1.收到数据(数据 为 字节型())变量 接收到的数据 为 文本型变量 解析数据 为 文本型(12)  '定义一个字符串数组,存放接收并解析后的数据接收到的数据 =  字节到文本(数据,"UTF-8")  '收到服务器发来的字节集数据,转换成文本   解析数据 = 分割文本(接收到的数据 ,"_") 如果 解析数据(0)="True" 则 拉空料.选中 = 真否则    拉空料.选中 = 假结束 如果如果 解析数据(1)="True" 则 送满料.选中=真否则  送满料.选中=假结束 如果如果 解析数据(2)="True" 则 申请进入.选中=真 否则  申请进入.选中=假结束 如果如果 解析数据(3)="True" 则 进入中.选中=真否则   进入中.选中=假结束 如果如果 解析数据(4)="True" 则 到达.选中=真否则 到达.选中=假结束 如果如果 解析数据(5)="True" 则 申请离开.选中=真否则    申请离开.选中=假结束 如果如果 解析数据(6)="True" 则 离开中.选中=真否则   离开中.选中=假结束 如果如果 解析数据(7)="True" 则 离开成功.选中=真否则   离开成功.选中=假结束 如果如果 解析数据(8)="True" 则 拉空料1.选中=真否则  拉空料1.选中=假结束 如果如果 解析数据(9)="True" 则 送满料1.选中=真否则  送满料1.选中=假结束 如果如果 解析数据(10)="True" 则 允许进入.选中=真否则 允许进入.选中=假结束 如果如果 解析数据(11)="True" 则 允许离开.选中=真否则 允许离开.选中=假结束 如果结束 事件变量 写入数据 为 文本型
事件 按钮写入.被单击()变量 是否拉空料 为 文本型变量 是否送满料 为 文本型变量 是否申请进入 为 文本型变量 是否在进入中 为 文本型 变量 是否到达 为 文本型变量 是否申请离开 为 文本型变量 是否在离开中 为 文本型变量 是否离开成功 为 文本型如果 拉空料.选中=真 则 是否拉空料 = "True"  否则  是否拉空料 = "False"结束 如果如果 送满料.选中=真 则 是否送满料 = "True"否则   是否送满料 = "False"结束 如果如果 申请进入.选中=真 则 是否申请进入 = "True"否则 是否申请进入 = "False"结束 如果如果 进入中.选中=真 则 是否在进入中 = "True"否则 是否在进入中 = "False"结束 如果如果 到达.选中=真 则 是否到达 = "True"否则    是否到达 ="False"结束 如果如果 申请离开.选中=真 则 是否申请离开 = "True"否则   是否申请离开 = "False"结束 如果如果 离开中.选中=真 则 是否在离开中 = "True"否则 是否在离开中 = "False"结束 如果如果 离开成功.选中=真 则 是否离开成功 = "True"否则    是否离开成功 =  "False"结束 如果   变量 结果 为 整数型写入数据 = 是否拉空料 & "-" & 是否送满料  & "-" &   是否申请进入  & "-" &  是否在进入中   & "-" &   是否到达   & "-" &  是否申请离开   & "-" &   是否在离开中  & "-" &   是否离开成功  如果 是否连接服务器 = 假 则弹出提示("请连接服务器!")      否则 如果 库位号 = "0" 则 弹出提示("请选择库位号!")否则结果 = 信息框2("写入确认","是否写入" & 库位号 & "信号!","确定","取消")如果 结果 = 0 则       客户1.发送数据(文本到字节("1" & "_" & 库位号 & "_" & 写入数据 ,"UTF-8")) '将xxx发送给服务器弹出提示(库位号 & "信号已写入!" )   否则 弹出提示(库位号 & "信号未写入!")        结束 如果   结束 如果结束 如果结束 事件事件 按钮刷新.被单击()如果 是否连接服务器 = 假 则弹出提示("请连接服务器!")       否则 如果 库位号 = "0" 则 弹出提示("请选择库位号!")否则 客户1.发送数据(文本到字节( 0 & "_" & 库位号 ,"UTF-8")) '将xxx发送给服务器结束 如果                  结束 如果   结束 事件事件 按钮退出.被单击()结束程序()
结束 事件

C#用Socket和S7.net实现安卓手机APP读写西门子PLC数据(安卓APP使用的E4A中文编程软件)相关推荐

  1. s7 200 java_java android 读写西门子PLC数据,包含S7协议和Fetch/Write协议,s7支持200smart,300PLC,1200PLC,1500PLC...

    本文将使用一个gitHub开源的组件技术来读写西门子plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作 gi ...

  2. 笔记本连接android手机,安卓手机连接电脑,详细教您安卓手机怎样连接电脑

    有很多时候我们都需要将手机与电脑进行连接,将手机的资料或者是电脑上的资料拷入另外一边,可以通过usb或者是数据线将两者连接来达到这一目的,usb是以前算是古老的办法了,现在科技非常发达,只需要将电脑和 ...

  3. 安卓识别exfat_如何使安卓手机能够读写移动硬盘?

    实现安卓手机能够读写移动硬盘,要从硬件和软件两个方面来实现. 硬件方面,首先手机要支持OTG,需要移动硬盘USB接口转OTG接头,连接手机. 其次手机不能给移动硬盘供电,需要有独立供电接口的,连接移动 ...

  4. android手机号码恢复,手把手教你如何恢复安卓手机通讯录里面丢失的数据

    原标题:手把手教你如何恢复安卓手机通讯录里面丢失的数据 虽然现在的智能手机有非常多的功能,但是通话仍然是手机里面最为重要也最为常用的功能,通讯录也是十分重要的,他存放着我们手机内所有联系人的手机号.那 ...

  5. android手机做音乐软件,安卓手机必备的五个黑科技APP,每个都强大到没有朋友!要低调使用...

    原标题:安卓手机必备的五个黑科技APP,每个都强大到没有朋友!要低调使用 如今使用安卓手机的朋友并不少,那么大家平时会为自己的安卓手机安装哪些好用又强大的APP呢? 今天小编给大家带来了5个黑科技AP ...

  6. 2019年1月Android手机性能榜,2019中国手机排行榜_2019年安卓手机排行榜:受欢迎的安卓手机前十名...

    2019年安卓手机排行榜:受欢迎的安卓手机前十名 5月5日消息,安兔兔公布了2019年4月国内Android手机好评榜.前五名是三星Galaxy Note 9.魅族16th.三星Galaxy S9+. ...

  7. android手机进入fastboot,安卓手机怎么进去fastboot模式?安卓手机进入fastboot模式的方法_系统圣地...

    安卓手机怎么进入Fastboot模式呢?有些小伙伴还不太清楚怎么进入Fastboot模式,还有些小伙伴连Fastboot模式是做什么的都还不太了解,今天小编就给大家带来有关安卓手机Fastboot模式 ...

  8. 安卓手机反应越来越慢怎么办_安卓手机运行慢怎么办 只需几步轻松提升安卓手机速度...

    当手机进入到智能时代后,以前我们不是很关注的手机硬件,现在开始变得关注并且是越来越受关注了,在那个非智能时代手机流畅度对我们来说好像并不这么重要,我们更多关心是否支持蓝牙.MP3等等功能,因为一般对于 ...

  9. android程序数据迁移sd,如何迁移安卓手机及SD卡的数据?方法很简单!

    陈宏斌   2019-6-3 高级软件工程师 概要 随着手机图片.音乐.视频以及其他数据逐渐添加,原本手机及SD卡的内存可能已经不满足我们的数据存储需求.这时我们就需要使用SD卡扩展,或更换更大容量的 ...

  10. 安卓手机上有适合学生的日程app?

    对于学生们的职责,我想大多数人首先想到的肯定是学习,因为学习既可以让学生们开阔眼界.获取更多的知识,同时又能让他们掌握未来在社会上的的生存技能,所以虽然这不是他们唯一的责任,但却是他们当下主要的职责. ...

最新文章

  1. 开关灯效果思路代码分享
  2. Ansible:Ansibl项目生产环境快速布局
  3. Qt-捕获Windows消息
  4. Python语法糖——遍历列表时删除元素
  5. 爱因斯坦谈教育,放在首位的该是什么?
  6. python 数组转音频_Python3+将2声道音频,分拆成1声道
  7. 关于mysql的教学文章_数据库课程教学方法探索论文
  8. CentOS关机大法之shutdown命令格式
  9. Ubuntu下RamDisk使用
  10. 高清录播系统与流媒体服务器,来同品牌全高清录播系统方案
  11. MITO-ID 线粒体膜电位检测试剂盒的作用机制和应用
  12. 关于二叉树的前序、中序、后序三种遍历
  13. 学习go语言里Duck typing 概念
  14. 163手机登录邮箱显示服务器无法登录,163邮箱登陆不了_为何无法正常登录邮箱 ?...
  15. 【壮丽70年·奋斗新时代】宿州:从农业大市转身为“中国云都”
  16. IC基础知识(八)ROM、PROM、EPROM、EEPROM和Flash之间的区别
  17. C++问题汇总(一)
  18. html表格自动跨页,表格打印跨页_html/css_WEB-ITnose
  19. BST二叉搜索树插入节点建树并找出不平衡节点,networkx,Python
  20. 2022icpc沈阳站感想

热门文章

  1. 阿里云ESS弹性伸缩服务新功能来袭,更全面、更自动化的使用体验
  2. R语言线性混合效应模型实战案例
  3. 计算机二级java复习资料
  4. 原版XP SP3安装程序集成识别SATA的AHCI驱动的解决方法
  5. Vue网页录音,js录音mp3
  6. 自动驾驶汽车传感器融合系统及多传感器数据融合算法浅析
  7. 通过InstallShield官网申请注册码
  8. 如何从CPU顶盖获取有用信息
  9. 中国民用航空飞行学院 - 人事工资薪酬管理系统
  10. CC2530单片机精确延时的时间分析