C# CIP协议2 demo测试代码
参考:EtherNet/IP实现欧姆龙NX系列PLC通信 - funiyi816 - 博客园
1、引言
工业以太网协议 (Ethernet/IP) 是由ODVA所开发并得到了罗克韦尔自动化的强大支持。它使用已用于ControlNet和DeviceNet的控制和信息协议 (CIP) 为应用层协议。
 
CIP提供了一系列标准的服务,提供“隐式”和“显示”方式对网络设备中的数据进行访问和控制。CIP数据包必须在通过以太网发送前经过封装,并根据请求服务类型而赋予一个报文头。这个报文头指示了发送数据到响应服务的重要性。通过以太网传输的CIP数据包具有特殊的以太网报文头,一个IP头、一个TCP头和封装头。封装头包括了控制命令、格式和状态信息、同步信息等。这允许CIP数据包通过TCP或UDP传输并能够由接收方解包。相对于DeviceNet或ControlNet,这种封装的缺点是协议的效率比较低。以太网的报文头可能比数据本身还要长,从而造成网络负担过重。因此,EtherNet/IP更适用于发送大块的数据 ( 如程序 ) ,而不是DeviceNet和ControlNet更擅长的模拟或数字的I/O数据。
EtherNet/IP指的是"以太网工业协议"(Ethernet Industrial Protocol)。它定义了一个开放的工业标准,将传统的以太网与工业协议相结合。该标准是由国际控制网络(CI, ControlNet International)和开放设备网络供应商协会 (ODVA)在工业以太网协会 (IEA, Industrial Ethernet Association)的协助下联合开发的,并于2000年3月推出。EtherNet/IP是基于TCP/IP系列协议,因此采用以原有的形式OSI层模型中较低的4层。所有标准的以太网通信模块,如PC接口卡、电缆、连接器、集线器和开关都能与 EtherNet/IP 一起使用。
2、 EtherNet/IP_报文格式
注册请求帧:EtherNet/IP与Fins类似,在正式通信前需要进行注册请求,获取到会话句柄。
注册请求帧报文格式如下:
 
因此,一个完整的注册请求帧发送实例:
65 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
注册应答帧:顾名思义,注册应答帧就是注册请求帧的应答报文,包含了我们需要的会话句柄。
注册应答帧报文格式如下:
 
1 注册请求帧接收实例:
65 00 04 00 71 01 0E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
根据应答报文,提取会话句柄为:0x71 0x01 0x0E 0x00
其中状态相当于错误代码,如下所示:
 
