1.SDK下载

JT701SDK(JT701SDK_V2.rar) 下载地址
Jt701SDK测试工具(JT701SDK_Test.rar) 下载地址
如果需要测试工具及SDK开发源码,请与商务申请


2.集成开发说明

2.1.集成开发语言及框架说明

JT701SDK(Jt701DataParser.dll)及测试工具(Test.exe)是基于C#语言,.NET Framework 4.6.1目标框架开发的。该SDK的集成开发环境也是需要依赖于C#语言以及.NET Framework 4.6.1目标框架。

2.2.集成说明

(1)将Jt701DataParser.dll引入到自己的网关程序中,引入方法如下:
鼠标右键项目下的”引用”,选择”添加引用(R)…”

选择下图中”浏览(B)…”,找到你解压JT701SDK20210327.rar后的文件夹,选中Jt701DataParser.dll与对应的Json字符串序列化/反序列化包Newtonsoft.Json.dll即可完成对SDK的引用

(2)调用Jt701DataParser.dll中的解析方法receiveData
receiveData()是一个重载方法,你可以传入16进制字符串或者byte[],一般我们网关程序接收到的设备数据是以二进制流进行传输的,所以我们这里建议使用此重载方法receiveData(byte[] bytes);
当然如果你想通过16进制字符串进行测试,也可以使用receiveData(string strData);
strData:就是我们接收到的设备数据转成16进制后的字符串,
例如定位数据:2480405003701911003426032115595533197294020020085d0586000010e10c0000000020e043600a526d1700020f0f0f0f0f0f0f0f0f0f0eaa028f018b
指令数据:28373839303632393238342c5034352c3236303332312c3138353334322c32362e3138313239362c4e2c35302e3332393738382c452c562c302e30302c302c352c302c303030303030303030302c302c302c3029

2.3.核心代码

Jt701DataParser.dll
(1)解析类DataParser.cs

