经测试 已经成功。
先写ModbusTCP类库
using Modbus.Device;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using thinger.cn.DataConvertHelper;
namespace DAL
{
public class NmodbusTCPhelper
{
private TcpClient tcpClient = null;
private ModbusIpMaster master;
#region 打开与关闭Socket
public bool Connect(string ip, string port)
{
try
{
tcpClient = new TcpClient();
tcpClient.Connect(IPAddress.Parse(ip), Convert.ToInt32(port));
master = ModbusIpMaster.CreateIp(tcpClient);
}
catch (Exception)
{
return false;
}
return true;
}
public bool Disconnect()
{
if (tcpClient!=null)
{
tcpClient.Close();
return true;
}
else
{
return false;
}
}
#endregion
#region 读4区寄存器
///
///
///
/// 偏移量
/// 寄存器个数
///
public byte[] ReedKeepReg(int iAdress, int iLength)
{
try
{
ushort[] des = master.ReadHoldingRegisters(Convert.ToUInt16(iAdress), Convert.ToUInt16(iLength));
byte[] res = ByteArrayLib.GetByteArrayFromUShortArray(des,DataFormat.BADC);//ABCD,BADC,CDAB
return res;
}
catch (Exception)
{
return null;
}
}
#endregion
#region 读取一区数据
public byte[] ReedCoilReg(int iAdress, int iLength)
{
try
{
bool[] des = master.ReadCoils(Convert.ToUInt16(iAdress), Convert.ToUInt16(iLength));
byte[] res = ByteArrayLib.GetByteArrayFromBoolArray(des);
return res;
}
catch (Exception)
{
return null;
}
}
#endregion
#region 预置保持寄存器
public bool PreSetFloatReg(int iAdress, ushort[] data)
{
try
{
master.WriteMultipleRegisters(Convert.ToUInt16(iAdress), data);
return true;
}
catch (Exception)
{
return false;
}
}
#endregion
}
}
写静态类记录数据
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.IO;
using DAL;
using System.Text.Json;
using System.Windows;
namespace ShenhuaSQLite
{
public static class CommonMethods
{
#region 定义通信实体信息
//从站地址
public static int Address = 0;
//获取到的modbus信息, 实体变量
public static ModbusTCPEntity objModbusTCPEntity = new ModbusTCPEntity();
//变量信息结合
public static List<Variable_Modbus> VarModbusList = new List<Variable_Modbus>();
//报警变量集合
public static List<VarAlarm_Modbus> VarAlarmModbusList = new List<VarAlarm_Modbus>();
//存储区域集合
public static List<StoreArea> StoreAreaList = new List<StoreArea>();
//变量名和数值的字典集合 存储实时数据
public static Dictionary<string, string> CurrentValue = new Dictionary<string, string>();
//变量名和地址的字典集合
public static Dictionary<string, string> CurrentVarAddress = new Dictionary<string, string>();
//归档变量集合
public static List<Variable_Modbus> FileVarModbusList = new List<Variable_Modbus>();
//数据报表变量集合
public static List<Variable_Modbus> ReportVarModbusList = new List<Variable_Modbus>();
//创建ModbusTCP实例
public static NmodbusTCPhelper objModTCP = new NmodbusTCPhelper();
public static TreadTCP treadTCP = new TreadTCP();
//TCP标志位
public static bool TCPisConnected = false;
//TCP首次连接标志位
public static bool FirstConnect = true;
//创建数据写入的标志位 如果在写 就不能读
public static bool IsWriting = false;
//趋势缓冲区 集合的集合
public static List<List<Cache>> CacheList = new List<List<Cache>>();
//定义缓冲区大小(每秒一条)
public static int CacheCount = 600;//10分钟, 每秒一条
//定义变量名称和注释的键值对
public static Dictionary<string, string> CurrentVarNote = new Dictionary<string, string>();
//定义所有变量名称的集合
public static List<string> FileVarNameList = new List<string>();
//0x区域的变量列表
public static List<Variable_Modbus> List_0x = new List<Variable_Modbus>();
//1x区域的变量列表
public static List<Variable_Modbus> List_1x = new List<Variable_Modbus>();
//3x区域的变量列表
public static List<Variable_Modbus> List_3x = new List<Variable_Modbus>();
//4x区域的变量列表
public static List<Variable_Modbus> List_4x = new List<Variable_Modbus>();//用来存储实时数据
//public static TreadTCP treadTCP = new TreadTCP();
#endregion
#region 一种读取ini配置文件的方法
public static ModbusTCPEntity GetConfigTCP(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open);
StreamReader sr = new StreamReader(fs, Encoding.Default);
List<string> list = new List<string>();
while (true)
{
string str = sr.ReadLine();
if (str == null) break;
list.Add(str);
}
ModbusTCPEntity objModbusTCP = new ModbusTCPEntity();
if (list.Count == 2)
{
objModbusTCP.Ip = list[0];
objModbusTCP.Port = list[1];
}
return objModbusTCP;
}
#endregion
#region 加载Modbus变量的 json文件
public static List<Variable_Modbus> LoadJsonVarible(string JsonPath)
{
List<Variable_Modbus> VarModbusList = null;
FileStream fs = new FileStream(JsonPath, FileMode.Open);
JsonDocument doc = JsonDocument.Parse(fs);
if (!File.Exists(JsonPath))
{
MessageBox.Show("IO配置变量的Json文件不存在!");
}
else
{
VarModbusList = new List<Variable_Modbus>();
foreach (var item in doc.RootElement.GetProperty("Root").GetProperty("Variable").EnumerateArray())
{
Variable_Modbus objVar = new Variable_Modbus();//要在循环中实例化接收
objVar.VarName = item.GetProperty("VarName").GetString();
objVar.Address = item.GetProperty("Address").GetString();
objVar.DataType = item.GetProperty("DataType").GetString();
objVar.StoreArea = item.GetProperty("StoreArea").GetString();
objVar.IsFiling = item.GetProperty("IsFiling").GetString();
objVar.IsAlarm = item.GetProperty("IsAlarm").GetString();
objVar.IsReport = item.GetProperty("IsReport").GetString();
objVar.Note = item.GetProperty("Note").GetString();
objVar.AbsoluteAddress = item.GetProperty("AbsoluteAddress").GetString();
VarModbusList.Add(objVar);
}
}
return VarModbusList;
}
#endregion
#region 加载StoreType变量的 json文件
public static List<StoreArea> LoadJsonStoreType(string JsonPath)
{
List<StoreArea> storeAreaList = null;
FileStream fs = new FileStream(JsonPath, FileMode.Open);
JsonDocument doc = JsonDocument.Parse(fs);
if (!File.Exists(JsonPath))
{
MessageBox.Show("IO配置变量的Json文件不存在!");
}
else
{
storeAreaList = new List<StoreArea>();
foreach (var item in doc.RootElement.GetProperty("Root").GetProperty("StoreArea").EnumerateArray())
{
StoreArea storeArea = new StoreArea(); ;//要在循环中实例化接收
storeArea.StoreType = item.GetProperty("StoreType").GetString();
storeArea.Length = Convert.ToInt32(item.GetProperty("Length").GetString());
storeArea.StartReg = Convert.ToInt32(item.GetProperty("StartReg").GetString());
storeAreaList.Add(storeArea);
}
}
return storeAreaList;
}
#endregion
}
}
写多线程函数
using DAL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using thinger.cn.DataConvertHelper;
namespace ShenhuaSQLite
{
public class TreadTCP
{
public static int a;
//起始地址
int StartAddress = 0;
//错误次数
public int ErrorTimer { get; set; } = 0;
public int MaxErrorTimer { get; set; } = 3;
public bool FirstConnect { get; set; } = true;
public bool IsConnected { get; set; } = true;
#region 实时读取
public void Communication()
{
foreach (StoreArea item in CommonMethods.StoreAreaList)
{
byte[] Res = null;
int Start = item.StartReg;//偏移量 StartReg="0"
List<byte> byteList = null;
switch (item.StoreType)
{
case "01 Coil Status(0x)":
byteList = new List<byte>();
Res = CommonMethods.objModTCP.ReedCoilReg(Start, item.Length);
if (Res != null)
{
this.ErrorTimer = 0;
byteList.AddRange(Res);
}
else
{
this.ErrorTimer += 1;
if (this.ErrorTimer>= this.MaxErrorTimer)
{
this.IsConnected=false;
}
}
//item.Length :线圈数,除以8得到字节数
int length = item.Length % 8 == 0 ? item.Length / 8 : item.Length / 8 + 1;
if (byteList.Count == length) //ushortList.Count=116,寄存器个数
{
}
break;
case "03 Holding Register(4x)":
byteList = new List<byte>();
Res = CommonMethods.objModTCP.ReedKeepReg(Start, item.Length);
if (Res != null)
{
this.ErrorTimer = 0;
byteList.AddRange(Res);
}
else
{
ErrorTimer += 1;
if (this.ErrorTimer >= this.MaxErrorTimer)
{
this.IsConnected = false;
}
}
//item.Length :116个 寄存器,232个字节 ,一个寄存器是16位,2个字节
a = byteList.Count;
if (byteList.Count == 2*item.Length)
{
AnalyseData_4x(byteList);
}
break;
default:
break;
}
}
}
#endregion
#region 解析4区数据
///
/// 解析4区Float数据
///
/// 接收16位无符号整型数组
/// 接收偏移量
///
public void AnalyseData_4x(List<byte> byteList)
{
int StartByte;
byte[] Res = null;
ushort[] Ures = null;
if (byteList != null && byteList.Count>0)
{
foreach (Variable_Modbus item in CommonMethods.List_4x)
{
switch (item.DataType)
{
case "Float":
//每个寄存器为0-65535,是2的16次方,16位,2个字节
//每个数据占2个寄存器,即4个字节,
//浮点数 占满2个寄存器,即4个字节,32位。
//在232个字节数组中,则第一个浮点数占位情况为为从第0到第4个字节
//Address="0","2"..."114", StartByte=0,4,...,228。 总共232个字节
StartByte = int.Parse(item.Address) * 2;
byte[] byteLib = byteList.ToArray();
float floatLib = FloatLib.GetFloatFromByteArray(byteLib, StartByte);
CommonMethods.CurrentValue[item.VarName] = Convert.ToDouble(floatLib).ToString("f1"); //floatlib.ToString()
break;
case "Unsigned":
//StartByte = int.Parse(item.Address) * 2;
//Res = new byte[2] { byteList[StartByte], byteList[StartByte + 1] };
Ures = UShortLib.GetUShortArrayFromByteArray(byteList.ToArray());
StartByte = int.Parse(item.Address);
CommonMethods.CurrentValue[item.VarName] = Ures[StartByte].ToString();
break;
default:
break;
}
}
}
}
#endregion
}
}
在主窗体的ViewModel中加载多线程函数和写入数据库函数
using DAL;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using ShenhuaSQLite.View;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace ShenhuaSQLite.ViewModel
{
///
/// This class contains properties that the main View can data bind to.
///
/// Use the mvvminpc snippet to add bindable properties to this ViewModel.
///
///
/// You can also use Blend to data bind with the tool's support.
///
///
/// See http://www.galasoft.ch/mvvm
///
///
public class MainViewModel : ViewModelBase
{
public AppData appData { get; private set; } = AppData.Instance;
public MainViewModel()
{
InitialTxt();
IniTaskModbus();
}
///
/// 判断传入参数是否为RadioButton,经过选择判断切换到相应页面
/// 使用container.Content接收用户控件页面
///
public RelayCommand<RadioButton> SelectViewCommand
{
get
{
return new RelayCommand<RadioButton>((t) =>
{
if (!(t is RadioButton button))
{
return;
}
if (string.IsNullOrEmpty(button.Content.ToString()))
{
return;
}
switch (t.Content.ToString())// button 可以代替
{
case "首页": AppData.Instance.MainWindow.container.Content = new IndexView(); break;
case "控制流程": AppData.Instance.MainWindow.container.Content = new ReportDataView(); break;
//case "硬件组态": AppData.Instance.MainWindow.container.Content = new CargoView(); break;
case "历史趋势": AppData.Instance.MainWindow.container.Content = new IndexView(); break;
case "数据报表": AppData.Instance.MainWindow.container.Content = new ActualDataView(); break;
case "参数设置": AppData.Instance.MainWindow.container.Content = new IndexView(); break;
default:
break;
}
});
}
}
#region 读取配置文件信息
//定义根目录路径
private static string FilePath = System.IO.Directory.GetCurrentDirectory() + "\\ConfigFile\\";
//读取配置文件路径,ini要是ANSI格式
SettingManager IniManager = new SettingManager(FilePath + "MODBUSTCP_.ini");
private CancellationTokenSource cts = new CancellationTokenSource();
//插入数据库
InsertDataSQLite objInsert = new InsertDataSQLite(10000);//10秒1次
#endregion
#region Socket 操作
private bool OpenTCP(ModbusTCPEntity modbusTCP)
{
return CommonMethods.objModTCP.Connect(modbusTCP.Ip, modbusTCP.Port);
}
private bool CloseTCP(ModbusTCPEntity modbusTCP)
{
return CommonMethods.objModTCP.Disconnect();
}
#endregion
#region 初始化信息
private void InitialTxt()
{
//第一步: 加载通讯接口信息
CommonMethods.objModbusTCPEntity = IniManager.LoadSysSettings();
//第二步: 变量集合信息
CommonMethods.VarModbusList = CommonMethods.LoadJsonVarible(FilePath + "Variable_Modbus.json");
CommonMethods.StoreAreaList = CommonMethods.LoadJsonStoreType(FilePath + "StoreArea.json");
//第三步: 其他初始化 实时接收值, 将配置信息写入CurrentValue与CurrentVarAddress
foreach (Variable_Modbus item in CommonMethods.VarModbusList)
{
if (!CommonMethods.CurrentValue.ContainsKey(item.VarName)) 变量名和数值的字典集合
{
CommonMethods.CurrentValue.Add(item.VarName, "");
CommonMethods.CurrentVarAddress.Add(item.VarName, item.Address);
}
if (item.StoreArea == "01 Coil Status(0x)")
{
CommonMethods.List_0x.Add(item); //CommThread objComm = new CommThread(); CommThread中的方法
}
if (item.StoreArea == "02 Input Status(1x)")
{
CommonMethods.List_1x.Add(item);
}
if (item.StoreArea == "03 Holding Register(4x)")
{
CommonMethods.List_4x.Add(item);
}
if (item.StoreArea == "04 Input Register(3x)")
{
CommonMethods.List_3x.Add(item); //存储实时数据
}
if (item.IsFiling == "1")//是否归档
{
CommonMethods.FileVarModbusList.Add(item);
CommonMethods.CurrentVarNote.Add(item.VarName, item.Note);
CommonMethods.FileVarNameList.Add(item.Note);
}
if (item.IsReport == "1")//是否做报表
{
CommonMethods.ReportVarModbusList.Add(item);
}
}
}
#endregion
#region 打开线程读取数据
private void IniTaskModbus()
{
//bool a;
Task.Run(async () =>
{
await Task.Delay(200);
OpenTCP(CommonMethods.objModbusTCPEntity);//是否运行?
await Task.Delay(200);
CommonMethods.TCPisConnected = true;
while (!cts.IsCancellationRequested)
{
await Task.Delay(3000);
if (CommonMethods.TCPisConnected)
{
CommonMethods.treadTCP.Communication();
//this.ErrorTimer <= this.MaxErrorTimer
if (CommonMethods.treadTCP.ErrorTimer > 0 & CommonMethods.treadTCP.ErrorTimer <= CommonMethods.treadTCP.MaxErrorTimer)
{
await Task.Delay(3000);
CommonMethods.treadTCP.Communication();
CommonMethods.treadTCP.FirstConnect = false;
}
else if (!CommonMethods.treadTCP.IsConnected)//this.ErrorTimer >= this.MaxErrorTimer
{
CommonMethods.TCPisConnected = false;
}
}
else
{
if (!CommonMethods.treadTCP.FirstConnect)
{
await Task.Delay(3000);
CommonMethods.objModTCP?.Disconnect();
}
OpenTCP(CommonMethods.objModbusTCPEntity);
CommonMethods.treadTCP = new TreadTCP();
CommonMethods.TCPisConnected = true;
CommonMethods.treadTCP.FirstConnect = false;
}
}
//else
//{
// CommonMethods.TCPisConnected = false;
//}
}, cts.Token);
}
#endregion
}
}
写插入SQLite数据库的函数
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DAL;
namespace ShenhuaSQLite
{
public class InsertDataSQLite
{
System.Timers.Timer t;
int count = 0;
public InsertDataSQLite(int Interval)
{
t = new System.Timers.Timer(Interval);
t.Elapsed += T_Elapsed;
t.AutoReset = true;
t.Enabled = true;
t.Start();
count = CommonMethods.CacheCount; //默认为600
}
private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (CommonMethods.TCPisConnected)
{
InsertActualData();
if (DateTime.Now.Minute == 0 && DateTime.Now.Second == 0)//整点插入
{
InsertDataHourReport();
}
}
}
#region 插入实时数据
private void InsertActualData()
{
if (CommonMethods.CurrentValue != null && CommonMethods.CurrentValue.Count > 0)
{
List<ActualData> actualDataArray = new List<ActualData>();
List<Cache> cache = new List<Cache>();
FileVarModbusList归档变量集合 在commonMethos里创建的静态变量, 归档, 意味着存入数据库
//归档信息在json里面存储
foreach (Variable_Modbus item in CommonMethods.FileVarModbusList)//FileVarModbusList归档集合
{
string varName = item.VarName;//modbus 点表中的名称
string remark = item.Note; //注释
double value = 0.0;
if (!CommonMethods.CurrentValue.ContainsKey(varName) || CommonMethods.CurrentValue[varName].Length == 0)
{
value = 0.0;
}
else
{
value = Convert.ToDouble(CommonMethods.CurrentValue[varName]);
}
DateTime dt = DateTime.Now;
actualDataArray.Add(new ActualData()
{
InsertTime = dt,
VarName = varName,
Value = Convert.ToString(value),
Remark = remark
});
cache.Add(new Cache()
{
InsertTime = dt,
VarName = varName,
Value = value.ToString(),
Remark = remark
});
}
var count = new ActualDataProvider().Insert(actualDataArray); //向数据库插入数据
if (CommonMethods.CacheList.Count <= count)//如果Count小于600
{
CommonMethods.CacheList.Add(cache);//集合的集合
}
else
{
CommonMethods.CacheList.RemoveAt(0);//把第一个去掉
CommonMethods.CacheList.Add(cache);//继续添加
}
}
}
#endregion
#region 插入小时数据
private void InsertDataHourReport()
{
List<string> array = new List<string>();
foreach (Variable_Modbus item in CommonMethods.ReportVarModbusList)
{
double value = 0.0;
if (CommonMethods.CurrentValue.ContainsKey(item.VarName))
{
string Res = CommonMethods.CurrentValue[item.VarName];//键值对,a[key]为值,存了19个变量的值
if (Res.Length == 0)
{
value = 0.0;
}
else
{
value = Convert.ToDouble(Res);
}
array.Add(value.ToString("f1"));
}
ReportData reportData = new ReportData()
{
J0GCB11CF101 = array[0],
J0GCB12CF101 = array[1],
J0GCB13CF101 = array[2],
J0GCB20CT101 = array[3],
J0GCB20CQ101_NTU = array[4],
J0GCB10CP102 = array[5],
J0GCB21CF101 = array[6],
J0GCB21CF102 = array[7],
J0GCB21CP101 = array[8],
J0GCB21CP102 = array[9],
J0GCB21CQ101_NTU = array[10],
J0GCB22CF101 = array[11],
J0GCB22CF102 = array[12],
J0GCB22CP101 = array[13],
J0GCB22CP102 = array[14],
J0GCB22CQ101_NTU = array[15],
J0GCB31CL101 = array[16],
J0GCB32CL101 = array[17],
J0GCB50CP101 = array[18]
};
var conut = new ReportDataProvider().Insert(reportData);
}
}
#endregion
}
}
写EF的数据库查询provider
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DAL
{
public class ActualDataProvider
{
private MainEntities db = new MainEntities();
public int Delete(ActualData t)
{
if (t == null) return 0;
var model = db.ActualData.ToList().FirstOrDefault(item => t.Id == item.Id);
if (model == null) return 0;
db.ActualData.Remove(model);
int count = db.SaveChanges();
return count;
}
public int Insert(List<ActualData> t)
{
if (t == null) return 0;
//if (t.Value == null) return 0;
foreach (var item in t.ToArray())
{
db.ActualData.Add(item);
}
int count = db.SaveChanges();
return count;
}
public List<ActualData> Select()
{
var actualData = db.ActualData.Where(u=>System.Data.Objects.EntityFunctions.DiffDays(DateTime.Now,u.InsertTime)<=1).ToList();
return actualData;
}
public int Update(ActualData t)
{
if (t==null) return 0;
var model = db.ActualData.ToList().FirstOrDefault(item=>t.Id== item.Id);
if(model == null) return 0;
model.InsertTime = t.InsertTime;
model.Value=t.Value;
model.Remark= t.Remark;
model.InsertTime = t.InsertTime;
int count = db.SaveChanges();
return count;
}
}
}