modbus TCP 写的断线重连程序

经测试 已经成功。

先写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;
        }
    }
}

你可能感兴趣的:(tcp/ip,c#,网络协议)