上位机通信(一)

概念

基于软件的设备控制程序

涉及C#的语法

多线程

委托(Action/Func/delegate)

C#变量类型:Byte ushort short int uint long float double decimal

设备类型:Bit(位) Byte(字节) Word(字) Dword(双字) float

上位机数据交换

物理接口:232 422 485 SerialPort(串口)

以太网:网口(TCP/UDP),Socket

简单介绍一下串口通信和网口通信

串口通信

属性说明
名  称 说  明
PortName 获取或设置通信端口
BaudRate 获取或设置串行波特率
DataBits 获取或设置每个字节的标准数据位长度
Parity 获取或设置奇偶校验检查协仪
StopBits 获取或设置每个字节的标准停止位数
 串口通信示例
SerialPort serialPort = new SerialPort("COM1",9600,Parity.None,8,StopBits.One);
//打开串口
serialPort.Open(); 
//读取数据
方式一:订阅事件,被动触发 一对一  发送模式设置要一致 ASCII码

  serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);

  //方式二 Read

  Thread.Sleep(20);

  while (serialPort.BytesToRead == 0)

  {

    if (serialPort.BytesToRead == 1)

      Console.WriteLine(serialPort.BytesToRead);

    // 超时情况

  }

  byte[] buffer = new byte[serialPort.BytesToRead];

  serialPort.Read(buffer, 0, serialPort.BytesToRead);

  string s = String.Join(" ", new List(buffer).Select(x => x.ToString("X2")));

  //方式三 ReadByte

  serialPort.DiscardInBuffer();

  serialPort.DiscardOutBuffer();

  serialPort.Write(new byte[] { 0x01, 0x02 }, 0, 2);

  List datas = new List();

  while (datas.Count < 8)

  {

    byte b = (byte)serialPort.ReadByte();

    datas.Add(b);

  }

  if (datas.Count == 8)

  {

    string s1 = String.Join(" ", new List(datas).Select(x => x.ToString("X2")));

    this.Dispatcher.Invoke(new Action(() =>

    {

      tb_receive.Text = s1;

    }));

  }

  else

  {

    tb_receive.Text = "数据接收异常";

  }
 常用方法

GetPortNames:获取当前计算机的串行端口名称数组【静态】

Open:打开一个新的串行端口连接

Read:从SerialPort输入缓冲区中读取

ReadByte:读取一个字节的数据

ReadChar:读取一个字符的数据

Write:将数据写入串行端口输出缓冲区

Close:关闭端口连接,将IsOpen属性设置为False,并释放内部Stream常用

长连接|短连接的区分:

背景 :软件持续请求设备数据

短连接:每次请求:打开->发送->接收->关闭 串口 总线

长连接:打开->每次请求:发送->接收 关闭软件:关闭连接

网口通讯

常用方法
TCP 服务端 客户端
常用方法

Bind:将 Socket 绑定到特定的本地 IP 地址和端口,准备接受连接。

Listen:开启监听 Accept()/BeginAccept()/EndAccept():接受新的传入连接请求,返回一个用于与客户端通信的新 Socket 对象。

Send()/BeginSend()/EndSend():向连接的客户端发送数据。 Receive()/BeginReceive()EndReceive(): 从连接的客户端接收数据。

Connect/ BeginConnect/ EndConnect: 发起与指定远程服务端地址和端口的连接请求。 Send()/BeginSend()/EndSend():向连接的服务端发送数据。

Receive()/BeginReceive()/ EndReceive(): 从连接的服务端接收数据。

关闭

Shutdown:禁用Socket的发送接收 (Receive) 或双向 (Both) 功能。

Disconnect:(较少直接使用) 关闭Socket连接,并根据提供给方法的参数决定是否允许重用实例。

Close:关闭Socket连接并释放所有相关资源,另外提供一个带有参数的重载来允许在指定的超时时间内将已经排队的数据发送出去。

Dispose:实现 IDisposable 接口。释放当前Socket实例所使用的未托管资源

与服务端操作完全相同
断线重连

检测原理相同,实现逻辑通常在客户端或连接管理模块:

第一种:上位机应用 -------------> PLC