public class DataParser
    {
        private static byte[] endbytes = null;// 上一次未处理的剩余字节

        /// <summary>
        /// 解析16进制原始数据
        /// </summary>
        /// <param name="strData"></param>
        /// <returns></returns>
        public static string receiveData(string strData)
        {
            byte[] bytes = Common.HexStringToBytes(strData);
            return receiveData(bytes);
        }

        /// <summary>
        /// 解析2进制原始数据
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string receiveData(byte[] bytes)
        {
            byte[] newBytes = null;
            if (endbytes!=null && endbytes.Length > 0)
            {
                newBytes = new byte[endbytes.Length + bytes.Length];
                bytes = Common.CombineBytes(endbytes, bytes);
            }
            else
            {
                newBytes = new byte[bytes.Length];
                newBytes = bytes;
            }
            int i = 0;//初始化byte[]下标
            byte[] parserBytes = null;//去除正确包头之前的数据后的数据
            foreach (var item in newBytes)
            {
                if (item == '$')
                {
                    parserBytes = new byte[newBytes.Length - i];
                    parserBytes = newBytes.Skip(i).Take(newBytes.Length - i).ToArray();
                    return parserLocation(parserBytes);
                }
                else if (item == '(')
                {
                    parserBytes = new byte[newBytes.Length - i];
                    parserBytes = newBytes.Skip(i).Take(newBytes.Length - i).ToArray();
                    return parserCommand(parserBytes);
                }
                i++;
            }
            return null;
        }

        /// <summary>
        /// 解析定位数据
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private static string parserLocation(byte[] bytes)
        {
            //定义定位数据实体类
            Result model = new Result();
            try
            {
                //跳过包头,然后解析设备ID
                model.DeviceID= Common.ByteToHexStr(bytes.Skip(1).Take(5).ToArray());
                model.MsgType = "Location";
                LocationData location = new LocationData();
                location.ProtocolType = bytes[6];
                location.DeviceType = bytes[7] & 0x0F;
                location.DataType = (bytes[7]>>4) & 0x0F;
                //得到数据长度
                location.DataLength = Common.SwapUInt16(BitConverter.ToUInt16(bytes, 8));
                //获取时间段,转成我们识别的"yyyy-MM-dd HH:mm:ss"格式
                location.GpsTime = Common.GetDataTime(bytes.Skip(10).Take(6).ToArray());
                //纬度信息解析
                LatLon latLon = Common.GetLatLon(bytes.Skip(16).Take(9).ToArray());
                location.Latitude = latLon.Latitude;//纬度
                location.Longitude = latLon.Longitude;//经度
                location.LocationType = latLon.LocationType;//定位状态
                //解析速度
                location.Speed = (int)(bytes[25]*1.85);
                //解析方向
                location.Direction = bytes[26] * 2;
                //解析里程
                location.Mileage = Common.SwapUInt32(BitConverter.ToUInt32(bytes, 27));//里程
                //解析GPS卫星个数
                location.GpsSignal = bytes[31];
                //解析设备状态
                DeviceStatus deviceStatus = Common.GetDeviceStatus(bytes.Skip(36).Take(2).ToArray(), location.LocationType);
                //解析是否基站定位(GPS定位>基站定位>不定位)
                location.LocationType = deviceStatus.LocationType;
                //解析报警类型
                location.Alarm = deviceStatus.Alarm;
                //解析锁绳状态
                location.LockRope = deviceStatus.LockRope;
                //解析锁状态
                location.LockStatus = deviceStatus.LockStatus;
                //解析后盖状态
                location.BackCover = deviceStatus.BackCover;
                //获取电量(255为充电中)
                location.Battery = bytes[38];
                //解析小区码CellID低2位数据
                string strLowCellID = Common.ByteToHexStr(bytes.Skip(39).Take(2).ToArray());
                //解析小区码LAC
                location.LAC = Common.SwapUInt16(BitConverter.ToUInt16(bytes, 41));
                //解析GSM信号值
                location.GSMSignal = bytes[43];
                //解析区域ID
                location.AlarmArea = bytes[44];
                //得到唤醒源
                location.Awaken = bytes[45];
                //得到IMEI号
                location.IMEI = Common.ByteToHexStr(bytes.Skip(48).Take(8).ToArray());
                ////解析小区码CellID高2位数据
                string strHightCellID = Common.ByteToHexStr(bytes.Skip(56).Take(2).ToArray());
                location.CELLID = Convert.ToInt64(strHightCellID + strLowCellID, 16);
                location.MCC = Common.SwapUInt16(BitConverter.ToUInt16(bytes, 58));
                location.MNC = bytes[60];
                location.Index= bytes[61];
                model.DataBody = location;
                if (location.ProtocolType < 0x19)
                {
                    model.ReplyMsg= "(P35)";
                }
                else
                {
                    model.ReplyMsg = string.Format("(P69,0,{0})", location.Index);
                }
            }
            catch (Exception ex)
            {
                return null;
            }
            return JsonConvert.SerializeObject(model);
        }

        /// <summary>
        /// 解析指令数据
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private static string parserCommand(byte[] bytes)
        {
            Result result = new Result();
            byte[] newBytes = null;
            int tailIndex = Common.BytesIndexOf(bytes,0x29)+1;
            if (tailIndex > 0)
            {
                if (bytes.Length > tailIndex)
                {
                    endbytes = bytes.Skip(tailIndex).Take(bytes.Length - tailIndex).ToArray();
                }
                newBytes= bytes.Skip(0).Take(tailIndex).ToArray();
            }
            else
            {
                endbytes = new byte[bytes.Length];
                endbytes = bytes;
            }
            if (newBytes!=null && newBytes.Length > 0)
            {
                //转换成()带括号的数据
                string msgAscii = Common.ByteToASCII(newBytes);
                //按逗号拆分字符串
                char[] p = new char[] { ',' };
                //返回的数组中,包含空字符串
                string[] msgArrays = msgAscii.Replace("(", "").Replace(")", "").Split(p, StringSplitOptions.None);
                result.DeviceID = msgArrays[0];
                string msgType = string.Empty;
                if (msgArrays.Length > 5 && msgArrays[3] == "WLNET")
                {
                    msgType = msgArrays[3] + msgArrays[4];
                }
                else
                {
                    msgType = msgArrays[1];
                }
                result.MsgType = msgType;
                if (msgType.Equals("WLNET5"))
                {
                    SensorData sensorData = parserWLNET5(msgArrays, newBytes);
                    result.DataBody = sensorData;
                    result.ReplyMsg = Common.replyMessage(msgType, sensorData.Index);
                }
                else
                {
                    if (msgType.Equals("P45"))
                    {
                        LockEvent lockEvent = parserP45(msgArrays);
                        result.DataBody = lockEvent;
                    }
                    else
                    {
                        result.DataBody = msgAscii;
                    }
                    result.ReplyMsg = Common.replyMessage(msgType, msgArrays);
                }
                return JsonConvert.SerializeObject(result);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// WLNET5透传数据解析
        /// </summary>
        /// <param name="msgArrays"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private static SensorData parserWLNET5(string [] msgArrays, byte[] inbytes)
        {
            byte[] bytes = Common.unescape(inbytes, inbytes.Length).ToArray();
            if (bytes.Length > 30)
            {
                //透传数据之外的消息头长度
                int headLength = string.Format("({0},{1},{2},{3},{4},{5},", msgArrays[0], msgArrays[1], msgArrays[2], msgArrays[3], msgArrays[4], msgArrays[5]).Length;
                SensorData sensor = new SensorData();
                byte[] wlnetBytes = new byte[bytes.Length - headLength];
                //得到无限网关透传数据
                wlnetBytes = bytes.Skip(headLength).Take(bytes.Length - headLength).ToArray();
                //获取时间段,转成我们识别的"yyyy-MM-dd HH:mm:ss"格式
                sensor.GpsTime = Common.GetDataTime(wlnetBytes.Skip(0).Take(6).ToArray());
                //纬度信息解析
                LatLon latLon = Common.GetLatLon(wlnetBytes.Skip(6).Take(9).ToArray());
                sensor.Latitude = latLon.Latitude;//纬度
                sensor.Longitude = latLon.Longitude;//经度
                sensor.LocationType = latLon.LocationType;//定位状态
                //解析速度
                sensor.Speed = (int)(wlnetBytes[15]*1.85);
                //解析方向
                sensor.Direction = wlnetBytes[16] * 2;
                sensor.DateTime = Common.GetDataTime(wlnetBytes.Skip(17).Take(6).ToArray());
                sensor.SensorID = Common.ByteToHexStr(wlnetBytes.Skip(23).Take(5).ToArray());
                sensor.Index = wlnetBytes[28];
                sensor.Voltage = (Common.SwapUInt16(BitConverter.ToUInt16(wlnetBytes, 29)) / 100.0).ToString("f2");
                sensor.Power = wlnetBytes[31];
                sensor.RSSI = -wlnetBytes[32];
                sensor.SensorType = wlnetBytes[33];
                int fEventType = -1;
                if (sensor.SensorType == 1)
                {
                    sensor.Temperature = Common.SwapUInt16(BitConverter.ToUInt16(wlnetBytes, 34)) * 0.1;
                    sensor.Humidity = wlnetBytes[36];
                }
                else if (sensor.SensorType == 4)
                {
                    fEventType = Common.SwapUInt16(BitConverter.ToUInt16(wlnetBytes, 34));
                    if ((fEventType & 0x0001) > 0)
                    {
                        fEventType = (int)EventTypeEnum.LockEvent_0;
                    }
                    else if ((fEventType & 0x0002) > 0)
                    {
                        fEventType = (int)EventTypeEnum.LockEvent_1;
                    }
                    else if ((fEventType & 0x0004) > 0)
                    {
                        fEventType = (int)EventTypeEnum.LockEvent_2;
                    }
                    else if ((fEventType & 0x0008) > 0)
                    {
                        fEventType = (int)EventTypeEnum.LockEvent_3;
                    }
                    else if ((fEventType & 0x0010) > 0)
                    {
                        fEventType = (int)EventTypeEnum.LockEvent_4;
                    }
                    else if ((fEventType & 0x0020) > 0)
                    {
                        fEventType = (int)EventTypeEnum.LockEvent_5;
                    }
                    else if ((fEventType & 0x0040) > 0)
                    {
                        fEventType = (int)EventTypeEnum.LockEvent_6;
                    }
                    else if ((fEventType & 0x0080) > 0)
                    {
                        fEventType = (int)EventTypeEnum.LockEvent_7;
                    }
                    else
                    {
                        fEventType = -1;
                    }
                    sensor.Event = fEventType;
                    sensor.LockRope = (Common.SwapUInt16(BitConverter.ToUInt16(wlnetBytes, 36)) & 0x0001);
                    sensor.LockStatus = sensor.LockRope;
                    sensor.LockTimes = Common.SwapUInt16(BitConverter.ToUInt16(wlnetBytes, 38));
                }
                return sensor;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// P45开关锁事件解析
        /// </summary>
        /// <param name="msgArrays"></param>
        /// <returns></returns>
        private static LockEvent parserP45(string[] msgArrays)
        {
            LockEvent model = new LockEvent();
            model.DateTime= Common.GetDataTime(msgArrays[2] + msgArrays[3]);
            model.Latitude = double.Parse(msgArrays[4]);
            if (msgArrays[5].Equals("S"))
            {
                model.Latitude = -model.Latitude;
            }
            model.Longitude = double.Parse(msgArrays[6]);
            if (msgArrays[7].Equals("W"))
            {
                model.Longitude = -model.Longitude;
            }
            model.LocationType= msgArrays[8].Equals("V") ? 0 : 1;
            model.Speed= (int)double.Parse(msgArrays[9]);
            model.Direction = int.Parse(msgArrays[10]);
            model.Event = int.Parse(msgArrays[11]);
            //开锁验证
            int status= int.Parse(msgArrays[12]);
            model.RFIDNo = msgArrays[13];
            //动态密码开锁
            if (model.Event == 6)
            {
                if (status == 0)
                {
                    model.Status = 0;//开锁密码不正确
                }
                else if (status > 0 && status <= 10)
                {
                    model.Status = 1;//正常开锁
                    model.UnlockFenceID = status;//围栏内开锁时候的围栏ID
                }
                else if (status == 98)
                {
                    model.Status = 1;//正常开锁
                }
                else if (status == 99)
                {
                    model.Status = 3;//设备开启了围栏内开锁,且当前开锁并未在围栏内,拒绝开锁
                }
            }
            else if (model.Event == 4)
            {
                if (int.Parse(msgArrays[14]) == 0)
                {
                    model.Status = 0;//开锁密码不正确
                }
                else
                {
                    model.Status = 1;//正常开锁
                }
            }
            model.PsdErrorTimes = int.Parse(msgArrays[15]);
            model.Index = int.Parse(msgArrays[16]);
            if (msgArrays.Length > 17)
            {
                model.Mileage = long.Parse(msgArrays[17]);
            }
            return model;
        }
    }

(2)工具类Common.cs

public static class Common
    {

        /// <summary>
        /// 16进制格式字符串转字节数组
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] HexStringToBytes(string hexString)
        {
            hexString = Regex.Replace(hexString, @".{2}", "$0 ");
            //以 ' ' 分割字符串,并去掉空字符
            string[] chars = hexString.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            byte[] returnBytes = new byte[chars.Length];
            //逐个字符变为16进制字节数据
            for (int i = 0; i < chars.Length; i++)
            {
                returnBytes[i] = Convert.ToByte(chars[i], 16);
            }
            return returnBytes;
        }

        /// <summary>
        /// 合并数组
        /// </summary>
        /// <param name="bytes1"></param>
        /// <param name="bytes2"></param>
        /// <returns></returns>
        public static byte[] CombineBytes(byte[] bytes1, byte[] bytes2)
        {
            List<byte> tmp = new List<byte>(bytes1.Length + bytes2.Length);
            tmp.AddRange(bytes1);
            tmp.AddRange(bytes2);
            byte[] merged = tmp.ToArray();
            return merged;
        }

        /// <summary>
        /// 报告指定的 System.Byte[] 在此实例中的第一个匹配项的索引。
        /// </summary>
        /// <param name="srcBytes">被执行查找的 System.Byte[]。</param>
        /// <param name="searchBytes">要查找的 System.Byte[]。</param>
        /// <returns>如果找到该字节数组,则为 searchBytes 的索引位置;如果未找到该字节数组,则为 -1。如果 searchBytes 为 null 或者长度为0,则返回值为 -1。</returns>
        public static int BytesIndexOf(byte[] srcBytes, byte searchBytes)
        {
            if (srcBytes == null) { return -1; }
            if (srcBytes.Length == 0) { return -1; }
            for (int i = 0; i < srcBytes.Length; i++)
            {
                if (srcBytes[i] == searchBytes)
                {
                    return i;
                }
            }
            return -1;
        }

        /// <summary>
        /// 16进制字符串转ASCII
        /// </summary>
        /// <param name="Data"></param>
        /// <param name="istrans"></param>
        /// <returns></returns>
        public static string HexStrToAsciiString(string Data, bool istrans)
        {
            if (istrans)
            {
                Data = Regex.Replace(Data, @"(?is)(?<=^([0-9a-f]{2})+)(?!$)", " ");
                Data = Data.Replace("3D 11", "2C ").Replace("3D 14", "28 ").Replace("3D 15", "29 ").Replace("3D 00", "3D ").Replace(" ", "");
            }
            int count = Data.Length / 2;
            string strContent = "";
            char[] num = new char[count];
            for (int i = 0; i < count; i++)
            {
                string str = Data.Substring(i * 2, 2);
                byte by = Convert.ToByte(Convert.ToInt32(str, 16));
                num[i] = Convert.ToChar(by);
                strContent += num[i].ToString();
            }
            return strContent;
        }

        /// <summary>
        /// 二进制转ASCII
        /// </summary>
        /// <param name="bt"></param>
        /// <returns></returns>
        public static string ByteToASCII(byte[] bt)
        {
            string lin = "";
            for (int i = 0; i < bt.Length; i++)
            {
                lin = lin + bt[i] + " ";
            }
            string[] ss = lin.Trim().Split(new char[] { ' ' });
            char[] c = new char[ss.Length];
            int a;
            for (int i = 0; i < c.Length; i++)
            {
                a = Convert.ToInt32(ss[i]);
                c[i] = Convert.ToChar(a);
            }

            string b = new string(c);
            return b;
        }

        /// <summary>
        /// 应答回复
        /// </summary>
        /// <param name="msgType"></param>
        /// <param name="strArry"></param>
        /// <returns></returns>
        public static string replyMessage(string msgType,string [] strArry)
        {
            string replyContent = string.Empty;
            switch (msgType)
            {
                case "P22":
                    replyContent = string.Format("(P22,{0})", DateTime.UtcNow.ToString("ddMMyyHHmmss"));
                    break;
                case "P43":
                    if (strArry[2].Equals("0"))
                    {
                        replyContent = string.Format("{0}", "P44,1,888888");//密码重置
                    }
                    break;
                case "P45":
                    replyContent = string.Format("(P69,0,{0})", strArry[16]);
                    break;
                case "P52":
                    if (strArry[2].Equals("2"))
                    {
                        replyContent = string.Format("(P52,2,{0})", strArry[3]);
                    }
                    break;
                default:
                    break;
            }
            return replyContent;
        }

        /// <summary>
        /// 应答回复
        /// </summary>
        /// <param name="msgType"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static string replyMessage(string msgType, int index)
        {
            string replyContent = string.Empty;
            switch (msgType)
            {
                case "WLNET5":
                case "WLNET7":
                    replyContent = string.Format("(P69,0,{0})", index);
                    break;
                default:
                    break;
            }
            return replyContent;
        }

        /// <summary>
        /// 时间格式转换
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static DateTime GetDataTime(byte[] bytes)
        {
            return DateTime.ParseExact(ByteToHexStr(bytes), "ddMMyyHHmmss", System.Globalization.CultureInfo.CurrentCulture);
        }
        public static DateTime GetDataTime(string strdate)
        {
            return DateTime.ParseExact(strdate, "ddMMyyHHmmss", System.Globalization.CultureInfo.CurrentCulture);
        }
        /// <summary>
        /// 字节数组转16进制字符串
        /// </summary>
        /// <param name="byteDatas"></param>
        /// <returns></returns>
        public static string ByteToHexStr(byte[] byteDatas)
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < byteDatas.Length; i++)
            {
                builder.Append(string.Format("{0:X2}", byteDatas[i]));
            }
            return builder.ToString().Trim();
        }

        public static LatLon GetLatLon(byte[] bytes)
        {
            try
            {
                LatLon model = new LatLon();
                model.Latitude = GetLatLong60(bytes.Skip(0).Take(4).ToArray());
                model.Longitude = GetLatLong60(bytes.Skip(4).Take(5).ToArray());
                model.LocationType = bytes[8] & 0x01;
                if ((bytes[8] & 0x02) == 0)
                {
                    model.Latitude = -model.Latitude;
                }
                if ((bytes[8] & 0x04) == 0)
                {
                    model.Longitude = -model.Longitude;
                }
                return model;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// 经纬度计算
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static double GetLatLong60(byte[] bytes)
        {
            try
            {
                string locStr = ByteToHexStr(bytes);
                if (locStr.Length < 9)
                {
                    locStr = locStr.PadLeft(9, '0');
                }
                else
                {
                    locStr = locStr.Substring(0, 9);
                }
                var head = Convert.ToDouble(locStr.Remove(3));
                var bodyStr = locStr.Substring(3, locStr.Length - 3);
                var body = Convert.ToDouble(bodyStr) / 10000;
                head += body / 60;
                return head;
            }
            catch (Exception ex)
            {
                return 0;
            }
        }

        public static ushort SwapUInt16(ushort v)
        {
            return (ushort)(((v & 0xff) << 8) | ((v >> 8) & 0xff));
        }

        public static uint SwapUInt32(uint v)
        {
            return (uint)(((SwapUInt16((ushort)v) & 0xffff) << 0x10) |
                           (SwapUInt16((ushort)(v >> 0x10)) & 0xffff));
        }

        /// <summary>
        /// 获取设备状态
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static DeviceStatus GetDeviceStatus(byte[] bytes, int locationType)
        {
            try
            {
                DeviceStatus model = new DeviceStatus();
                //低8位
                if (locationType == 0)
                {
                    model.LocationType = bytes[0] & 0x01;
                }
                else
                {
                    model.LocationType = locationType;
                }
                if ((bytes[1] & 0x02)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_9;
                }
                if ((bytes[1] & 0x04)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_10;
                }
                if ((bytes[1] & 0x08)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_1;
                }
                if ((bytes[1] & 0x10)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_2;
                }
                else
                {
                    model.Alarm = -1;
                }
                model.LockRope = (bytes[1] & 0x40) > 0 ? 0 : 1;
                model.LockStatus = (bytes[1] & 0x80) > 0 ? 0 : 1;
                //高8位
                if ((bytes[0] & 0x01)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_3;
                }
                if ((bytes[0] & 0x02)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_4;
                }
                if ((bytes[0] & 0x04)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_5;
                }
                if ((bytes[0] & 0x08)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_6;
                }
                if ((bytes[0] & 0x10)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_7;
                }
                model.BackCover = (bytes[1] & 0x20) > 0 ? 1 : 0;
                if ((bytes[0] & 0x40)>0)
                {
                    model.Alarm = (int)AlarmTypeEnum.LOCK_ALARM_8;
                }

                return model;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// 转义
        /// </summary>
        /// <param name="inBytes"></param>
        /// <param name="bodyLen"></param>
        /// <returns></returns>
        public static List<byte> unescape(byte[] inBytes, int bodyLen)
        {
            List<byte> list = new List<byte>();
            int i = 0;
            while (i < bodyLen)
            {
                int b = inBytes[i];
                if (b == 0x3D)
                {
                    int nextByte = inBytes[i+1]; ;
                    if (nextByte == 0x14)
                    {
                        list.Add(0x3D ^ 0x14);
                    }
                    else if (nextByte == 0x15)
                    {
                        list.Add(0x3D ^ 0x15);
                    }
                    else if (nextByte == 0x00)
                    {
                        list.Add(0x3D ^ 0x00);
                    }
                    else if (nextByte == 0x11)
                    {
                        list.Add(0x3D ^ 0x11);
                    }
                    else
                    {
                        list.Add(BitConverter.GetBytes(b)[0]);
                        list.Add(BitConverter.GetBytes(nextByte)[0]);
                    }
                    i += 2;
                }
                else
                {
                    list.Add(BitConverter.GetBytes(b)[0]);
                    i++;
                }
            }
            return list;
        }
    }

2.4.返回消息及说明

(1)定位数据
原始数据:

2480405002251911003426032118530329532416031008941d0000000018070c0000000020e04f8b0c56001f00020f0f0f0f0f0f0f0f0f0f00f2028f0157

返回消息:

{
    "DeviceID": "8040500225",
    "MsgType": "Location",
    "DataBody": {
        "ProtocolType": 25,
        "DeviceType": 1,
        "DataType": 1,
        "DataLength": 52,
        "GpsTime": "2021-03-26T18:53:03",
        "Latitude": -29.88736,
        "Longitude": 31.014901666666667,
        "LocationType": 1,
        "Speed": 0,
        "Direction": 0,
        "Mileage": 6151,
        "GpsSignal": 12,
        "GSMSignal": 31,
        "Alarm": -1,
        "AlarmArea": 0,
        "Battery": 79,
        "LockStatus": 0,
        "LockRope": 0,
        "BackCover": 1,
        "MCC": 655,
        "MNC": 1,
        "LAC": 22016,
        "CELLID": 15895308,
        "IMEI": "0F0F0F0F0F0F0F0F",
        "Awaken": 2,
        "Index": 87
    },
    "ReplyMsg": "(P69,0,87)"
}

返回消息描述

{
    "DeviceID":设备ID
    "MsgType":消息类型, 此处为: Location,表示定位数据,
    "DataBody":消息体内容 
    {
        "ProtocolType": 协议版本号,
        "DeviceType": 终端类型号,
        "DataType": 数据类型号( 1 表明最新二进制定位数据, 2 表示报警数据, 3 表示盲区常规二进制定位数据, 4 表示次新二进制定位数据),
        "DataLength": 数据长度,
        "GpsTime": 定位时间( GMT时间),
        "Latitude": 纬度(dd.dddd格式),
        "Longitude": 经度(dd.dddd格式),
        "LocationType": 定位类型( 0: 不定位; 1: GPS定位; 2: 基站定位),
        "Speed": 速度( 单位: km / h),
        "Direction": 方向( 0~360; 0 与360表示正北方向),
        "Mileage": 当前里程值(单位:km),
        "GpsSignal": GPS卫星个数,
        "GSMSignal": GSM信号值,
        "Alarm": 报警类型( - 1: 无告警信息; 1: 锁绳剪断; 2: 震动; 3: 长时间开锁; 4: 开锁密码连续5次错误; 5: 刷非法卡; 6: 低电量; 7: 开后盖; 8: 卡锁; 9: 进区域报警; 10: 出区域报警),
        "AlarmArea": 如果告警与区域有关, 则此处值为区域的ID,
        "Battery": 电量值( 0~100; 255: 充电中),
        "LockStatus": 锁电机状态( 1: 开; 0: 关),
        "LockRope": 锁绳状态( 1: 拔出; 0: 插入),
        "BackCover": 后盖状态( 1: 关闭; 0: 开启),
        "MCC": 国家代码,
        "MNC": 运营商代码,
        "LAC": 位置区域码,
        "CELLID": 基站编号,
        "IMEI": IMEI号码, 全是0F无效,
        "Awaken": 唤醒源( 0 重启,
        1: RTC唤醒, 2: 震动, 3: 开后盖, 4: 锁绳, 5: 接外电, 6: 刷卡, 7: 门磁, 8: VIP短信, 9: 非VIP短信或垃圾短信),
        "Index": 数据流水号
    },
    "ReplyMsg":回复内容( 如果为空字符串, 则不需要给终端回复内容)
}

(2)传感器采集数据(WLNET5)
原始数据(SensorType=1):

28383035303530303037332c312c3134312c574c4e45542c352c322c260321184709649672953949673d1408ff002603211847181020110986660133623c0100d71b00000029

返回消息(SensorType=1):

{
    "DeviceID": "8050500073",
    "MsgType": "WLNET5",
    "DataBody": {
        "GpsTime": "2021-03-26T18:47:09",
        "Latitude": -65.612158333333326,
        "Longitude": -395.61215,
        "LocationType": 0,
        "Speed": 471,
        "Direction": 0,
        "SensorID": "1020110986",
        "LockStatus": 0,
        "LockRope": 0,
        "LockTimes": 0,
        "Index": 102,
        "Voltage": "3.07",
        "Power": 98,
        "RSSI": -60,
        "DateTime": "2021-03-26T18:47:18",
        "SensorType": 1,
        "Temperature": 21.5,
        "Humidity": 27,
        "Event": -1
    },
    "ReplyMsg": "(P69,0,102)"
}

原始数据(SensorType=4):

28373030303331333330392C312C3038312C574C4E45542C352C322C05082115430722348250113550300F0000050821154304E0171E086925018D4E690400400000002A0029

返回消息(SensorType=4):

{
    "DeviceID": "7000313309",
    "MsgType": "WLNET5",
    "DataBody": {
        "GpsTime": "2021-08-05T15:43:07",
        "Latitude": 22.580416666666668,
        "Longitude": 113.91716666666667,
        "LocationType": 1,
        "Speed": 0,
        "Direction": 0,
        "SensorID": "E0171E0869",
        "LockStatus": 0,
        "LockRope": 0,
        "LockTimes": 42,
        "Index": 37,
        "Voltage": "3.97",
        "Power": 78,
        "RSSI": -105,
        "DateTime": "2021-08-05T15:43:04",
        "SensorType": 4,
        "Temperature": 0.0,
        "Humidity": 0,
        "Event": 6
    },
    "ReplyMsg": "(P69,0,37)"
}

返回消息描述

{
    "DeviceID": 设备ID,
    "MsgType": 消息类型, 此处为: WLNET5,表示传感器透传数据,
    "DataBody": 消息体内容
    {
        "GpsTime": 定位时间(GMT时间),
        "Latitude": 纬度(dd.dddd格式),
        "Longitude": 经度(dd.dddd格式),
        "LocationType": 定位类型( 0: 不定位; 1: GPS定位; 2: 基站定位),
        "Speed": 速度( 单位: km / h);0xFF表示速度无效,
        "Direction": 方向( 0~360; 0 与360表示正北方向),
        "SensorID": 传感器ID,
        "LockStatus": 如果从机类型SensorType=4,此是才有效;1:开锁;0:关锁,
        "LockRope": 如果从机类型SensorType=4,此是才有效;1:锁绳拔出;0:锁绳插入,
        "LockTimes": 开锁次数(如果SensorType=4此值才有效),
        "Index": 数据流水号,
        "Voltage": 电压值(单位:V),
        "Power": 传感器电量(0~100;255: 充电中),
        "RSSI": RSSI信号强度,是负数越接近0信号越好,
        "DateTime": 数据采集时间(GMT时间),
        "SensorType": 传感器类型(1:温湿度传感器(JT126);4:从机JT709;),
        "Temperature":温度值(如果SensorType=1此值才有效),
        "Humidity": 湿度值(如果SensorType=1此值才有效),
        "Event": 事件类型(-1:无从机事件;0:关锁事件;1:蓝牙开锁事件;2:NFC开锁事件;3:Lora开锁事件;4:从机锁剪断报警事件;5:按键唤醒事件;6:定时上报事件;7:充电上报事件)
    },
    "ReplyMsg": 回复内容(如果为空字符串,则不需要给终端回复内容)
}

(3)锁事件上报数据(P45)
原始数据:

28373839303632393238342c5034352c3236303332312c3139343933392c32362e3237323033352c4e2c35302e3632313433352c452c412c302e30352c302c342c312c303030303030303030302c312c302c3129

返回消息:

{
    "DeviceID": "7890629284",
    "MsgType": "P45",
    "DataBody": {
        "DateTime": "2021-03-26T19:49:39",
        "Latitude": 26.272035,
        "Longitude": 50.621435,
        "LocationType": 1,
        "Speed": 0,
        "Direction": 0,
        "Event": 4,
        "Status": 1,
        "UnlockFenceID": -1,
        "RFIDNo": "0000000000",
        "PsdErrorTimes": 0,
        "Index": 1,
        "Mileage": 0
    },
    "ReplyMsg": "(P69,0,1)"
}

返回消息描述:

{
    "DeviceID": 设备ID,
    "MsgType": 消息类型, 此处为: P45,表示开关锁事件数据上传,
    "DataBody": 消息体内容
    {
        "DateTime": 事件时间(GMT时间),
        "Latitude": 纬度(dd.dddd格式),
        "Longitude": 经度(dd.dddd格式),
        "LocationType": 定位类型( 0: 不定位; 1: GPS定位; 2: 基站定位),
        "Speed": 速度( 单位: km / h),
        "Direction": 方向( 0~360; 0 与360表示正北方向),
        "Event": 事件类型(1:表示刷授权卡;2:表示刷非法卡;3:表示刷车辆ID卡绑定;4:表示为凭密码开锁;5:表示终端自动关锁记录;6: 动态密码围栏内开锁;7: 蓝牙开锁),
        "Status": 开锁验证(0:开锁密码不正确;1:正常开锁;2:因为开启了围栏开锁,未在围栏内开锁,开锁被拒绝),
        "UnlockFenceID": (-1:开锁与围栏无关;1~10:标识对应的开锁围栏ID),
        "RFIDNo": 刷卡卡号;如果未“0000000000”,则无效,
        "PsdErrorTimes": 开锁密码错误次数,
        "Index": 数据流水号,
        "Mileage": 当前里程值(单位:km)
    },
    "ReplyMsg": 回复内容(如果为空字符串,则不需要给终端回复内容)
}

(4)其他指令回复数据
原始数据:

28373839303632393238342c50333529

返回消息:

{
    "DeviceID": "7890629284",
    "MsgType": "P35",
    "DataBody": "(7890629284,P35)",
    "ReplyMsg": ""
}

返回消息描述:

{
    "DeviceID": 设备ID,
    "MsgType": 消息类型(参见更多的消息类型及其描述,请参阅3.消息类型及消息体内容描述),
    "DataBody": 消息体内容(除定位数据:Location;传感器透传数据:WLNET5;锁事件上报:P45外;其他此处均直接返回指令内容的ASCII字符串),
    "ReplyMsg": 回复内容(如果为空字符串,则不需要给终端回复内容)
}

3.消息类型及消息体内容描述

3.1.P01:查询终端当前的版本号

消息体内容:(示例)
(7591225008,P01,JT701D_20200720_China_Jointech_SIM7600,77%)
消息体内容描述:
7591225008:设备ID
P01:消息类型
JT701D_20200720_China_Jointech_SIM7600:协议版本
77%:当前设备电量

P03:低电休眠控制

消息体内容:(示例)
(7560704001,P03,1,30)
消息体描述:
7560704001:设备ID
P03:消息类型
1:生效;0:不生效
30:设置电量低于30的时候进入休眠,默认31%,可设定范围5%~90%

P04:设置/查询数据上传间隔和休眠自动唤醒间隔

消息体内容:(示例)
(7570101998,P04,30,30)
消息体描述:
7570101998:设备ID
P04:消息类型
30:数据上传间隔,单位秒钟,默认30秒,取值范围5-600
30:休眠自动唤醒间隔,单位分钟,默认30分钟,取值范围30-1440

P06:设置/查询监控中心IP与端口、APN

消息体内容:(示例)
(7570101998,P06,211.162.111.225,10906,CMNET,user,password,1)
消息体描述:
7570101998:设备ID
P06:消息类型
211.162.111.225:监控中心的IP地址
10906:监控中心端口地址,最大65530
CMNET:接入点名称(最长50个字节)
User:APN用户名(最长50个字节)
Password:APN密码(最长50个字节)
1:0表示卡1,1表示卡2

P10:设置/查询终端使用地点与国际标准时间的时差

消息体内容:(示例)
(7570101998,P10,480)
消息体描述:
7570101998:设备ID
P10:消息类型
480:时差值,以分钟为单位.如北京时间与标准时时差为8小时,即为480分钟,取值范围-12*60-13*60,默认0

P11:设置/查询VIP手机号码

消息体内容:(示例)
(7570101998,P11,1,8613910102345)
消息体描述:
7570101998:设备ID
P11:消息类型
1:VIP手机号码索引,取值为1-5,允许有五组VIP手机号码
8613910102345:手机号码,不能超过15位数字,前面需加国际区号,中国为86或者+86.

P12:设置/查询VIP号码是否允许报警

消息体内容:(示例)
(7570101998,P12,1,1,1,1,1)
消息体描述:
7570101998:设备ID
P12:消息类型
1,1,1,1,1:分别对应5个VIP号码是否允许报警;1表示允许对应的VIP号码报警,0表示不允许此VIP号码报警

P13:恢复出厂设置

消息体内容:(示例)
(7570101998,P13)
消息体描述:
7570101998:设备ID
P13:消息类型

P14:读取终端的IMEI号

消息体内容:(示例)
(7570101998,P14,012207004451636)
消息体描述:
7570101998:设备ID
P14:消息类型
012207004451636:终端的IMEI号

P15:终端重启指令

消息体内容:(示例)
(7570101998,P15)
消息体描述:
7570101998:设备ID
P15:消息类型

P22:GPS无效的时候,监控中心对终端授时

消息体内容:(示例)
(7570101998,P22,1)
消息体描述:
7570101998:设备ID
P22:消息类型
1:1表示授时成功,0表示失败,2表示主动请求授时

P22:设置/取消短信、电话可唤醒工作模式

消息体内容:(示例)
(7570101998,P23,1)
消息体描述:
7570101998:设备ID
P23:消息类型
1:1表示设置成功,0表示设置失败.

P24:区域是否有效,及区域名称设置指令

消息体内容:(示例)
(7570101998,P24,10,1,area10)
消息体描述:
7570101998:设备ID
P24:消息类型
10:表示第10个区域.
1:表示有效 ,0表示无效
area10:表示区域名称,最大长度为16个字节.

P29:设置或者查询区域的详细节点信息

消息体内容:(示例)
(7570101998,P29,8,15,1,10,11323.1234…)
消息体描述:
7570101998:设备ID
P29:消息类型
8:表示第八个区域
15:表示总点数
1:表示当前页
10:表示当前页的点数.余下的为各个点的经度与纬度

P30:清除相关的区域

消息体内容:(示例)
(7570101998,P30,1)
消息体描述:
7570101998:设备ID
P30:消息类型
1:1表示清除成功,0表示清除失败.

P31:区域信息设置完毕

消息体内容:(示例)
(7570101998,P31)
消息体描述:
7570101998:设备ID
P31:消息类型

P32:主动进入休眠指令

消息体内容:(示例)
(7570101998,P32)
消息体描述:
7570101998:设备ID
P32:消息类型

P37:查询/设置G-sensor相关参数

消息体内容:(示例)
(7570101998,P37,500)
消息体描述:
7570101998:设备ID
P37:消息类型
500:运动检测门限值,范围是63到500,单位是mg;如果设置为0则为关闭G-sensor相关全部功能;关闭G-sensor功能后,如需重新开启G-sensor功能只需重新设置有效的G-sensor参数即可.默认值126

P38:开锁报警时间间隔设置指令

消息体内容:(示例)
(7570101998,P38,120)
消息体描述:
7570101998:设备ID
P38:消息类型
120:开锁报警时间间隔设置,即从锁电机处于开锁状态时刻算起,如果该状态持续保持超过120分钟,则可触发开锁报警,范围是3到180,单位是分钟;默认为120分钟.

P40:查询/设置GPRS通道和短消息通道的报警开关

消息体内容:(示例)
(7570101998,P40,1,1,1,1,1,1,1,1,1,1,1)
消息体描述:
7570101998:设备ID
P40:消息类型
1,1,1,1,1,1,1,1,1,1,1:从左至右依次为锁挂绳剪断报警、刷非法卡报警、开锁状态保持一段时间报警、指令开锁密码连续输错5次报警、震动报警、进区域报警、出区域报警的开关、低电报警、开后盖报警、卡锁报警;每个报警开关参数可以取值为0,1,2,3,并且可以任意组合,0表示GPRS和SMS报警都关闭,1表示只开启GPRS报警,2表示只开启SMS报警,3表示GPRS和SMS报警都开启.

P41:增删开锁授权号指令

消息体内容:(示例1)
(7570101998,P41,1,30)
消息体描述:
7570101998:设备ID
P41:消息类型
1:1表示增加授权卡号操作;2表示删除授权号,3表示删除所有的授权号
30:30表示当前已存授权卡号总个数;

消息体内容:(示例2)
(7570101998,P41,2,3,0013953759, 0013953758, 0013953757)
消息体描述:
7570101998:设备ID
P41:消息类型
2:查询第2组数据;一共分3组,分别为1~3,查询时每次最多返回20个ID号
3:3表示有3个ID号
0013953759, 0013953758, 0013953757表示该组存储的授权号列表

P42:现场刷卡授权模式配置指令

消息体内容:(示例1)
(7570101998,P42,0)
消息体描述:
7570101998:设备ID
P42:消息类型
1:1表示打开批量增加终端开锁授权号功能, 0表示关闭批量增加终端开锁授权号功能。

消息体内容:(示例2)
(7570101998,P41,2,0013953759,0013953751)
消息体描述:
7570101998:设备ID
P42:消息类型
2:表示终端已存了2个开锁授权号.
0013953759,0013953751:授权卡号.

P44:远程开锁密码修改指令

消息体内容:(示例1)
(7570101998,P44,1)
消息体描述:
7570101998:设备ID
P44:消息类型
1:1:表示修改密码是否成功,1表示成功,0表示失败。

消息体内容:(示例2)
(7570101998,P44,888888)
消息体描述:
7570101998:设备ID
P44:消息类型
888888:当前的设备可开锁的动态密码

P50:电源开关生效控制设置

消息体内容:(示例)
(7570101998,P50,1)
消息体描述:
7570101998:设备ID
P44:消息类型
1:1表示开关有效,默认是1;0表示停用开关.

P52:动态密码指令

消息体内容:(示例1)
(7570101998,P52,0,405935,326387)
消息体描述:
7570101998:设备ID
P52:消息类型
0:指令操作;查询当前产生的动态密码和当前用来开锁的动态密码
405935:表示,还没有被系统确认的动态密码
326387:表示目前用来开锁的动态密码

消息体内容:(示例2)
(7570101998,P52,1,1,0)
消息体描述:
7570101998:设备ID
P52:消息类型
1:1表示设置动态密码功能
1:1查询是否开启动态密码功能;0:表示关闭动态密码功能
0:1表示动态密码开锁必须在区域内才能开锁,即表明如果想要开锁,必须设置区域,0表示动态密码开锁跟区域无关,只要符合动态密码开锁的其它条件就可以开锁

消息体内容:(示例3)
(7570101998,P52,2,405935)
消息体描述:
7570101998:设备ID
P52:消息类型
2:回复上传的动态密码确认指令
405935:需要被确认的动态密码

消息体内容:(示例4)
(7570101998,P52,3,1,0)
消息体描述:
7570101998:设备ID
P52:消息类型
3:发送动态密码开锁
1:1,代表成功,0是失败
0:代表失败次数

P54:查询/设置是否关闭休眠模式指令(设备不休眠)

消息体内容:(示例)
(7570101998,P54,0,0)
消息体描述:
7570101998:设备ID
P54:消息类型
0:0 查询;1 设置
0:0 需要休眠 , 1 开启不休眠

P58:查询和设置RFID卡是否关联电子围栏 (默认关联电子围栏)

消息体内容:(示例)
(7570101998,P58,1,1)
消息体描述:
7570101998:设备ID
P58:消息类型
1:1表示设置,0表示查询
1:1:关联电子围栏,必须在区域内才能刷卡开锁;0表示刷卡开锁跟区域无关,只要符刷卡开锁的条件就可以开锁

P61:设置或查询电量低报警提示的阀值

消息体内容:(示例)
(7570101998,P61,30)
消息体描述:
7570101998:设备ID
P61:消息类型
30:当前阀值

P62:设置或查询里程统计相关参数

消息体内容:(示例)
(7570101998,P62,1,10)
消息体描述:
7570101998:设备ID
P62:消息类型
1:操作参数类型;1:设置一个速度值,低于这个速度里程不会统计;2:同步当前设备里程值
10:当操作参数类型为1,此时为速度值,单位km/h;当操作参数类型为2,此时为里程值,单位km

P63:静态飘移处理功能设置

消息体内容:(示例)
(7570101998,P63,1)
消息体描述:
7570101998:设备ID
P63:消息类型
1:1开启,0关闭(默认)

P68:查询SIM卡的IMSI/ICCID号

消息体内容:(示例)
(7570101998,P65,2,898600220909A0206023)
消息体描述:
7570101998:设备ID
P65:消息类型
2:1:查询IMSI, 2:查询ICCID
898600220909A0206023:SIM卡的IMSI/ICCID号

P70:启用/关闭VIP号码功能

消息体内容:(示例)
(7570101998,P70,1)
消息体描述:
7570101998:设备ID
P70:消息类型
1:0:关闭状态;1:开启状态

WLNET1:查询/设置要监听的从机设备ID号

消息体内容:(示例)
(700160818000,1,001,WLNET,1,20,0217270000,,,,,,,,,)
消息体描述:
700160818000:设备ID
WLNET,1:组合消息类型WLNET1
20:表示当前配置20个从机设备ID号,如果为配置0个ID号表示清除所有配置的ID号
0217270000,,,,,,,,,:从机ID号

WLNET2:查询/设置从机工作时间间隔

消息体内容:(示例)
(700160818000,1,001,WLNET,2,20)
消息体描述:
700160818000:设备ID
WLNET,2:组合消息类型WLNET2
20:20:从机工作时间间隔为20分钟,单位分钟,最小1分钟,最大1440一天,默认5分钟

WLNET3:查询/设置从机发送功率

消息体内容:(示例)
(700160818000,1,001,WLNET,3,2)
消息体描述:
700160818000:设备ID
WLNET,3:组合消息类型WLNET3
2:从机从机发送功率,1~3分别是低中高三个发送功率模式,默认是1低功率

WLNET4:查询/设置从机发送功率

消息体内容:(示例)
(700160818000,1,001,WLNET,4,170828,170829)
消息体描述:
700160818000:设备ID
WLNET,4:组合消息类型WLNET4
170828:网关版本
170829:传感器的软件版本

WLNET6:查询/设置从机温度门限参数

消息体内容:(示例)
(700160818000,1,001,WLNET,6,1,2,10,120,30,15,12)
消息体描述:
700160818000:设备ID
WLNET,6:组合消息类型WLNET6
1:指令功能:0表示查询;1:表示设置
2:参数类型:1表示温度相关参数,2表示设置数据上传方式
10:低温门限值:默认0值无效,温度以1度为单位10~175有效,-40~125度,门限值 – 50 = 实际值,如10- 50 = -40℃。
120:高温门限值:默认0值无效,温度以1度为单位10~175有效,-40~125度,门限值 – 50 = 实际值,如120- 50 = 70℃
30:温度变化时间值:多长时间内温度变化多少的时间值,单位分钟,默认0值无效,一般20~60参考值,最大240,4小时
15:温度变化值:默认0值无效,15表示1.5度,最大250、25度,也就是如30分钟内变化1.5度就报警
12:每天定点上传多少个点:默认0值当前一个点,12表示每天12个点2小时一个点,最多24个点,一般12或24个点参考值

文档更新时间: 2022-04-27 15:09   作者:admin