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都要再反转,也会增加开销。这就要看存储协议和运算程序的配合了,如果相同最快,如果不同就要反转,会变慢。比如如果你的程序主要就是处理网络基础数据包,大端肯定有优势。还有一种情况就是原子操作,在一些极端追求性能的特殊情况中,原子操作非常重要,如果运算结果需要反转就破坏了原子性,当然这跟具体程序有关,到底大端要转还是小端药转不好说。上面说的三种情况问题基本都是面对差异的问题,不是哪个好的问题,最坏的情况就是有两种端的情况。



