VisionPro 工业通信协议
一、核心概念术语说明SerialPort.NET 串口通信类用于 RS-232/RS-485 等串行通信Socket网络套接字用于 TCP/IP 网络通信的基础类TCP传输控制协议提供可靠的、面向连接的通信UDP用户数据报协议无连接的通信协议PLC可编程逻辑控制器工业自动化核心设备S7.Net西门子 PLC 通信库支持 S7-200/300/400/1200/1500HslCommunication通用工业通信库支持三菱、欧姆龙、Modbus 等Modbus工业通信协议广泛应用于 PLC、传感器等设备波特率串口通信速率单位 bps比特/秒数据位每个字符的数据位数通常为 7 或 8 位停止位字符结束标志通常为 1、1.5 或 2 位校验位数据完整性校验支持奇校验、偶校验、无校验RS-232串行通信标准适用于短距离通信RS-485串行通信标准适用于长距离、多点通信二、常用操作1. 串口通信基础串口参数配置// 创建串口对象 SerialPort sp new SerialPort(); // 配置串口参数 sp.PortName COM2; // 串口名称 sp.BaudRate 9600; // 波特率 sp.DataBits 8; // 数据位 sp.StopBits StopBits.One; // 停止位 sp.Parity Parity.None; // 校验位 // 绑定数据接收事件 sp.DataReceived sp_DataReceived; // 打开串口 sp.Open(); // 发送数据 sp.Write(Hello PLC); // 关闭串口 sp.Close();数据接收处理private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) { // 获取字节大小 int size sp.BytesToRead; // 定义字节数组 byte[] data new byte[size]; // 读取数据 sp.Read(data, 0, data.Length); // 解码字节数组 string msg Encoding.Default.GetString(data); // 跨线程更新 UI textBox1.BeginInvoke(new Actionstring(str { textBox1.Text msg; }), msg); }2. 串口通信拍照实现public class SerialPortCamera { public CogAcqFifoTool cogAcq null; public string vppPath Directory.GetCurrentDirectory() \VPP\acq.vpp; // 初始化 public void Initialize() { // 加载相机 VPP cogAcq CogSerializer.LoadObjectFromFile(vppPath) as CogAcqFifoTool; // 打开串口 serialPort1.Open(); } // 串口数据接收事件 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { int size serialPort1.BytesToRead; byte[] data new byte[size]; serialPort1.Read(data, 0, data.Length); string msg Encoding.Default.GetString(data); // 检测到触发信号 T1 时拍照 if (msg.Contains(T1)) { cogAcq.Run(); cogRecordDisplay1.Image cogAcq.OutputImage; cogRecordDisplay1.Fit(); // 发送拍照完成信号 serialPort1.Write(True); } } // 释放资源 public void Cleanup() { if (cogAcq.Operator ! null) { cogAcq.Operator.FrameGrabber.Disconnect(false); } if (serialPort1.IsOpen) { serialPort1.Close(); } } }3. TCP/IP Socket 通信服务端实现public class TcpServer { public Socket ServerSocket { get; set; } // 监听套接字 // 注意ClientSocket 每次有新客户端连入就会被覆盖仅保持最后一个客户端 // 多客户端场景需改用 ListSocket 维护连接列表 public Socket ClientSocket { get; set; } // 客户端套接字 // 开始监听 public void StartListening(string ip, int port) { // 创建 Socket ServerSocket new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 绑定 IP 和端口 IPAddress ipAddress IPAddress.Parse(ip); IPEndPoint endPoint new IPEndPoint(ipAddress, port); // 绑定并监听 ServerSocket.Bind(endPoint); ServerSocket.Listen(5); // 5 等待队列最大积压数未处理的待接连接 // 开始接受连接 Thread listenThread new Thread(ListenClients); listenThread.IsBackground true; // 后台线程主程序退出时自动终止无需手动 Abort listenThread.Start(); } // 监听客户端连接 private void ListenClients() { while (true) { // Accept 阻塞等待客户端连接返回专用于该客户端通信的新 Socket ClientSocket ServerSocket.Accept(); // 获取客户端 IP:Port Console.WriteLine(ClientSocket.RemoteEndPoint); // 每个客户端开一个独立线程接收消息不阻塞监听循环 Thread receiveThread new Thread(ReceiveMessages); receiveThread.IsBackground true; receiveThread.Start(ClientSocket); } } // 接收消息 private void ReceiveMessages(object obj) { Socket socket obj as Socket; while (true) { byte[] buffer new byte[1024 * 1024 * 2]; // 2MB 缓冲区可根据实际消息大小调整 int received socket.Receive(buffer); // 阻塞接收返回实际收到的字节数 if (received 0) return; // received0 表示对端主动断开连接 string msg Encoding.UTF8.GetString(buffer, 0, received); Console.WriteLine(msg); } } // 发送消息只发给最后连入的客户端 public void SendMessage(string msg) { byte[] bytes Encoding.UTF8.GetBytes(msg); ClientSocket.Send(bytes); } // 停止服务 public void Stop() { if (ClientSocket ! null) { // Shutdown 先禁用收发确保对端知道连接即将关闭 ClientSocket.Shutdown(SocketShutdown.Both); ClientSocket.Close(); } ServerSocket.Close(); } }客户端实现public class TcpClient { public Socket ClientSocket { get; set; } private Thread receiveThread; // 保存线程引用断开时需要终止它 // 连接服务器 public bool Connect(string ip, int port) { try { ClientSocket new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ipAddress IPAddress.Parse(ip); IPEndPoint endPoint new IPEndPoint(ipAddress, port); ClientSocket.Connect(endPoint); // 开始接收消息 receiveThread new Thread(ReceiveMessages); receiveThread.IsBackground true; receiveThread.Start(); return true; } catch (Exception ex) { Console.WriteLine(ex.Message); return false; } } // 接收消息 private void ReceiveMessages() { while (true) { byte[] buffer new byte[1024 * 1024 * 2]; int received ClientSocket.Receive(buffer); if (received 0) return; string msg Encoding.UTF8.GetString(buffer, 0, received); Console.WriteLine(msg); } } // 发送消息 public void SendMessage(string msg) { byte[] bytes Encoding.UTF8.GetBytes(msg); ClientSocket.Send(bytes); } // 断开连接 public void Disconnect() { if (ClientSocket ! null) { ClientSocket.Shutdown(SocketShutdown.Both); ClientSocket.Close(); // Close 会导致 Receive 抛异常进而结束接收循环 } // 如果 Receive 未能自动结束用 Abort 强制终止 receiveThread?.Abort(); } }4. PLC 通信实现西门子 PLC 通信S7.Netusing S7.Net; public class SiemensPLC { public Plc plc null; // 构造函数 public SiemensPLC(CpuType type, string ip, short rack, short slot) { plc new Plc(type, ip, rack, slot); } // 连接 PLC public string Connect() { try { plc.Open(); return ConnectOK; } catch (Exception ex) { return $ConnectFail: {ex.Message}; } } // 检查连接状态 public bool IsConnected() { return plc ! null plc.IsConnected; } // 读取数据地址格式示例DB1.DBW0(字) DB1.DBD4(int/float) M0.0(bit) MW10(word) public object Read(string address) { return plc.Read(address); } // 写入数据地址格式同上float 类型先转为 uint 再写入BitConverter public void Write(string address, object value) { plc.Write(address, value); } // 读取字符串西门子 S7 String 格式首字节存字符个数后续为实际内容 public string ReadString(DataType dataType, int db, int startByte) { // 读 startByte 处 1 个字节字符串的实际长度 var count (byte)plc.Read(dataType, db, startByte, VarType.Byte, 1); // startByte1 跳过长度字节读取内容 string str (string)plc.Read(dataType, db, startByte 1, VarType.String, count); return str; } // 读取批量字节适用于自定义协议解析 // DataType 示例DataType.DataBlockDB块 DataType.MemoryM内存 public byte[] ReadBytes(DataType dataType, int db, int startByte, int count) { return plc.ReadBytes(dataType, db, startByte, count); } // 写入批量数据 public void WriteBytes(DataType dataType, int db, int startByte, byte[] value) { plc.WriteBytes(dataType, db, startByte, value); } // 断开连接 public void Disconnect() { plc.Close(); } }三菱 PLC 通信HslCommunicationusing HslCommunication; using HslCommunication.Profinet.Melsec; public class MitsubishiPLC { private MelsecMcNet plc; // 连接 PLC public bool Connect(string ip, int port) { plc new MelsecMcNet(ip, port); OperateResult result plc.ConnectServer(); return result.IsSuccess; } // 读取数据 // 三菱地址格式D100数据寄存器, M100辅助继电器, X0/Y0输入/输出, W100网络寄存器 public string Read(string address) { return plc.ReadInt32(address).Content.ToString(); } // 写入数据 public bool Write(string address, int value) { OperateResult result plc.Write(address, value); return result.IsSuccess; } // 断开连接 public void Disconnect() { plc.ConnectClose(); } }欧姆龙 PLC 通信HslCommunicationusing HslCommunication; using HslCommunication.Profinet.Omron; public class OmronPLC { private OmronFinsNet plc; // 连接 PLC public bool Connect(string ip, int port) { plc new OmronFinsNet(ip, port); OperateResult result plc.ConnectServer(); return result.IsSuccess; } // 写入数据 // 欧姆龙地址格式DM100数据内存, CIO100公共 I/O, W100工作区 public bool Write(string address, int value) { OperateResult result plc.Write(address, value); return result.IsSuccess; } // 断开连接 public void Disconnect() { plc.ConnectClose(); } }Modbus TCP 通信using HslCommunication.ModBus; public class ModbusTCP { private ModbusTcpNet busTcpClient; // 连接 public bool Connect(string ip, int port) { busTcpClient new ModbusTcpNet(ip, port); OperateResult result busTcpClient.ConnectServer(); return result.IsSuccess; } // 写入数据 // Modbus 地址格式100保持寄存器 4x, x1;100线圈 1x public bool Write(string address, int value) { OperateResult result busTcpClient.Write(address, value); return result.IsSuccess; } // 断开连接 public void Disconnect() { busTcpClient.ConnectClose(); } }三、问题排查错误1串口打开失败现象sp.Open()抛出异常原因串口被占用或不存在解决检查设备管理器中的串口号关闭其他占用串口的程序使用SerialPort.GetPortNames()获取可用串口错误2串口数据接收乱码现象接收到的数据为乱码原因编码格式不匹配解决确认发送端和接收端使用相同的编码格式尝试使用Encoding.UTF8或Encoding.Default检查波特率、数据位、停止位配置错误3Socket 连接超时现象ClientSocket.Connect()长时间无响应原因网络不通或服务器未启动解决使用ping命令测试网络连通性检查服务器 IP 和端口是否正确确认防火墙未阻止连接错误4跨线程操作 UI 异常现象System.InvalidOperationException: 线程间操作无效原因在非 UI 线程访问控件解决// 使用 BeginInvoke 更新 UI textBox1.BeginInvoke(new Actionstring(str { textBox1.Text str; }), msg);错误5PLC 连接失败现象plc.Open()抛出异常原因IP 地址、机架号、插槽号配置错误解决确认 PLC 的 IP 地址与电脑在同一网段检查 CPU 型号S7-200/300/1200/1500确认机架号和插槽号通常为 0,0 或 0,2错误6PLC 读写数据失败现象读写数据返回异常值原因地址格式错误或数据类型不匹配解决确认地址格式如 DB1.0、MD100检查数据类型int、float、string使用 PLC 编程软件验证地址四、相关资源官方文档Cognex VisionPro Documentation