您好!欢迎来到默认站点! 请登录 注册有礼
首页 新闻资讯 > 研发日志 > 物联网

物流网IO模块开发三:解析上报数据

2020-6-14 20:20:00 人评论

物流网IO模块相关功能均在SocketServer.cs派生类SwitchDeviceServer.cs中实现,暂时可处理计数和开关状态两种模式using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net;namespace Mvcms.Common…

物流网IO模块相关功能均在SocketServer.cs派生类SwitchDeviceServer.cs中实现,暂时可处理计数和开关状态两种模式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;

namespace Mvcms.Common.Net {
    /// <summary>
    /// IO开关量型设备Socket服务
    /// </summary>
    public class SwitchDeviceServer: SocketServer {
        private static BLL.IOT.device_interface_counter bll_counter_log = new BLL.IOT.device_interface_counter();
        private static BLL.IOT.device_interface_status bll_status_log = new BLL.IOT.device_interface_status();
        private static BLL.IOT.devices bll_devices = new BLL.IOT.devices();
        private static List<Models.IOT.devices> list_devices = new List<Models.IOT.devices>();//保存上报数据的设备信息,避免频繁读取数据库

        #region 构造函数
        public SwitchDeviceServer()
            : base() {
            BroadcastPort = 21678;//只有搜索设备操作时会使用无参构造函数创建实例
            BroadcastContent = Utils.HexStringToByte("FA013433215623A57B29C55D3C3212FE01FF000000000000000000003A4B");
        }

        public SwitchDeviceServer(int port)
            : base(port) {
        }

        /// <summary>
        /// 构造SocketServer对象实例,监听队列值上限值和接收数据队列上限值默认为SocketHelper对应常量
        /// </summary>
        /// <param name="ipaddress">监听的IP地址(服务器本机)</param>
        /// <param name="port">监听的端口号(服务器本机)</param>
        public SwitchDeviceServer(IPAddress ipaddress, int port)
            : base(ipaddress, port) {
        }
        #endregion

        public override bool SocketReceiveData(SocketClient socketClient, BaseSocketReceive receiveData) {
            bool result = base.SocketReceiveData(socketClient, receiveData);
            Models.IOT.listen_port model_port = new BLL.IOT.listen_port().GetFirstModel("port=" + receiveData.DestinationPort);
            if (model_port == null) {
                return false;
            }
            List<Entities.IOT.card_type> listCardType = new BLL.IOT.card_type().GetList(model_port.ArrayKeys);
            byte[] receiveBytes = receiveData.ReceiveBytes;//Socket接收到的数据(包含多条卡号数据的标志位、卡号数据、CRC-16结束数据位)
            byte[] flag_bit = Common.Utils.HexStringToByte(model_port.custom_data);//获取标志位数据
            int index = 0;//接收数据检索索引值,不是单向递增1,如果与卡型匹配失败会回退起始位置重新匹配下一个卡类型标志位
            int type_index = 0;//设置
            while (index < receiveBytes.Length) {
                byte[] buffer = new byte[4];//设置状态数据字节数组缓冲区
                int total_length = 10;//完整状态数据位10个字节,示例:00 00 00 00 00 04 00 02 01 01
                bool ismatch = false;//是否已经成功匹配到了当前卡类型标志位
                int flag_index = 0;//匹配标志位进行中的索引值
                int buffer_index = 0;
                int end_index = 0;//上一条有效数据匹配结束时的索引值
                while (index < receiveBytes.Length) {
                    if (ismatch) {//读取卡号数据中
                        #region 成功匹配到标志位,开始读取数据
                        buffer[buffer_index] = receiveBytes[index];//保存卡号字节数据到卡号字节数组
                        buffer_index++;
                        if (buffer_index == buffer.Length) {//读取了一条完整的EPC卡号数据,进行下一条数据匹配
                            //保存接收到状态数据
                            receiveData.CustomBytes = buffer;

                            ismatch = false;//重新匹配下一个卡号数据
                            buffer_index = 0;//缓冲区索引重置为0
                            end_index = index;//设置上一个卡号成功匹配的结束索引值
                            flag_index = 0;//标志位索引设置为0
                            type_index = 0;//类型索引归零,重新开始匹配
                        }
                        #endregion
                    }
                    else {//匹配标志位中
                        #region 匹配标志位进行中
                        if (receiveBytes[index] == flag_bit[flag_index]) {//成功匹配标志位中,需要判断是否所有标志位都匹配成功
                            flag_index++;
                            if (flag_index == flag_bit.Length) {
                                //标志位匹配成功,开始读取卡号数据
                                ismatch = true;
                                flag_index = 0;
                                index++;
                                continue;
                            }
                        }
                        else {
                            if (flag_index > 0) {//标志位索引值大于0,说明有部分标志匹配成功,当前标志位匹配失败
                                flag_index = 0;//标志位索引值设置为0,重新开始匹配标志位
                            }
                        }
                        if ((index - end_index) > total_length) {//匹配的数据长度超出单条卡数据长度
                            type_index++;//设置匹配下一个卡类型标志位
                            if (type_index < listCardType.Count) {
                                //后续还有卡类型需要匹配
                                index = end_index;//接收数据索引值重置为上一次成功匹配的结束索引
                                break;
                            }
                            else {
                                //所有卡类型的标志位匹配失败
                                type_index = 0;
                                end_index = index;//设置结束索引值为当前索引,忽略当前区段的数据
                                flag_index = 0;//重置标志位索引值
                            }
                        }
                        #endregion
                    }//end if(ismatch)
                    index++;
                }//end while
            }//end while

            //保存接收的有效数据
            if (receiveData.CustomBytes != null && receiveData.CustomBytes.Length == 4) {
                receiveData.IsValid = true;
                AddReceiveValidData(receiveData);
                ReceiveStateData(socketClient, receiveData);//处理状态数据
            }
            return result;
        }

