Exemple #1
0
        /// <summary>
        /// Encode an OpenThings <see cref="Message"/>
        /// </summary>
        /// <param name="message">The <see cref="Message"/> message to encode</param>
        /// <returns>A <see cref="IList{T}"/> of the encoded OpentThings message bytes</returns>
        public IList <byte> Encode(Message message)
        {
            Crc16Ccitt crc16Ccitt = new Crc16Ccitt(0);

            List <byte> encoded = new List <byte>
            {
                0, // Length
                message.Header.ManufacturerId,
                message.Header.ProductId,
                0, // Pip Msb
                0, // Pip Lsb
                (byte)(message.Header.SensorId >> 16),
                (byte)((message.Header.SensorId & 0xFF00) >> 8),
                (byte)(message.Header.SensorId & 0xFF)
            };

            foreach (var record in message.Records)
            {
                encoded.Add((byte)record.Parameter.Identifier);
                encoded.AddRange(record.Data.Encode());
            }

            encoded.Add(0);

            var crcBytes = crc16Ccitt.ComputeChecksumBytes(encoded.Skip(5).ToArray());

            encoded.AddRange(crcBytes.Reverse());

            encoded[0] = (byte)(encoded.Count - 1);

            return(encoded);
        }
        /// <summary>
        /// Decode a <see cref="IList{T}"/> of bytes representing the OpenThings message payload
        /// </summary>
        /// <param name="payload">The OpenThings payload message bytes</param>
        /// <param name="pidMaps">A mapping of PID to manufacture Ids to enable linear shift encryption decoding</param>
        /// <returns>A decode <see cref="Message"/></returns>
        public Message Decode(IList <byte> payload, IList <PidMap> pidMaps)
        {
            Crc16Ccitt crc16Ccitt = new Crc16Ccitt(0);

            if (payload == null || payload.Count == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(payload));
            }

            if (payload.Count < 11)
            {
                throw new OpenThingsException($"Invalid buffer length [{payload.Count}] too short");
            }

            if (payload[0] < 11)
            {
                throw new OpenThingsException($"Invalid OpenThings Header length [{payload[0]}]");
            }

            if (payload[0] > payload.Count)
            {
                throw new OpenThingsException($"Invalid OpenThings Header length [{payload[0]}] Buffer Length: [{payload.Count}]");
            }

            var pip = BitConverter.ToUInt16(payload.Skip(3).Take(2).Reverse().ToArray(), 0);

            var header = new MessageHeader(payload[1], payload[2], pip, 0x0);

            var body = payload.Take(payload[0] + 1).Skip(5).ToList();

            if (pip != 0)
            {
                var pidMap = pidMaps.FirstOrDefault(_ => _.ManufacturerId == header.ManufacturerId);

                if (pidMap == null)
                {
                    throw new OpenThingsException($"No [{nameof(PidMap)}] found for manufacture id [0x{header.ManufacturerId:X}]");
                }

                body = Decrypt(body, pidMap.Pid, header.Pip);

                header.SetSensorId(body.Take(3).ToList());
            }
            else
            {
                header.SetSensorId(body.Take(3).ToList());
            }

            ushort crcActual   = (ushort)((body.Skip(body.Count - 2).First() << 8) + body.Skip(body.Count - 1).First());
            ushort crcExpected = crc16Ccitt.ComputeChecksum(body.Take(body.Count - 2).ToArray());

            if (crcActual != crcExpected)
            {
                throw new OpenThingsException($"Invalid Crc Expected: [0x{crcExpected:X4}] Actual: [0x{crcActual:X4}]");
            }

            var message = new Message(header);

            ParseMessageRecords(message, body.Skip(3).Take(body.Count - 6).ToList());

            return(message);
        }