.netCore环境下使用ModbusTCP读取PLC数据, 大小端的几种转换方法

NModbus4有.NetCore版本 此为.netCore3.1

依赖项

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.22" />
    <PackageReference Include="NModbus4.NetCore" Version="1.0.1" />
	  <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
	  <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
	  <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
	  <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
	  <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
	  <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.1.0" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.22" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="3.1.22" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.22" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.22">
		  <PrivateAssets>all</PrivateAssets>
		  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	  
  </ItemGroup>

  <ItemGroup>
    <None Update="Ipcfg.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>

  <ItemGroup>
    <Folder Include="DataConvertLib\" />
  </ItemGroup>

</Project>

ModbusTCP Helper

using Modbus.Device;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using thinger.DataConvertLib;

namespace NModbusCoreDemon
{
    public class NModBusHelper
    {
        private TcpClient tcpClient = null;
        private ModbusIpMaster master;

        public bool Connect(string ip, string port)
        {
            try
            {
                tcpClient = new TcpClient();
                tcpClient.Connect(IPAddress.Parse(ip), int.Parse(port));
                master = ModbusIpMaster.CreateIp(tcpClient);
            }
            catch (Exception)
            {
                return false;
            }
            
            return tcpClient.Connected;
        }

        public bool Disconnect()
        {
            if (tcpClient != null)
            {
                tcpClient.Close();
                return true;
            }
            else
            {
                return false;
            }
        }

        public byte[] ReadKeepRegByteArr(string iAddress, string iLength)//偏移量,寄存器数量
        {
            try
            {
                ushort[] des = master.ReadHoldingRegisters(ushort.Parse(iAddress), ushort.Parse(iLength));
                byte[] res = ByteArrayLib.GetByteArrayFromUShortArray(des);
                return res;
            }
            catch (Exception)
            {
                return null;
            }
        }

        public ushort[] ReadKeepRegUshort(string iAddress, string iLength)//偏移量,寄存器数量
        {
            try
            {
                ushort[] des = master.ReadHoldingRegisters(ushort.Parse(iAddress), ushort.Parse(iLength));
                //byte[] res = ByteArrayLib.GetByteArrayFromUShortArray(des);
                return des;
            }
            catch (Exception)
            {
                return null;
            }
        }

        public List<float> AnalyseData_4x(ushort[] des, string iAddress)
        {
            int StartByte;
            StartByte = int.Parse(iAddress) * 2;
            List<float> floatArray = new List<float>();
            byte[] byteArray = ByteArrayLib.GetByteArrayFromUShortArray(des);

            for (int i = StartByte; i < byteArray.Length; i += 4)
            {
                floatArray.Add(FloatLib.GetFloatFromByteArray(byteArray, i));
            }
            return floatArray;
        }
    }
}


主界面UI

using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using thinger.DataConvertLib;
using System.Linq;
using System.IO;

namespace NModbusCoreDemon
{
    public partial class FrmMain : Form
    {
        public FrmMain()
        {
            InitializeComponent();

            this.Load += FrmMain_Load;
        }

        private void FrmMain_Load(object sender, EventArgs e)
        {
            cfgBuilder.AddJsonFile("Ipcfg.json", optional: true, reloadOnChange: true);
            IConfigurationRoot configRoot = cfgBuilder.Build();
            this.ip = configRoot.GetSection("Ip").Value;
            this.port = configRoot.GetSection("Port").Value;
            //str = Directory.GetCurrentDirectory();
        }

        private ConfigurationBuilder cfgBuilder = new ConfigurationBuilder();
        public string ip;
        public string port;
        private CancellationTokenSource cts = new CancellationTokenSource();
        private bool IsConnected = false;
        ushort[] res;
        string iAddress = "0";
        string iLenth = "10"; //寄存器个数
        private List<float> floatList = new List<float>();


        private NModBusHelper objTcp = new NModBusHelper();


        //设置ListBox
        private void Addinfo(string info)
        {
            this.isInfo.Items.Insert(
                0, DateTime.Now.ToString("HH:mm:ss") + " " + info + Environment.NewLine);
        }


        private void btnConn_Click(object sender, EventArgs e)
        {
            IsConnected = objTcp.Connect(ip, port);
            Addinfo(IsConnected ? "连接成功" : "连接失败");
        }

        private void btnDisConn_Click(object sender, EventArgs e)
        {
            objTcp.Disconnect();
            Addinfo("断开连接");
        }