        private void ReceiveStateData( SocketClient socketClient, BaseSocketReceive receiveData) {
            Models.IOT.devices find_device = list_devices.Find(p => p.ipaddress == receiveData.IPAddress);
            if (find_device == null) {
                //获取设备信息
                find_device = bll_devices.GetFirstModel("ipaddress='" + receiveData.IPAddress + "'");
                if (find_device == null) {
                    return;
                }
                find_device.OnLine = true;
                list_devices.Add(find_device);
            }
            //处理开关量状态逻辑
            byte bitstatus = receiveData.ReceiveBytes[receiveData.ReceiveBytes.Length - 1];
            receiveData.CustomBytes = new byte[] { bitstatus };
            foreach (Models.IOT.device_interface port in find_device.InPortList) {
                DateTime old_time = port.update_time;
                //判断状态是否已经改变,接口序号由1起始,需减1
                byte stateBit = Utils.GetBit(bitstatus, (byte)(port.interface_index - 1));//当前接口实时状态, 只有0/1两个状态
                if (port.StateBit == stateBit) {
                    continue;
                }
                switch (port.InterfaceDataTypeEnum) {
                    case Common.IOT.TEnums.InterfaceDataType.BitValue:
                        bool isSave = false;
                        //计算与上一次上报数据的时差(毫秒)
                        decimal diff_ms = (decimal)(receiveData.ReceiveTime - port.update_time).TotalMilliseconds;
                        if (stateBit == 0) {//端口当前已处于高电平(开路状态)
                            //判断是否需要设置端口状态
                            if (port.StateBit == 1) {//端口历史状态为低电平(短接状态)
                                if (diff_ms > port.threshold_value) {//时间间隔已达到端口设定阈值(毫秒)
                                    isSave = true;
                                }
                            }
                            else {//端口历史状态为高电平(开路状态)
                                if (diff_ms > (port.threshold_value * 2)) {//超时状态,上一次计时区间未达到阈值时间
                                    //更新时间,重新进行区间计时
                                    port.update_time = receiveData.ReceiveTime;
                                }
                            }
                        }
                        else {//端口当前处于低电平(短接状态)
                            if (diff_ms > (port.threshold_value * 2)) {//开始新的计时周期
                                port.StateBit = stateBit;
                                port.update_time = receiveData.ReceiveTime;
                            }
                        }
                        //判断是否完成统计周期,创建完整记录
                        if (isSave) {
                            //更新接口状态,保存数据库
                            port.StateBit = 0;
                            port.update_time = DateTime.Now;
                            Models.IOT.devices dev = list_devices.Find(p => p.id == port.device_id);
                            if (dev != null) {
                                if (port.InterfaceWordMode == Common.IOT.TEnums.InterfaceWorkMode.Status) {
                                    if (!bll_status_log.Add(dev, port, old_time, Convert.ToInt32(diff_ms))) {//添加状态记录(门开关时间统计)
                                        ErrorCode = bll_status_log.ErrorCode;
                                        Message = bll_status_log.Message;
                                    }
                                }
                                if (port.InterfaceWordMode == IOT.TEnums.InterfaceWorkMode.Counter) {
                                    if (!bll_counter_log.Add(dev, port, Convert.ToInt32(diff_ms))) {//添加计数记录(车次统计)
                                        ErrorCode = bll_status_log.ErrorCode;
                                        Message = bll_status_log.Message;
                                    }
                                }
                            }
                        }
                        break;
                }
            }
        }

        

