我是靠谱客的博主 友好香水,最近开发中收集的这篇文章主要介绍从零开始搭建4G DTU设备对应的云平台(一)一、了解通信方式二、对云平台数据采集部分进行编码,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、了解通信方式

搭建4G DTU设备对应的云平台过程中第一个问题就是,如何在自己的服务器上面与设备进行通信。

刚开始的时候,我看到说明书上写着TCP协议,还以为是用HTTP协议,用RequestHeaders和Request Params访问设备,然后会返回想要的数据,但是多方求证无果,最后发现仅仅是TCP协议,也就是单纯的Socket通信。

第二个问题是端口的问题。

以前公司里用过一次以太网转串口的服务器,在哪台设备上,特定的串口都是绑定到特定的端口的,比如说:

 

刚开始我也以为这个DTU设备的串口透传是要通过特定的端口来访问的,但是问了厂家的技术人员,但是对方好像不是很了解,问了两天结果问了个寂寞????(不知道是我的表达问题还是啥)。

 

然后自己写了个控制台程序,把端口都遍历了一遍:

 

但是全被拒绝了。

分析问题,应该是因为设备上设置了服务器的端口,所以这个设备只会跟服务器上面的特定端口进行通信,其他的端口即使尝试建立连接也会被拒绝。而我遍历设备端口的时候正是实时生成随机端口来访问设备的。

后来尝试在TCP连接(三次握手)建立之后,在建立连接的套接字上面向设备不停的发送数据,这一次成功了,从而知道,透传的端口不是特定的,而是在建立TCP连接的端口就可以透传数据。

二、对云平台数据采集部分进行编码

同样,采集数据还是直接用控制台程序,因为没有什么参数好设置的。

这一部分,又可以拆分出来三个小部分:数据通信,数据校验,数据存储。

1、数据校验

这一部分主要是针对CRC16 modbus校验来编写的,首先根据CRC16的定义得到以下的校验算法(这个算法是网上找的,出处忘了):

public static byte[] CRC16(byte[] bytes)
        {
            //计算并填写CRC校验码
            int crc = 0xffff;
            int len = bytes.Length;
            for (int n = 0; n < len; n++)
            {
                byte i;
                crc = crc ^ bytes[n];
                for (i = 0; i < 8; i++)
                {
                    int TT;
                    TT = crc & 1;
                    crc = crc >> 1;
                    crc = crc & 0x7fff;
                    if (TT == 1)
                    {
                        crc = crc ^ 0xa001;
                    }
                    crc = crc & 0xffff;
                }

            }

            var nl = bytes.Length + 2;
            //生成的两位校验码
            byte[] redata = new byte[2];
            redata[0] = (byte)((crc & 0xff));
            redata[1] = (byte)((crc >> 8) & 0xff);

            //重新组织字节数组
            var newByte = new byte[nl];
            for (int i = 0; i < bytes.Length; i++)
            {
                newByte[i] = bytes[i];
            }
            newByte[nl - 2] = (byte)redata[0];
            newByte[nl - 1] = redata[1];

            return newByte;
        }

因为不只是发送的指令需要校验,从设备返回的数据同样是需要校验的,所以需要一个函数来对返回数据进行校验其正确性:

public static bool isDataCorrect(byte[] data)
        {
            var crcData = CRC16(data.Take(data.Length - 2).ToArray());
            return crcData[crcData.Length - 1] == data[data.Length - 1] && crcData[crcData.Length - 2] == data[data.Length - 2];
        }

另外一个跟数据处理相关的地方就是把byte转换成十六位无符号整数、十六位有符号整数、三十二位无符号整数、三十二位有符号整数等,一开始我是自己写的:

public static int getCombinedInt(List<byte> list, int startIndex)
        {
            return (short)((list[startIndex] << 8) + list[startIndex + 1]);
        }
        public static long getCombinedLong(List<byte> list, int startIndex)
        {
            return ((int)(list[startIndex] << 24)) | ((int)list[startIndex + 1] << 16) | (list[startIndex + 2] << 8) | list[startIndex + 3];
        }

 

挺抽象的,也挺麻烦的,后来才知道.net 平台已经提供了此类数据转换的封装,直接调用就行:

public static int getSignedInt(byte[] data, int startIndex)
        {
            return BitConverter.ToInt32(data, startIndex);
        }
        public static uint getInt(byte[] data, int startIndex)
        {
            return BitConverter.ToUInt32(data, startIndex);
        }
        public static short getSignedShort(byte[] data,int startIndex)
        {
            return BitConverter.ToInt16(data, startIndex);
        }
        public static ushort getShort(byte[] data,int startIndex)
        {
            return BitConverter.ToUInt16(data, startIndex);
        }