        private void btnRead_Click(object sender, EventArgs e)
        {
            Addinfo("开始读取整数");
            Task.Run(async () =>
            {
                while (!cts.IsCancellationRequested)
                {
                    await Task.Delay(500);
                    res = objTcp.ReadKeepRegUshort(iAddress, iLenth);

                    for (int i = 0; i < res.Length; i++)
                    {
                        this.isInfo.Invoke(new Action<ushort>(t =>
                        {
                            this.isInfo.Items.Add(t.ToString());
                        }), res[i]);
                    }
                }
            }, cts.Token);
        }

        private void btnReadFloat_Click(object sender, EventArgs e)
        {
            Addinfo("开始读取浮点数");
            Task.Run(async () =>
            {
                while (!cts.IsCancellationRequested)
                {
                    await Task.Delay(500);
                    res = objTcp.ReadKeepRegUshort(iAddress, iLenth);
                    floatList.AddRange(objTcp.AnalyseData_4x(res, iAddress));
                    for (int i = 0; i < floatList.Count; i++)
                    {
                        this.isInfo.Invoke(new Action<float>(t =>
                        {
                            this.isInfo.Items.Add(t.ToString());
                        }), floatList.ToArray()[i]);
                    }
                }
            }, cts.Token);
        }

        //大小端的方法:西门子等欧系PLC都是大端在前

        List<ushort> barr2 = new List<ushort>();
        //第一种方法
        private void btn1_Click(object sender, EventArgs e)
        {
            byte[] barr = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
            for (int i = 0; i < barr.Length; i+=2)
            {
                byte tmpByte = barr[i+1];
                barr[i + 1] = barr[i];
                barr[i] = tmpByte;
            }

            for (int i = 0; i < barr.Length; i++)
            {
                this.isInfo.Invoke(new Action<byte>(t =>
                {
                    this.isInfo.Items.Add(t.ToString());
                }), barr[i]);
            }
        }

        //第二种方法
        private void btn2_Click(object sender, EventArgs e)
        {
            byte[] barr = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
            //barr2.AddRange(barr);
            byte[] byteArr;
            for (int i = 0; i < barr.Length; i += 2)
            {
                barr2.Add(Convert.ToUInt16(barr[i]  + barr[i + 1] * 256));

            }
            byteArr = ByteArrayLib.GetByteArrayFromUShortArray(barr2.ToArray());
            for (int i = 0; i < byteArr.Length; i++)
            {
                this.isInfo.Invoke(new Action<ushort>(t =>
                {
                    this.isInfo.Items.Add(t.ToString());
                }), byteArr[i]);
            }
        }

        private void btnInsert_Click(object sender, EventArgs e)
        {
            using (MyDbContext dbContext = new MyDbContext())
            {
                ActualData actualData = new ActualData()
                {
                    Description = "测试Description1",
                    Value = "23345",
                    InsertTime = DateTime.UtcNow
                };

                dbContext.ActualDatas.Add(actualData);
                dbContext.SaveChanges();

            }


        }
    }
}


json文件

{
  "Ip": "127.0.0.1",
  "Port": "502",
  "ConnectionStrings": {
    "SqliteConnectionString": "Data Source=E:\\Csharp\\NModbusCoreDemon\\NModbusCoreDemon\\bin\\Debug\\netcoreapp3.1\\Database\\DbSqlite.db",
    "MySQLConnectionString": "server=192.168.85.102; database=OneToMany; uid=root; pwd=123456;"
  }

}

实体类:

using System;
using System.Collections.Generic;
using System.Text;

namespace NModbusCoreDemon
{
    public class ActualData
    {
        public long Id { get; set; }
        public string Description { get; set; }
        public string Value { get; set; }
        public DateTime InsertTime { get; set; }

    }
}

using System;
using System.Collections.Generic;
using System.Text;

