十六进制格式负载解码

问题描述 投票:0回答:2

我有一个家庭作业,需要解码十六进制有效负载。我一直在尝试解码有效负载,但无法专门针对时间字段生成正确的输出。我希望能帮助您解码它。

任务描述:

为了监控电池的健康状况,我们决定将电池的数据发送到云端。该数据以十六进制格式传输并在我们的 AWS 账户中接收。

数据以十六进制字符串形式传输。每个有效负载由 8 个字节组成。由于空间优化,信息不是字节对齐的。字段可以从字节的中间开始。因此,我们需要位操作来解码有效负载。有效负载未以小端方式签名和编码。下表描述了有效负载中包含的数据字段及其位位置。

例如,类型在第一个字节中的 4 位上进行编码。充电状态在第 6 个字节的 8 位(1 字节)上进行编码。

时间:时间表示数据的时间戳。它是自 UNIX 纪元以来以秒为单位定义的。

State:state是一个字符串,对应的值如下:

0:“关闭电源”

1:“开机”

2:“放电”

3:“充电”

4:“充电完成”

5:“主机模式”

6:“关闭”

7:“错误”

8:“未定义”

充电状态:充电状态表示电池的电量。它是一个浮点数,值介于 0 到 100 之间,精度为 0.5。为了将其存储为整数,将其乘以 2。

电池温度:电池温度代表电池的温度。值可以在 -20 到 100 之间变化。精度为 0.5。为了将其存储为整数,我们添加了 20 并乘以 2。

测试数据示例

输入:F1E6E63676C75000

输出:{“时间”:1668181615,“状态”:“错误”,“充电状态”:99.5,“温度”:20.0}

我的脚本:

import base64
import struct
import json
from datetime import datetime


def lambda_handler(event):
    # Extract device and payload from the event
    device = event["device"]
    payload_hex = event["payload"]

    # Convert hexadecimal payload to bytes
    payload_bytes = bytes.fromhex(payload_hex)

    # Unpack the payload using struct
    unpacked_data = struct.unpack('<I2Bh', payload_bytes)

    # Extract individual fields
    time = unpacked_data[0]

    state = unpacked_data[1]
    state = (state >> 4) & 0x0F

    state_of_charge = unpacked_data[2] / 2.0
    temperature = (unpacked_data[3] / 2.0) - 20.0

    # Mapping state values to corresponding strings
    state_mapping = {
    0: "power off",
    1: "power on",
    2: "discharge",
    3: "charge",
    4: "charge complete",
    5: "host mode",
    6: "shutdown",
    7: "error",
    8: "undefined"
    }

    # Create the output dictionary
    output_data = {
    "device": device,
    "time": time,
    "state": state_mapping.get(state, "unknown"),
    "state_of_charge": round(state_of_charge, 1),
    "temperature": round(temperature, 1)
    }

    # Log the output data to stdout
    print(json.dumps(output_data))

event = {'device': 'device1', 'payload': '6188293726C75C00'}
lambda_handler(event)

我目前正在努力获得正确的输出,这不仅仅取决于基于上述逻辑的 unpacked_data[0]。

python hex decimal decoding
2个回答
1
投票

struct
库并不是特别有用,因为数据不是字节对齐的,因此您需要执行一些手动位工作。如果您以前没有见过这个词,半个字节称为 nibble;该程序需要将字节拆分为半字节。

# parse the hexadecimal payload
payload = bytes.fromhex("6188293726C75C00")

# split the payload into individual bytes
b0, b1, b2, b3, b4, b5, b6, b7 = payload

# extact the type from the least significant 4 bits on the first byte
type = b0 & 0x0F

# extract the time from the next 32 bits
time_nibbles = [
    b0 >> 4, # bits 0 - 4
    b1 & 0x0F, b1 >> 4, # bits 5 - 12
    b2 & 0x0F, b2 >> 4, # bits 13 - 20
    b3 & 0x0F, b3 >> 4, # bits 21 - 28
    b4 & 0x0F, # bits 29 - 32
]
time_bytes = [low | (high << 4) for low, high in zip(time_nibbles[::2], time_nibbles[1::2])]
time = int.from_bytes(time_bytes, byteorder="little")
print(f"{time = }")

# extract state
state = b4 >> 4
print(f"{state = }")

# extract state of charge
state_of_charge = b5 / 2
print(f"{state_of_charge = }")

# extract battery temperature
battery_temperature = (int.from_bytes([b6, b7], byteorder="little") / 2) - 20
print(f"{battery_temperature = }")

输出:

time = 1668454534
state = 2
state_of_charge = 99.5
battery_temperature = 26.0

0
投票

您可以使用支持位域的

ctypes
结构。向类添加属性也可以为您进行计算:

import ctypes as ct
import struct

class _Data(ct.Structure):
    # Bitfield definitions
    _fields_ = (('type', ct.c_uint64, 4),
                ('time', ct.c_uint64, 32),
                ('state', ct.c_uint64, 4),
                ('soc', ct.c_uint64, 8),
                ('temp', ct.c_uint64, 8))

    states = ('power off', 'power on', 'discharge', 'charge',
              'charge complete', 'host mode', 'shutdown',
              'error', 'undefined')

class Data(ct.Union):
    _fields_ = (('_u', ct.c_uint64),
                ('_d', _Data))

    def __init__(self, data):
        # Take raw byte data and unpack as 64-bit little-endian
        self._u = struct.unpack('<Q', data)[0]

    @property
    def type(self):
        return self._d.type

    @property
    def time(self):
        return self._d.time

    @property
    def state(self):
        try:
            return self._d.states[self._d.state]
        except IndexError:
            return 'invalid'

    @property
    def state_of_charge(self):
        return self._d.soc / 2

    @property
    def battery_temperature(self):
        return self._d.temp / 2 - 20

    def __repr__(self):
        return (f'Data(type={self.type}, '
                     f'time={self.time}, '
                     f'state={self.state!r}, '
                     f'state_of_charge={self.state_of_charge}, '
                     f'temperature={self.battery_temperature})')

    def as_dict(self):
        return {'type': self.type,
                'time': self.time,
                'state': self.state,
                'state_of_charge': self.state_of_charge,
                'temperature': self.battery_temperature}

data = Data(bytes.fromhex('F1E6E63676C75000'))
print(data)
print(data.as_dict())

输出:

Data(type=1, time=1668181615, state='error', state_of_charge=99.5, temperature=20.0)
{'type': 1, 'time': 1668181615, 'state': 'error', 'state_of_charge': 99.5, 'temperature': 20.0}
© www.soinside.com 2019 - 2024. All rights reserved.