栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

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

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

依赖项



  
    WinExe
    netcoreapp3.1
    true
  

  
    
    
	  
	  
	  
	  
	  
	  
	  
	  
	  
	  
		  all
		  runtime; build; native; contentfiles; analyzers; buildtransitive
	  
	  
  

  
    
      PreserveNewest
    
  

  
    
  


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 AnalyseData_4x(ushort[] des, string iAddress)
        {
            int StartByte;
            StartByte = int.Parse(iAddress) * 2;
            List floatArray = new List();
            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 floatList = new List();


        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(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(t =>
                        {
                            this.isInfo.Items.Add(t.ToString());
                        }), floatList.ToArray()[i]);
                    }
                }
            }, cts.Token);
        }

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

        List barr2 = new List();
        //第一种方法
        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(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(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
    {
        public void Configure(EntityTypeBuilder 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
    {
        public void Configure(EntityTypeBuilder 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 ActualDatas { get; set; }
        public DbSet 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 iniActualList = JsonConvert.DeserializeObject(iniActualData);
            //modelBuilder.Entity().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
网 络 7:
0001 #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都要再反转,也会增加开销。这就要看存储协议和运算程序的配合了,如果相同最快,如果不同就要反转,会变慢。比如如果你的程序主要就是处理网络基础数据包,大端肯定有优势。还有一种情况就是原子操作,在一些极端追求性能的特殊情况中,原子操作非常重要,如果运算结果需要反转就破坏了原子性,当然这跟具体程序有关,到底大端要转还是小端药转不好说。上面说的三种情况问题基本都是面对差异的问题,不是哪个好的问题,最坏的情况就是有两种端的情况。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/913176.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号