场景:常见于串口通信或直连以太网场景,表现为电缆损坏、接口松动、PLC端口故障等

Poll:检查连接状态

bool state = !(!socket.Connected || (socket.Poll(1, SelectMode.SelectRead) && socket.Poll(1, SelectMode.SelectWrite) && (socket.Available == 0)));// 返回True :连接正常

第二种:上位机应用 --------路由器/交换机 -----(断连)----> PLC

场景:路由器/交换机硬件故障或电源中断。

交换机端口配置错误(如VLAN隔离、端口禁用)

检测方法:心跳

定义应用层心跳协议(不同的PLC不同处理)

单独线程定时执行Send心跳请求

等待Receive心跳响应,设置合理超时

连续超时N次时 判断失联

重连机制 (通常由客户端/连接管理实现):

检测到断线后,调用 Close/Dispose 清理旧 Socket。

创建新 Socket 实例。

重新发起 Connect/BeginConnect

实现指数退避等重连策略避免风暴。

Console.WriteLine(socket.Connected);// 保留最后一次操作时的状态,不是实时的状态

与服务端描述的 Poll 和 心跳 机制逻辑完全相同。客户端是执行这些检测的常见位置。

第一种:上位机应用 -------------> PLC

Poll:检查连接状态

bool state = !(!socket.Connected || (socket.Poll(1, SelectMode.SelectRead) && socket.Poll(1, SelectMode.SelectWrite) && (socket.Available == 0)));// 返回True :连接正常

场景:常见于串口通信或直连以太网场景,表现为电缆损坏、接口松动、PLC端口故障等

第二种:上位机应用 --------路由器/交换机 -----(断连)----> PLC

场景:路由器/交换机硬件故障或电源中断。

|交换机端口配置错误(如VLAN隔离、端口禁用)

心跳

心跳 请求 I0.0(不同的PLC不同处理)->响应false

Send->Receive(超时处理) 超时N次时 判断失联

单独线程 10秒钟执行一次

Console.WriteLine(socket.Connected);// 保留最后一次操作时的状态,不是实时的状态

异步发送

BeginSend

BeginReceive +

socket.EndSend(ret); BeginAccept(AsyncCallback, object): 开始异步接受连接请求。 EndAccept(IAsyncResult): 完成异步接受操作,获取新客户端 Socket。 BeginSend(...) / EndSend(IAsyncResult)**: 开始/结束异步发送。 BeginReceive(...) / EndReceive(IAsyncResult): 开始/结束异步接收。

正确关闭异步对象至关重要,EndXxx 必须在对应的 BeginXxx 完成后调用。

BeginAccept(AsyncCallback, object): 开始异步接受连接请求。 EndAccept(IAsyncResult): 完成异步接受操作,获取新客户端 Socket。 BeginSend(...)EndSend(IAsyncResult): 开始/结束异步发送。 BeginReceive(...) / EndReceive(IAsyncResult): 开始/结束异步接收。

正确关闭异步对象至关重要,EndXxx 必须在对应的 BeginXxx 完成后调用。

丢包/粘包

添加包头|分解包头

发送端处理 (封包 Framing):

常用方法:

添加包头 (Header): 在应用层数据前添加固定长度的包头。包头通常包含:

数据长度 (Length/Payload Size): (e.g., 4字节 Int32) 指示后续应用数据的字节数。 

消息类型/ID (Message Type/ID): (可选,e.g., 2字节) 标识数据含义

接收端处理 (解包 Deframing):

1.先读取固定长度的包头 (e.g., 6字节 = 4字节长度+2字节类型)。

2.解析包头,获取应用数据的长度 loadLength

3.根据 loadLength 继续读取指定字节数的应用数据。

4.组合/解析出完整的应用层消息。

TCP和UDP对比
TCP UDP
连接方式 TCP面向连接 面向无连接
可靠性 高可靠 无确认机制
传输顺序 有序 独立传输,可能无序
数据边界 需处理粘包、拆包 独立数据报
方式 1.Socket 2.服务端:TcpListener 客户端:TcpClient 1.Socket 2.UdpClient

你可能感兴趣的:(上位机,c#,上位机)