        public override void SetDeviceInfo(BaseSocketReceive socketReceive) {
            byte[] bytes = socketReceive.ReceiveBytes;
            //回复数据示例:SDD-4040-BB3                                  |                                   内容                                      |
            //FA013433215623A57B29C55D3C3212FE 02FF0000 0009F6113BB0 2400 0009F6113BB0 0A640A19 0614 1403 0002 C4CFC3C5C8EBBFDA000000000000000000000000 EF22
            //      包头16字节(固定内容)     |   命令  |   MAC地址  |长度|   MAC地址  |  IP地址 |编号| 主V| 从V |    设备名称                            |CRC                  
            int index = 28;//有效数据索引位置
            //MAC地址
            List<string> ss = new List<string>();
            for (int i = index + 0; i < index + 6; i++) {
                ss.Add(BitConverter.ToString(bytes, i, 1));
            }
            socketReceive.MAC = string.Join(":", ss);
            ss.Clear();
            //设备型号
            index += 10;
            byte[] num = new byte[2];
            Array.Copy(bytes, index, num, 0, 2);
            short deviceNumber = BitConverter.ToInt16(bytes, index);

            //设备名称
            index += 6;
            int endindex = bytes.Length;
            for(int i= index ;i<bytes.Length; i++){
                if(bytes[i] == 0){
                    endindex = i;
                    break;
                }
            }
            byte[] content = new byte[endindex - index];
            Array.Copy(bytes, index, content, 0, content.Length);
            socketReceive.Title = Encoding.Default.GetString(content);
            Entities.IOT.device_type entity_dev_type = new BLL.IOT.device_type().GetFirstEntity("custom_data='" + deviceNumber.ToString() + "'");
            if (entity_dev_type != null) {
                socketReceive.Name = entity_dev_type.name;
                socketReceive.Tag = entity_dev_type;
            } else {
                socketReceive.Name = "[设备编号:"+ deviceNumber.ToString() +"]" + socketReceive.Title;
            }
        }
    }
}

保存数据库代码属于基础功能,不需要备忘

相关的枚举定义:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;

namespace Mvcms.Common.IOT {
    public class TEnums {
        /// <summary>
        /// 设备工作方式
        /// </summary>
        public enum WorkMode {
            /// <summary>
            /// 未定义
            /// </summary>
            [Display(Name="未定义", Description="")]
            None = 0,
            /// <summary>
            /// 客户端-主动模式,设备主动向服务器报送数据
            /// </summary>
            [Display(Name="客户端-主动模式", Description="设备主动向服务器报送数据")]
            TCPClient=1,
            /// <summary>
            /// 服务端-被动模式,设备被动接受服务器发送指令
            /// </summary>
            [Display(Name="服务端-被动模式", Description="设备被动接受服务器发送指令")]
            TCPServer=2
        }

        /// <summary>
        /// IOT设备传输的数据类型
        /// </summary>
        public enum DeviceDataType {
            /// <summary>
            /// 未定义
            /// </summary>
            [Display(Name = "未定义", Description = "")]
            None = 0,
            /// <summary>
            /// 文本(RFID读头、LED显示屏...)
            /// </summary>
            [Display(Name = "字符串", Description = "文本")]
            Text = 1,
            /// <summary>
            /// 开关量(网络型IO模块)
            /// </summary>
            [Display(Name = "开关量", Description = "网络型IO模块")]
            BitValue = 2,
            /// <summary>
            /// 浮点数(网络型模拟量模块)
            /// </summary>
            [Display(Name = "模拟量", Description = "网络型模拟量模块")]
            DecimalValue = 3,

            /// <summary>
            /// 多媒体数据
            /// </summary>
            [Display(Name="媒体数据", Description="音视频多媒体设备")]
            Media = 4
        }

        /// <summary>
        /// 设备接口数据类型(与设备数据类型值对应,设备接口没有卡号传输口)
        /// </summary>
        public enum InterfaceDataType {
            /// <summary>
            /// 未定义
            /// </summary>
            [Display(Name = "未定义")]
            None = 0,
            /// <summary>
            /// 输入型开关量
            /// </summary>
            [Display(Name = "开关量")]
            BitValue = 2,
            /// <summary>
            /// 输入型模拟量
            /// </summary>
            [Display(Name = "模拟量")]
            InDecimal = 3
        }

        /// <summary>
        /// 设备接口业务模式
        /// </summary>
        public enum InterfaceWorkMode {
            /// <summary>
            /// 未定义
            /// </summary>
            [Display(Name = "未定义")]
            None = 0,
            /// <summary>
            /// 状态
            /// </summary>
            [Display(Name = "开关状态")]
            Status = 1,
            /// <summary>
            /// 计数
            /// </summary>
            [Display(Name = "计数器")]
            Counter = 2
        }

        /// <summary>
        /// 通行方式
        /// </summary>
        public enum TrafficMode {
            /// <summary>
            /// 缺省
            /// </summary>
            [Display(Name="缺省")]
            None = 0,
            /// <summary>
            /// 进入
            /// </summary>
            [Display(Name="进入")]
            In = 1,
            /// <summary>
            /// 离开
            /// </summary>
            [Display(Name="离开")]
            Out = 2
        }
    }
}


相关资讯

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?
    会员登陆
    18004549898
    QQ
    Mvcms网站管理系统客服QQ:
    返回顶部