namespace NModbusCoreDemon
{
    public class Variable
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string Type { get; set; }
        public string VarAddress { get; set; }
        public string Scale { get; set; }
        public string Offset { get; set; }
        public string Start { get; set; }
        public string AccessProperty { get; set; }
        public string AlarmEnable { get; set; }
        public string ArchiveEnable { get; set; }
        public string SetLimitEnable { get; set; }
        public string AlarmType { get; set; }
        public string DiscreteAlarmType { get; set; }
        public string DiscreteAlarmPriority { get; set; }
        public string DiscreteAlarmNote { get; set; }
        public string LoLoAlarmEnable { get; set; }
        public string LoLoAlarmValue { get; set; }
        public string LoLoAlarmPriority { get; set; }
        public string LoLoAlarmNote { get; set; }
        public string LowAlarmEnable { get; set; }
        public string LowAlarmValue { get; set; }
        public string LowAlarmPriority { get; set; }
        public string LowAlarmNote { get; set; }
        public string HighAlarmEnable { get; set; }
        public string HighAlarmValue { get; set; }
        public string HighAlarmPriority { get; set; }
        public string HighAlarmNote { get; set; }
        public string HiHiAlarmEnable { get; set; }
        public string HiHiAlarmValue { get; set; }
        public string HiHiAlarmPriority { get; set; }
        public string HiHiAlarmNote { get; set; }
        public string ArchivePeriod { get; set; }
        public string SetLimitMax { get; set; }
        public string SetLimitMin { get; set; }
        public string VarType { get; set; }
    }
}

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.Text;

namespace NModbusCoreDemon
{
    public class ActualDataConfig : IEntityTypeConfiguration<ActualData>
    {
        public void Configure(EntityTypeBuilder<ActualData> builder)
        {
            builder.ToTable("ActualData");
            builder.HasKey(a => a.Id);
            builder.Property(a => a.Description).HasMaxLength(100);
        }
    }
}

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.Text;

namespace NModbusCoreDemon
{
    public class VariableConfig : IEntityTypeConfiguration<Variable>
    {
        public void Configure(EntityTypeBuilder<Variable> builder)
        {
            builder.ToTable("Variable");
            builder.HasKey(v => v.Id);
            builder.Property(v => v.Description).HasMaxLength(100);
        }
    }
}

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Sqlite;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;

namespace NModbusCoreDemon
{
    public class MyDbContext:DbContext
    {
        public DbSet<ActualData> ActualDatas { get; set; }
        public DbSet<Variable> Variables { get; set; }

        private ConfigurationBuilder cfgBuilder = new ConfigurationBuilder();

        //private IConfiguration configuration;

        //private string connString;