2 读取单个信息报文帧 EtherNet/IP
PLC标签:TAG1
读取报文:
封装头:6F 00 28 00 70 01 27 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 02 00 00 00 00 00 B2 00 18 00 52 02 20 06 24 01 0A F0 0A 00 4C 03 91 04 54 41 47 31 01 00 01 00 01 00
报文解析:
6F 00 命令码
28 00 后面数据报文长度
70 01 27 00 会话句柄
00 00 00 00 状态,默认值0
00 00 00 00 00 00 00 00 发送方描述
00 00 00 00 选项默认
00 00 00 00 接口句柄 00 00 00 00 代表CIP
0A 00 超时
02 00 项数
00 00 空地址项
00 00 空地址项长度
B2 00 未连接项 默认
18 00 CIP报文包的长度
52 命令
02 请求路径长度
20 06 24 01 默认请求路径
0A F0 0A 00 默认超时
4C 服务标识
03 CIP长度多少字
91 固定
04 PLC标签长度 多少个字节
01 00 读取长度
01 00 01 00 槽号
返回信息报文帧 EtherNet/IP
返回报文:
封装头:6F 00 18 00 71 01 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 02 00 00 00 00 00 B2 00 08 00 CC 00 00 00 C1 00 00 00
报文解析:
6F 00 命令
18 00 长度 (总长度 -封装头长度 )
71 01 14 00 会话句柄(会话ID)
00 00 00 00 会话状态
00 00 00 00 00 00 00 00 发送方描述 固定
00 00 00 00 选项 默认
00 00 00 00 接口句柄 ,00000000 指CIP
0A 00 超时
02 00 项数 默认2
00 00 连接的地址项
00 00 连接地址项长度
B2 00 未连接数据项
08 00 连接长度
CC 服务标识
00 填充字节
00 00 状态
C1 00 数据类型 BOOL 备注: ( 0x00C1 为 (C# bool) 布尔型,0x00C2(C# byte)为BYTE型 ,0x00C3 为(C# short)整型16bit,0x00C4为 (C# int)型64bit,0x00C5 (C# long型) ,0x00CA 为float 实型,0x00CB 为double 实型 ,欧姆龙plc 0x00CD string类型) 部分plc string的都不一样建议用wireshark抓包查看
00 00 数据值
3 写入单个信息报文帧 EtherNet/IP
PLC标签:TAG1
写入报文:
封装头:6F 00 2C 00 71 01 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 02 00 00 00 00 00 B2 00 1C 00 52 02 20 06 24 01 0A F0 0E 00 4D 03 91 04 54
41 47 31 C1 00 01 00 00 00 01 00 01 00
解析报文:
6F 00 命令码
2C 00 长度(去除header后报文长度 字节为单位)
71 01 14 00 会话句柄
00 00 00 00 会话状态
00 00 00 00 00 00 00 00 发送方描述 固定
00 00 00 00 选项 默认
00 00 00 00 接口句柄 ( 00 00 00 00 指CIP)
0A 00 超时
02 00 项数 默认2
00 00 空地址项 默认
00 00 空地址项长度 默认
B2 00 未连接数据项 默认
1C 00 数据长度 指后面数据长度 (字节)
52 请求服务代码
02 请求路径长度
20 06 24 01 请求路径 默认
0A F0 超时默认 245760ms
OE 00 长度(从服务标识开始 到 写入的值 结束 )
4D 服务标识
03 长度(91 04 54 41 47 31 )
91 固定
04 标签长度
54 41 47 31 TAG1(标签名)
C1 00 数据类型 BOOL 备注: ( 0x00C1 为 (C# bool) 布尔型,0x00C2(C# byte)为BYTE型 ,0x00C3 为(C# short)整型16bit,0x00C4为 (C# int)型64bit,0x00C5 (C# long型) ,0x00CA 为float 实型,0x00CB 为double 实型 ,欧姆龙plc 0x00CD string类型) 部分plc string的都不一样建议用wireshark抓包查看
01 00 写入数量
00 00 写入的值 false:0 TRUE:任意非0数
01 00 01 00 PLC槽号
返回报文:
封装头:6F 00 14 00 71 01 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 02 00 00 00 00 00 B2 00 04 00 cd 00 00 00
解析报文:
71 01 14 00 会话句柄
B2 00 未连接数据项 默认
CD 服务标识(写)固定
00 填充字节
0000 状态:成功
0A 00 超时
4 多标签读取
参考单标签的报文帧,读取TAG1 和TAG2
54 41 47 31 TAG1的ascii码
54 41 47 32 TAG2的ascii码
发送报文:
6F 00 3E 00 70 01 27 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 02 00 00 00 00 00 B2 00 2E 00 52 02 20 06 24 01 0A F0 20 00 0A 02 20 02 24 01 02 00 06 00 10 00 4C 03 91 04 54 41 47 31 01 00 4C 03 91 04 54 41 47 32 01 00 01 00 01 00
返回报文:
6F 00 2A 00 70 01 27 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 02 00 00 00 00 00 B2 00 1A 00 8A 00 00 00 02 00 06 00 0E 00 CC 00 00 00 C1 00 01 00 CC 00 00 00 C1 00 01 00
5 注销会话
注销会话顾名思义就是通信结束时发送,注销会话是没有返回值的。
注销会话请求报文:
66 00 00 00 71 01 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
响应返回报文
66 00 00 00 71 01 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
报文解析:
66 00 :命令
00 00 :长度
71 01 04 00:要注销的会话句柄
00 00 00 00 状态 默认0
00 00 00 00 00 00 00 00:发送内容 默认0
00 00 00 00:选项 默认0
测试demo :使用.net6 wpf +hpsocke.net,模拟器使用的是hslcommunication搭建的cip虚拟服务器
<Window x:Class="EIP.Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:EIP.Test"
        xmlns:vm="clr-namespace:EIP.Test.ViewModel"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <vm:MainWindowViewModel></vm:MainWindowViewModel>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0"  >
       
            <Button Margin="3" Content="获取会话id" Command="{Binding SendCommand1}"></Button>
            <Button Margin="3" Content="读取TAG1" Command="{Binding TestReadCommand2}"></Button>
            <Button Margin="3" Content="写入TAG1" Command="{Binding TestWriteCommand2}"></Button>
            <Button  Margin="3" Content="读取TAG1 TAG2" Command="{Binding TestReadTwoTagsCommand3}"></Button>
            <Button  Margin="3" Content="取消会话id" Command="{Binding CancelSendCommand1}"></Button>
        </StackPanel>
    
            <TextBox TextWrapping="Wrap" Grid.Row="1" Text="{Binding Msg}"></TextBox>
       
       
    </Grid>
</Window>
using HPSocket.Tcp;
using libplctag;
using libplctag.DataTypes;
using libplctag.NativeImport;
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Markup;
namespace EIP.Test.ViewModel
{
    public  class MainWindowViewModel:BindableBase
    {
       TcpClient  tcpClient=new TcpClient();
        public MainWindowViewModel()
        {
            bool ok= tcpClient.Connect("127.0.0.1", 44818);
           var status= tcpClient.IsConnected;
            tcpClient.OnReceive += TcpClient_OnReceive;
        }
        private string _Msg;
        public string Msg
        {
            get { return _Msg; }
            set { SetProperty(ref _Msg, value); }
        }
        byte[] headerId=new byte[4];
        private HPSocket.HandleResult TcpClient_OnReceive(HPSocket.IClient sender, byte[] data)
        {
          
            var str = string.Join(" ", data.Select(y => y.ToString("X2")));
            AddMSg("<--" + str);
            if (data.Length > 2)
            {
                if (data[0] == 0x65 && data[1] == 0x00)
                {
                    Span<byte> bb = new Span<byte>(data);
                    var vals = bb.Slice(4, 4);
                    headerId = vals.ToArray();
                    AddMSg("会话id:" + string.Join(" ", headerId.Select(s => s.ToString("X2"))));
                }
            }
            if (data.Length>=47)
            {
                //bool型
                if (data[44]==0xc1&& data[45]==0x00)
                {
                    Span<byte> pp = new Span<byte>(data);
                   var arrs= pp.Slice(46, 2).ToArray();
                    bool val = BitConverter.ToBoolean(arrs);
                    AddMSg("bool:" + val);
                }
                //byte型
                if (data[44] == 0xc2 && data[45] == 0x00)
                {
                }
                //short
                if (data[44] == 0xc3 && data[45] == 0x00)
                {
                    Span<byte> pp = new Span<byte>(data);
                    int datalen = 2;
                    var arrs = pp.Slice(data.Length- datalen, datalen).ToArray();
                    short val = BitConverter.ToInt16(arrs);
                    AddMSg("short:" + val);
                }
                //int
                 if (data[44] == 0xc4 && data[45] == 0x00)
                {
                    Span<byte> pp = new Span<byte>(data);
                    int datalen = 4;
                    var arrs = pp.Slice(data.Length - datalen, datalen).ToArray();
                    int val = BitConverter.ToInt32(arrs);
                    AddMSg("int:" + val);
                }  //long
                if (data[44] == 0xc5 && data[45] == 0x00)
                {
                    Span<byte> pp = new Span<byte>(data);
                    int datalen = 8;
                    var arrs = pp.Slice(data.Length - datalen, datalen).ToArray();
                    long val = BitConverter.ToInt64(arrs);
                    AddMSg("long:" + val);
                }
                //float
                if (data[44] == 0xCA && data[45] == 0x00)
                {
                    Span<byte> pp = new Span<byte>(data);
                    int datalen = 4;
                    var arrs = pp.Slice(data.Length - datalen, datalen).ToArray();
                    float val = BitConverter.ToSingle(arrs);
                    AddMSg("float:" + val);
                }   
                //double
                if (data[44] == 0xCB && data[45] == 0x00)
                {
                    Span<byte> pp = new Span<byte>(data);
                    int datalen = 8;
                    var arrs = pp.Slice(data.Length - datalen, datalen).ToArray();
                    double val = BitConverter.ToDouble(arrs);
                    AddMSg("double:" + val);
                }     
                //string
                if (data[44] == 0xD0 && data[45] == 0x00)
                {
                    Span<byte> pp = new Span<byte>(data);
                    var lenArray= pp.Slice(46, 2).ToArray();
                    int datalen = BitConverter.ToInt16(lenArray);
                    var arrs = pp.Slice(48, datalen).ToArray();
                    string val = Encoding.UTF8.GetString(arrs);
                    AddMSg("string:" + val);
                }
            }
            return HPSocket.HandleResult.Ok;
        }
        private void AddMSg(string str)
        {
            this.Msg += $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}: " + str + "\r";
        }
 
        /// <summary>
        /// 1 注册会话
        /// </summary>
        private byte[] RegisterSessionIdcmd = new byte[28]
        {
          //--------------------------------------------------------Header 24byte-------------------------------------
          0x65,0x00,//命令 2byte 65 00    
          0x04,0x00,//Header后面数据的长度 2byte  04 00
          0x00,0x00,0x00,0x00,//会话句柄 4byte 00 00 00 00
          0x00,0x00,0x00,0x00,//状态默认0 4byte 00 00 00 00
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//发送方描述默认0 8byte 00 00 00 00 00 00 00 00 
          0x00,0x00,0x00,0x00,//选项默认0 4byte  00 00 00 00
               //-------------------------------------------------------CommandSpecificData 指令指定数据 4byte
          0x01,0x00,//协议版本 2byte 01 00 
          0x00,0x00,//选项标记 2byte 00 00
        };
        //2 读取单个
        private List<byte> ReadSingleTagHeader = new List<byte>//24
        {
           0x6F,0x00,//命令码 2byte
          0x28,0x00,//后面数据报文长度 2byte(总长度-Header的长度)=40  (进行动态替换)
          0x00,0x00,0x00,0x00,//会话句柄 4byte (进行动态替换)
          0x00,0x00,0x00,0x00,//状态,默认值0
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 发送方描述
          0x00,0x00,0x00,0x00,//选项默认
        };
        //2.1 读取单个
        private List<byte> CommandSpecificData =  new List<byte>()// 16 
        {
               0x00,0x00,0x00,0x00,//接口句柄 00 00 00 00 代表CIP 
              0x0A,0x00,//超时
              0x02,0x00,//项数
              0x00,0x00,//空地址项
              0x00,0x00,//空地址项长度
              0xb2,0x00,//未连接项 默认
              0x18,0x00,//CIP报文包的长度 (进行动态替换)
        };
        //2.2 读取单个
        private List<byte> CipMessage = new List<byte>()//24
        {
               0x52,// 命令
               0x02 ,   //请求路径长度 
               0x20, 0x06 ,0x24 ,0x01 ,//默认请求路径
              //0x22,06,0x24,0x01,//请求路径 默认0x01240622 4byte
              //0x0A,0xF0, 0x0A,0x00, //默认超时
              0x0A,0xF0, 0x0C,0x00, //默认超时
              0x4C,//  服务标识
              0x03,//  CIP长度多少字  2byte  规律为 (标签名的长度+1/2)+1 (进行动态替换)
              0x91,//固定
              0x04,//PLC标签长度 多少个字节  (进行动态替换)
              0x54,0x41,0x47,0x31,//进行动态替换) 标签名 :TAG1转换成ASCII字节 当标签名的长度为奇数时,需要在末尾补0  比如TAG转换成ASCII为0x54,0x41,0x47, (进行替换) 需要在末尾补0 变成 0x54,0x41,0x47,0 
               0x01,0x00,//服务命令指定数据 默认为0x0001  
              0x01,0x00,0x01,0x00//最后一位是PLC的槽号   
        };
        //3 写入单个
        byte[] WriteSingleTags = new byte[]
       {
                      0x6F ,0x00 ,//命令码
                      0x2C ,0x00 ,//长度(去除header后报文长度 字节为单位)(1 进行动态替换)
                      0x71 ,0x01 ,0x14 ,0x00 ,//会话句柄 (2 进行动态替换)
          
                      0x00 ,0x00 ,0x00 ,0x00 ,//会话状态
                      0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,//发送方描述 固定
                      0x00 ,0x00 ,0x00 ,0x00 ,//选项 默认
                      0x00 ,0x00 ,0x00 ,0x00 ,//接口句柄 ( 00 00 00 00 指CIP)
                      0x0A ,0x00 ,//超时
                      0x02 ,0x00 ,//项数 默认2
          
                      0x00 ,0x00 ,//空地址项 默认
                      0x00 ,0x00 ,//空地址项长度 默认
          
                      0xB2 ,0x00 ,//未连接数据项 默认
                      0x1C ,0x00 ,//数据长度 指后面数据长度 (字节)(3 进行动态替换)
          
                 
            };
        //3.1 写入单个
        byte[] WriteSingleTags2 = new byte[]
       {
                     
          
                      0x52 ,//请求服务代码
          
                      0x02 ,//请求路径长度
          
                      0x20 ,0x06 ,0x24 ,0x01 ,//请求路径 默认
                      0x0A ,0xF0 ,//超时默认 245760ms
                      0x0E ,0x00 ,// 长度(从服务标识开始 到 写入的值 结束) (1 进行动态替换)
                      0x4D ,// 服务标识
                      0x03 ,//长度  (2 进行动态替换 规律为 (标签名的长度+1/2)+1  )
                      0x91 ,// 固定
                      0x04 ,//标签长度 (3 进行动态替换)
                      0x54 ,0x41 ,0x47 ,0x31,//TAG1(标签名) (4 进行动态替换 奇数个后面加 0) 
                      0xC1 ,0x00 ,//数据类型 bool (5 进行动态替换) 0x00C3(195) 为整型,0x00CA(202)为实型,0x00C1(193)为布尔型,long 型为0x00C4,BYTE为0x00C2 ,欧姆龙plc 0x00CD string类型
                      //0x01,0x00 string需要加上字符串的长度 (7 进行动态加入)
                      0x01 ,0x00 ,//写入数量
                      0x00 ,0x00 ,//写入的值 false:0  TRUE:任意非0数  (6 进行动态替换)
                      0x01 ,0x00 ,0x01 ,0x00//PLC槽号
            };
        //4 读取多个
        public byte[] ReadTwoTags = new byte[]
    {
               0x6F ,0x00 ,//命令码
                0x3E ,0x00 ,//长度(去除header后报文长度 字节为单位)
                0x70 ,0x01 ,0x27 ,0x00,//会话句柄
               0x00 ,0x00 ,0x00 ,0x00 //会话状态
              ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 //发送方描述 固定
                ,0x00 ,0x00 ,0x00 ,0x00 //选项 默认
                ,0x00 ,0x00 ,0x00 ,0x00 //接口句柄 ( 00 00 00 00 指CIP)
                ,0x0A ,0x00 //超时
                ,0x02 ,0x00 //项数 默认2
                ,0x00 ,0x00 //空地址项 默认
                ,0x00,0x00 // 空地址项长度 默认
                ,0xB2,0x00 //未连接数据项 默认
                ,0x2E ,0x00 //CIP报文包的长度
                ,0x52 //请求服务代码
                ,0x02 //请求路径长度
                ,0x20 ,0x06 ,0x24 ,0x01 //请求路径 默认
                ,0x0A ,0xF0 //默认超时
                ,0x20 ,0x00 //CIP指令长度
                ,0x0A  //服务代码(多个标签)
                ,0x02 //请求路径大小  
                ,0x20  ,0x02 ,0x24 ,0x01 //请求路径 
                ,0x02 ,0x00 //标签的数量  
                ,0x06 ,0x00 //偏移量(初始值为:2+标签数量*2)
                ,0x10 ,0x00 //偏移量 =标签服务长度+初始偏移量(有几个标签就有几个偏移量)
                ,0x4C//服务标识 
                ,0x03 //CIP长度多少字
                ,0x91 //固定
                ,0x04 //PLC标签长度 多少个字节
                ,0x54 ,0x41 ,0x47 ,0x31 //TAG1(标签名)
                ,0x01 ,0x00 // 读取长度
                ,0x4C//服务标识 
                ,0x03 //CIP长度多少字
                ,0x91 //固定
                ,0x04 //PLC标签长度 多少个字节
                ,0x54 ,0x41 ,0x47 ,0x32 //TAG2(标签名)
                ,0x01 ,0x00  // 读取长度
                ,0x01 ,0x00 ,0x01 ,0x00
    };
        byte[] cancelBuffers = new byte[]
      {
             0x66 ,0x00 ,0x00 ,0x00 ,0x71 ,0x01 ,0x04 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
      };
        private DelegateCommand _SendCommand1;
        public DelegateCommand SendCommand1 =>
            _SendCommand1 ?? (_SendCommand1 = new DelegateCommand(ExecuteSendCommand1));
        /// <summary>
        ///    1 发送获取注册会话id
        /// </summary>
        void ExecuteSendCommand1()
        {
            var str = string.Join(" ", RegisterSessionIdcmd.Select(y => y.ToString("X2")));
            AddMSg("-->" + str);
            ChecktcpClient(tcpClient);
            tcpClient.Send(RegisterSessionIdcmd,RegisterSessionIdcmd.Length);
            Thread.Sleep(50);
 
        
        }
        private DelegateCommand _CancelSendCommand1;
        public DelegateCommand CancelSendCommand1 =>
            _CancelSendCommand1 ?? (_CancelSendCommand1 = new DelegateCommand(ExecuteCancelSendCommand1));
        /// <summary>
        /// 5 取消会话id
        /// </summary>
        void ExecuteCancelSendCommand1()
        {
            byte[] sendBytes = new byte[cancelBuffers.Length];
            Array.Copy(cancelBuffers , sendBytes, sendBytes.Length);
            //修改会话id
            for (int i = 0; i < 4; i++)
            {
                sendBytes[4 + i] = headerId[i];
            }
            var str = string.Join(" ", sendBytes.Select(y => y.ToString("X2")));
            AddMSg("取消会话-->" + str);
            ChecktcpClient(tcpClient);
            tcpClient.Send(sendBytes, RegisterSessionIdcmd.Length);
            Thread.Sleep(50);
        }
        private DelegateCommand _TestReadCommand2;
        public DelegateCommand TestReadCommand2 =>
            _TestReadCommand2 ?? (_TestReadCommand2 = new DelegateCommand(ExecuteTestCommand2));
        /// <summary>
        /// 2 读取单个标签参数
        /// </summary>
        void ExecuteTestCommand2()
        {
            string tagName = "T1234";
          
            byte[] tagbuffers = Encoding.UTF8.GetBytes(tagName);
            if (tagbuffers.Length%2!=0)
            {
                var tempbuffers = tagbuffers.ToList();
                tempbuffers.Add(0x00);
                tagbuffers = tempbuffers.ToArray();
            }
           var tempReadSingleTagHeader = ReadSingleTagHeader.ToList();
           var tempReadCommandSpecificData = CommandSpecificData.ToList();
           var tempReadCipMessage = CipMessage.ToList();
            //1修改会话id
            for (int i = 0; i < 4; i++)
            {
                tempReadSingleTagHeader[4+i] = headerId[i];
            }
            tempReadCipMessage.RemoveAt(14);
            tempReadCipMessage.RemoveAt(14);
            tempReadCipMessage.RemoveAt(14);
            tempReadCipMessage.RemoveAt(14);
            //2 修改标签名
            for (int i = 0; i < tagbuffers.Length; i++)
            {
                tempReadCipMessage.Insert(14+i, tagbuffers[i]);
            }
            //3 修改标签名长度
            tempReadCipMessage[11] = (byte)((tagName.Length + 1) / 2 + 1);
            //4 修改tag长度
            tempReadCipMessage[13] = (byte)tagName.Length;
            //5 修改长度
            var lengs=  BitConverter.GetBytes((short)tempReadCipMessage.Count());
            tempReadCommandSpecificData[14] = lengs[0];
            tempReadCommandSpecificData[15]= lengs[1];
            //6
            var lengs2 = BitConverter.GetBytes((short)(tempReadCipMessage.Count() + tempReadCommandSpecificData.Count()));
            tempReadSingleTagHeader[2] = lengs2[0];
            tempReadSingleTagHeader[3] = lengs2[1];
            var datas = tempReadSingleTagHeader.Concat(tempReadCommandSpecificData).Concat(tempReadCipMessage).ToArray();
            var str = string.Join(" ", datas.Select(y => y.ToString("X2")));
            AddMSg("-->" + str);
            tcpClient.Send(datas, datas.Length);
            Thread.Sleep(50);
            var val=   BitConverter.ToBoolean(new byte[] { 0xff, 0xff });
        }
        private DelegateCommand _TestWriteCommand2;
        public DelegateCommand TestWriteCommand2 =>
            _TestWriteCommand2 ?? (_TestWriteCommand2 = new DelegateCommand(ExecuteWriteCommand2));
        /// <summary>
        /// 3 写入单个标签
        /// </summary>
        void ExecuteWriteCommand2()
        {
            var tempWriteSingleTags = WriteSingleTags.ToArray();
            var tempWriteSingleTags2 = WriteSingleTags2.ToList();
            string tagName = "T1234";
            string datatype = "string";
            object wval = "aaab1";
            byte[] tagbuffers = Encoding.UTF8.GetBytes(tagName);
            if (tagbuffers.Length%2!=0)
            {
              var temp=  tagbuffers.ToList();
                temp.Add(0);
                tagbuffers = temp.ToArray();
            }
            //p1.2修改会话id
            for (int i = 0; i < 4; i++)
            {
                tempWriteSingleTags[4 + i] = headerId[i];
            }
            //p2.2修改标长度
            tempWriteSingleTags2[11] = (byte)((tagName.Length + 1) / 2 + 1);
            //p2.3 修改tag标签长度
            tempWriteSingleTags2[13] = (byte)tagName.Length;
            tempWriteSingleTags2.RemoveAt(14);
            tempWriteSingleTags2.RemoveAt(14);
            tempWriteSingleTags2.RemoveAt(14);
            tempWriteSingleTags2.RemoveAt(14);
            //p2.4 修改标签名
            for (int i = 0; i < tagbuffers.Length; i++)
            {
                tempWriteSingleTags2.Insert(14 + i, tagbuffers[i]);
            }
            byte[] datatypebuffer = new byte[2];
            //写入的类型
            int typestartindex = 13+1 + tagbuffers.Length;
            //写入的值
            int valuestartindex = tempWriteSingleTags2.Count-2-4;
            byte[] wvals = null;
            //删除写入的值
            tempWriteSingleTags2.RemoveAt(valuestartindex);
            tempWriteSingleTags2.RemoveAt(valuestartindex);
            //p2.5 修改类型
            switch (datatype)
            {
                case "bool":
                    tempWriteSingleTags2[typestartindex] = 0xC1;
                    tempWriteSingleTags2[typestartindex+1] = 0x00;
                    bool tempval = Convert.ToBoolean(wval);
                    wvals = BitConverter.GetBytes(tempval?0xffff:0);
                    //p2.6 修改写入的值
                    tempWriteSingleTags2.Insert(valuestartindex, wvals[0]);
                    tempWriteSingleTags2.Insert(valuestartindex + 1, wvals[1]);
                    break;
                case "byte":
                    tempWriteSingleTags2[typestartindex] = 0xC2;
                    tempWriteSingleTags2[typestartindex + 1] = 0x00;
                    byte tempbyteval  = Convert.ToByte(wval);
                    wvals = BitConverter.GetBytes(tempbyteval);
                    //p2.6 修改写入的值
                    tempWriteSingleTags2.Insert(valuestartindex, wvals[0]);
                    tempWriteSingleTags2.Insert(valuestartindex + 1,0);
                    break;
                case "short":
                    tempWriteSingleTags2[typestartindex] = 0xC3;
                    tempWriteSingleTags2[typestartindex + 1] = 0x00;
                    short val = Convert.ToInt16(wval);
                    //p2.6 修改写入的值
                    wvals= BitConverter.GetBytes(val);
                    tempWriteSingleTags2.Insert(valuestartindex, wvals[0]);
                    tempWriteSingleTags2.Insert(valuestartindex + 1, wvals[1]);
             
                    break;
                case "int":
                    tempWriteSingleTags2[typestartindex] = 0xC4;
                    tempWriteSingleTags2[typestartindex + 1] = 0x00;
                    int tempintval = Convert.ToInt32(wval);
                    //p2.6 修改写入的值
                    wvals = BitConverter.GetBytes(tempintval);
                    tempWriteSingleTags2.Insert(valuestartindex, wvals[0]);
                    tempWriteSingleTags2.Insert(valuestartindex + 1, wvals[1]);
                    tempWriteSingleTags2.Insert(valuestartindex+2, wvals[2]);
                    tempWriteSingleTags2.Insert(valuestartindex + 3, wvals[3]);
                    break;
                case "long":
                    tempWriteSingleTags2[typestartindex] = 0xC5;
                    tempWriteSingleTags2[typestartindex + 1] = 0x00;
                    long templongval = Convert.ToInt64(wval);
                    //p2.6 修改写入的值
                    wvals = BitConverter.GetBytes(templongval);
                    tempWriteSingleTags2.Insert(valuestartindex, wvals[0]);
                    tempWriteSingleTags2.Insert(valuestartindex + 1, wvals[1]);
                    tempWriteSingleTags2.Insert(valuestartindex + 2, wvals[2]);
                    tempWriteSingleTags2.Insert(valuestartindex + 3, wvals[3]);
                    tempWriteSingleTags2.Insert(valuestartindex + 4, wvals[4]);
                    tempWriteSingleTags2.Insert(valuestartindex + 5, wvals[5]);
                    tempWriteSingleTags2.Insert(valuestartindex + 6, wvals[6]);
                    tempWriteSingleTags2.Insert(valuestartindex + 7, wvals[7]);
                    break;
                case "float":
                    tempWriteSingleTags2[typestartindex] = 0xCA;
                    tempWriteSingleTags2[typestartindex + 1] = 0x00;
                    float tempfloatval = Convert.ToSingle(wval);
                    //p2.6 修改写入的值
                    wvals = BitConverter.GetBytes(tempfloatval);
                    tempWriteSingleTags2.Insert(valuestartindex, wvals[0]);
                    tempWriteSingleTags2.Insert(valuestartindex + 1, wvals[1]);
                    tempWriteSingleTags2.Insert(valuestartindex + 2, wvals[2]);
                    tempWriteSingleTags2.Insert(valuestartindex + 3, wvals[3]);
                    break;
                case "double":
                    tempWriteSingleTags2[typestartindex] = 0xCB;
                    tempWriteSingleTags2[typestartindex + 1] = 0x00;
                    double tempdoubleval = Convert.ToDouble(wval);
                    //p2.6 修改写入的值
                    wvals = BitConverter.GetBytes(tempdoubleval);
                    tempWriteSingleTags2.Insert(valuestartindex, wvals[0]);
                    tempWriteSingleTags2.Insert(valuestartindex + 1, wvals[1]);
                    tempWriteSingleTags2.Insert(valuestartindex + 2, wvals[2]);
                    tempWriteSingleTags2.Insert(valuestartindex + 3, wvals[3]);
                    tempWriteSingleTags2.Insert(valuestartindex + 4, wvals[4]);
                    tempWriteSingleTags2.Insert(valuestartindex + 5, wvals[5]);
                    tempWriteSingleTags2.Insert(valuestartindex + 6, wvals[6]);
                    tempWriteSingleTags2.Insert(valuestartindex + 7, wvals[7]);
                    break;
                case "string":
                    tempWriteSingleTags2[typestartindex] = 0xD0;
                    tempWriteSingleTags2[typestartindex + 1] = 0x00;
                    string tempstringval = wval.ToString();
                    short stringlen = (short)tempstringval.Length;
                    //p2.6 修改写入的值
                    wvals = Encoding.UTF8.GetBytes(tempstringval);
                    if (stringlen%2!=0)
                    {
                        var tempwvals = wvals.ToList();
                        tempwvals.Add(0);
                        wvals = tempwvals.ToArray();
                    }
                    for (int i = 0; i < wvals.Length; i++)
                    {
                        tempWriteSingleTags2.Insert(valuestartindex+i, wvals[i]);
                    }
                    byte[] buffer = BitConverter.GetBytes(wvals.Length);
                    tempWriteSingleTags2.Insert(tempWriteSingleTags2.Count- wvals.Length-4, buffer[0]);
                    tempWriteSingleTags2.Insert(tempWriteSingleTags2.Count - wvals.Length - 4, buffer[1]);
                    break;
                default:
                    break;
            }
    
            byte[] valslen = BitConverter.GetBytes((short)(tempWriteSingleTags2.Count));
            // p1.3  修改第一部分指示的长度
            tempWriteSingleTags[tempWriteSingleTags.Length - 2] = valslen[0];
            tempWriteSingleTags[tempWriteSingleTags.Length - 1] = valslen[1];
            byte[] valslen2 = BitConverter.GetBytes((short)(tempWriteSingleTags.Length+ tempWriteSingleTags2.Count-24));
            // p1.1  修改第一部分的长度
            tempWriteSingleTags[2] = valslen2[0];
            tempWriteSingleTags[3] = valslen2[1];
            //p2.1 修改长度
            short  len3= (short)(tempWriteSingleTags2.Count - 10 - 4);
            byte[] valslen3   =BitConverter.GetBytes(len3);
            tempWriteSingleTags2[8]= valslen3[0];
            tempWriteSingleTags2[9]= valslen3[1];
            var tempWriteSingleTagsALL = tempWriteSingleTags.Concat(tempWriteSingleTags2).ToArray();
            var str = string.Join(" ", tempWriteSingleTagsALL.Select(y => y.ToString("X2")));
            AddMSg("-->" + str);
            tcpClient.Send(tempWriteSingleTagsALL, tempWriteSingleTagsALL.Length);
            Thread.Sleep(50);
        }
        private DelegateCommand _TestReadTwoTagsCommand3;
        public DelegateCommand TestReadTwoTagsCommand3 =>
            _TestReadTwoTagsCommand3 ?? (_TestReadTwoTagsCommand3 = new DelegateCommand(ExecuteTestReadCommand3));
        /// <summary>
        /// 4 读取多个
        /// </summary>
        void ExecuteTestReadCommand3()
        {  
            //修改会话id
            for (int i = 0; i < 4; i++)
            {
                ReadTwoTags[4 + i] = headerId[i];
            }
 
            var str = string.Join(",", ReadTwoTags.Select(y => y.ToString("X2")));
            AddMSg("-->" + str);
            tcpClient.Send(ReadTwoTags, ReadTwoTags.Length);
            Thread.Sleep(50);
        }
 
    
        private void ChecktcpClient(TcpClient tcpClient)
        {
            if (tcpClient.IsConnected==false)
            {
                tcpClient.Connect();
                ExecuteSendCommand1();
            }
        }
    }
}
测试 读取多个标签失败,可能模拟器不支持同时读取单次2个及以上tag


 
                    
                     
                    
                 
                    
                
 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号