2、数据存储

存储数据使用的是MySQL,首先要在nuget里面安装MySQL的扩展包。

MySQL使用方式和SQL server差不多,都要有一个ConnetionString,所以需要有这样一个数据库连接字符串,然后提供打开连接和关闭连接:

private readonly MySqlConnection connection = new MySqlConnection();
        private MySqlCommand command;

        public DataBaseUtils(string connectionString,string remoteAddress)
        {
            connection.ConnectionString = connectionString;
        }

        public bool openConnection()
        {
            try
            {
                connection.Open();
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(connection.ConnectionString);
                Console.WriteLine(e.StackTrace);
                Console.WriteLine(e.Message);
                return false;
            }

        }
        public bool closeConnection()
        {
            try
            {
                connection.Close();
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(connection.ConnectionString);
                Console.WriteLine(e.StackTrace);
                Console.WriteLine(e.Message);
                return false;
            }
        }

 

然后用MySqlCommand,MySqlConnection向数据库插入数据:

public bool insertMysql(MysqlTables table, string createTime, List<long> data)
        {
            try
            {
                var tableName = getTableName(table);
                var tableData = getDataString(data);
                var insertSql = $"insert into {tableName} values(null,'{createTime}',{tableData})";
                command = new MySqlCommand(insertSql, connection);
                var result = command.ExecuteNonQuery();
                if (result == 0)
                {
                    return false;
                }
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
                return false;
            }

        }

3、数据通信

这一部分是直接写在Main函数里面的,因为代码比较少,就不再抽象成其他的库来二次调用了。

思路是:在主线程中不断地监听端口,每当有一个TCP连接建立,就新创建一个新的线程,在新的线程里面用套接字进行通信。

首先实例化一个TcpListener,在循环中不断尝试接受TCP连接请求:

            bool listening = true;
            var listener = new TcpListener(IPAddress.Parse("0.0.0.0"), 8081);
            listener.Start();
            Console.WriteLine("正在监听8081端口rn");
            while (listening)
            {
                var socket = listener.AcceptSocket();

            }

刚开始我把IPAddress.Parse的参数写成了127.0.0.1,但是怎么都收不到外网传进来的TCP连接,只能在内网自己连着玩????。经过开发者群大佬的指点,得知这个地方要写0.0.0.0:

 

TCP连接之后,创建子线程,进行指令的发送和数据的采集和存储:

Task.Run(() =>
                {
                    try
                    {
                        string remoteSocket = socket.RemoteEndPoint.ToString();
                        var buffer = new byte[128];
                        int i = socket.Receive(buffer);//接受连接请求的字节流
                        string msg = "<" + remoteSocket + ">" + System.Text.Encoding.UTF8.GetString(buffer);
                        Console.WriteLine(DateTime.Now);
                        Console.WriteLine(msg);//在控制台显示字符串
                        while (socket.Connected)
                        {
                            socket.Send(CRC16Utils.CRC16(Commands.DTU_COMMAND));
                            socket.Receive(buffer);
                            if (buffer[0] == 1 && buffer[1] == 3)
                            {
                                if (CRC16Utils.isDataCorrect(buffer.Take(67).ToArray()))
                                {
                                    var model = DataDeSeirializeUtils.deserializeDTUAmmeterData(buffer);
                                    model.time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                    dataBase.insertData(model, ProjectCommonLibs.enums.MysqlTables.DTU_AMMETER);
                                }
                            }
                            var listHex = (from item in buffer select String.Format("{0:X2}", item)).ToArray();
                            Console.WriteLine(String.Join(" ", listHex));
                            Thread.Sleep(10 * 1000);
                        }
                        socket.Shutdown(SocketShutdown.Both);
                        socket.Close();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        Console.WriteLine(ex.StackTrace);
                        if (socket.Connected)
                        {
                            socket.Shutdown(SocketShutdown.Both);
                            socket.Close();
                        }
                    }
                });

数据采集部分结束,后面写服务器端和前端。

最后

以上就是友好香水为你收集整理的从零开始搭建4G DTU设备对应的云平台(一)一、了解通信方式二、对云平台数据采集部分进行编码的全部内容,希望文章能够帮你解决从零开始搭建4G DTU设备对应的云平台(一)一、了解通信方式二、对云平台数据采集部分进行编码所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(59)

评论列表共有 0 条评论

立即
投稿
返回
顶部