        public MyDbContext()
        {
            //configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("Ipcfg.json").Build();
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            cfgBuilder.AddJsonFile("Ipcfg.json", optional: true, reloadOnChange: true);
            IConfigurationRoot configRoot = cfgBuilder.Build();
            string connString = configRoot.GetSection("ConnectionStrings:SqliteConnectionString").Value;

            optionsBuilder.UseSqlite(connString);
            //configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("Ipcfg.json").Build();
            
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            //var iniActualData = File.ReadAllText(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"/Database/Variable.json");
            //IList<ActualData> iniActualList = JsonConvert.DeserializeObject<IList<ActualData>>(iniActualData);
            //modelBuilder.Entity<ActualData>().HasData(iniActualList);

            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }
}

效果
在这里插入图片描述

发那科大小端转换程序

0001 IF # 接收完成信号 THEN
0002 FOR #Tmp := 0 TO (#Receive_Word_Num - 1) BY 2 DO
0003 #Tmp_Byte := #Buffer[#Tmp + 1];
0004 #Buffer[#Tmp + 1] := #Buffer[#Tmp];
0005 #Buffer[#Tmp] := #Tmp_Byte;
0006 END_FOR;
0007 #POS := 0;
0008 #Err := Deserialize(SRC_ARRAY := #Buffer, DEST_VARIABLE => #Receive_Data, POS := #POS);
0009 IF (#Err <> 0) THEN
0010 #ERROR := -30;

0011 RETURN;
0012 END_IF;
0013 END_IF;
0014
网 络 70001 #POS := 0;
0002 #Err := Serialize(SRC_VARIABLE := #Send_Data, DEST_ARRAY => #Buffer, POS := #POS);
0003 IF (#Err <> 0) THEN
0004 #ERROR := -40;
0005 RETURN;
0006 END_IF;
0007
0008 FOR #Tmp := 0 TO (#Send_Word_Num - 1) BY 2 DO
0009 #Tmp_Byte := #Buffer[#Tmp + 1];
0010 #Buffer[#Tmp + 1] := #Buffer[#Tmp];
0011 #Buffer[#Tmp] := #Tmp_Byte;
0012 END_FOR;
        [Description("将Float数值转换成字节数组")]
        public static byte[] GetByteArrayFromFloat(float value, DataFormat dataFormat = DataFormat.ABCD)
        {
            byte[] resTemp = BitConverter.GetBytes(value);

            byte[] res = new byte[4];

            switch (dataFormat)
            {
                case DataFormat.ABCD:
                    res[0] = resTemp[3];
                    res[1] = resTemp[2];
                    res[2] = resTemp[1];
                    res[3] = resTemp[0];
                    break;
                case DataFormat.CDAB:
                    res[0] = resTemp[1];
                    res[1] = resTemp[0];
                    res[2] = resTemp[3];
                    res[3] = resTemp[2];
                    break;
                case DataFormat.BADC:
                    res[0] = resTemp[2];
                    res[1] = resTemp[3];
                    res[2] = resTemp[0];
                    res[3] = resTemp[1];
                    break;
                case DataFormat.DCBA:
                    res = resTemp;
                    break;
            }
            return res;
        }
        [Description("将UShort类型数值转换成字节数组")]
        public static byte[] GetByteArrayFromUShort(ushort value, DataFormat dataFormat = DataFormat.ABCD)
        {
            byte[] resTemp = BitConverter.GetBytes(value);

            byte[] res = new byte[2];

            switch (dataFormat)
            {
                case DataFormat.ABCD:
                case DataFormat.CDAB:
                    res[0] = resTemp[1];
                    res[1] = resTemp[0];
                    break;
                case DataFormat.BADC:
                case DataFormat.DCBA:
                    res = resTemp;
                    break;
                default:
                    break;
            }
            return res;
        }
        [Description("将Double类型数值转换成字节数组")]
        public static byte[] GetByteArrayFromDouble(double value, DataFormat dataFormat = DataFormat.ABCD)
        {
            byte[] resTemp = BitConverter.GetBytes(value);

            byte[] res = new byte[8];

            switch (dataFormat)
            {
                case DataFormat.ABCD:
                    res[0] = resTemp[7];
                    res[1] = resTemp[6];
                    res[2] = resTemp[5];
                    res[3] = resTemp[4];
                    res[4] = resTemp[3];
                    res[5] = resTemp[2];
                    res[6] = resTemp[1];
                    res[7] = resTemp[0];
                    break;
                case DataFormat.CDAB:
                    res[0] = resTemp[1];
                    res[1] = resTemp[0];
                    res[2] = resTemp[3];
                    res[3] = resTemp[2];
                    res[4] = resTemp[5];
                    res[5] = resTemp[4];
                    res[6] = resTemp[7];
                    res[7] = resTemp[6];
                    break;
                case DataFormat.BADC:
                    res[0] = resTemp[6];
                    res[1] = resTemp[7];
                    res[2] = resTemp[4];
                    res[3] = resTemp[5];
                    res[4] = resTemp[2];
                    res[5] = resTemp[3];
                    res[6] = resTemp[0];
                    res[7] = resTemp[1];
                    break;
                case DataFormat.DCBA:
                    res = resTemp;
                    break;
            }
            return res;
        }

在这里插入图片描述

X86是小端,大家上学时候学计算机原里大多也是学的小端,一些大佬坚持自己的观点,arm,power,mips早期都坚持大端,但是也许是为了怕作死,也做了小端。小端的好处是学计算机原里的时候大多都学的这个,而且无论大端小端的cpu,1个字节内的顺序都是小端,比较符合一般初学者思维惯性。大端的好处是网络通信的TCP/IP协议中字节序就是按照大端规定的。x86的cpu处理所有网络数据包时都要先将数字进行字节序反转才能进行运算,发送时也要先反转才能发送,增加了一点计算开销。如果你是大尾cpu就不需要反转了。最坏的情况是,现实中者两种字节序我们都必然要用到。关于性能的一些思考在一些情况下,比如b-tree,tire tree等从一端开始逐bit运算的算法中,根据数据的离散情况不同,字节序会很大程度影响运算速度。还有一种情况就是IO序和内序如果不同,每次读取IO数据都要反转,写入IO都要再反转,也会增加开销。这就要看存储协议和运算程序的配合了,如果相同最快,如果不同就要反转,会变慢。比如如果你的程序主要就是处理网络基础数据包,大端肯定有优势。还有一种情况就是原子操作,在一些极端追求性能的特殊情况中,原子操作非常重要,如果运算结果需要反转就破坏了原子性,当然这跟具体程序有关,到底大端要转还是小端药转不好说。上面说的三种情况问题基本都是面对差异的问题,不是哪个好的问题,最坏的情况就是有两种端的情况。

在这里插入图片描述
在这里插入图片描述
序列化的目标只能是byte数组
反序列化的源也只能是byte数组

#pos := 0;
#ret := Deserialize(SRC_ARRAY := #arrPGNO, DEST_VARIABLE => #RPGNO, POS := #pos);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潘诺西亚的火山

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值