物流网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 } } }
共有条评论 网友评论