You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
11 KiB
Python
262 lines
11 KiB
Python
import struct
|
|
from typing import MutableMapping, Union
|
|
from . import tools
|
|
|
|
|
|
def parse(response: bytes) -> MutableMapping[str, Union[int, bytes, str, float]]:
|
|
# print(response, 1111111111111)
|
|
out: MutableMapping[str, Union[int, bytes, str, float]] = dict()
|
|
out["full_response"] = response
|
|
if response[0] & 0x80: # long address
|
|
out["address"] = int.from_bytes(response[1:6], "big")
|
|
response = response[6:]
|
|
else: # short address
|
|
out["address"] = response[1]
|
|
response = response[2:]
|
|
command, bytecount, response_code, device_status = struct.unpack_from(">BBBB", response)
|
|
out["device_status"] = device_status
|
|
out["response_code"] = response_code
|
|
|
|
if response_code == 0:
|
|
out["status"] = "success"
|
|
else:
|
|
out["status"] = "error"
|
|
out["error"] = f"Device responded with error code: {response_code}"
|
|
|
|
data = response[4 : 4 + bytecount]
|
|
out["command"] = command
|
|
out["command_name"] = f"hart_command_{command}"
|
|
out["bytecount"] = bytecount
|
|
out["data"] = data
|
|
|
|
# handle error return
|
|
if bytecount == 2:
|
|
return out
|
|
|
|
# universal commands
|
|
if command in [0, 11]:
|
|
out["command_name"] = "read_unique_identifier"
|
|
out["manufacturer_id"] = data[1]
|
|
out["manufacturer_device_type"] = data[2]
|
|
out["number_response_preamble_characters"] = data[3]
|
|
out["universal_command_revision_level"] = data[4]
|
|
out["transmitter_specific_command_revision_level"] = data[5]
|
|
out["software_revision_level"] = data[6]
|
|
out["hardware_revision_level"] = data[7]
|
|
out["device_id"] = int.from_bytes(data[9:12], "big")
|
|
elif command in [1]:
|
|
out["command_name"] = "read_primary_variable"
|
|
units, variable = struct.unpack_from(">Bf", data)
|
|
out["primary_variable_units"] = units
|
|
out["primary_variable"] = variable
|
|
elif command in [2]:
|
|
out["command_name"] = "read_loop_current_and_percent"
|
|
analog_signal, primary_variable = struct.unpack_from(">ff", data)
|
|
out["analog_signal"] = analog_signal
|
|
out["primary_variable"] = primary_variable
|
|
elif command in [3]:
|
|
out["command_name"] = "read_dynamic_variables_and_loop_current"
|
|
if len(data) >= 4:
|
|
analog_signal = struct.unpack_from(">f", data, 0)[0]
|
|
out["analog_signal"] = analog_signal
|
|
|
|
# 计算剩余数据长度并确定支持的变量数量
|
|
remaining_length = len(data) - 4
|
|
variable_count = remaining_length // 5 # 每个变量占5字节 (1字节单位 + 4字节值)
|
|
|
|
# 动态解析变量数据
|
|
offset = 4 # 从第4字节开始是变量数据
|
|
for i in range(variable_count):
|
|
if offset + 5 > len(data): # 确保有足够数据
|
|
break
|
|
|
|
# 解析单位枚举值 (1字节)
|
|
unit_enum = struct.unpack_from(">B", data, offset)[0]
|
|
offset += 1
|
|
|
|
# 解析变量值 (4字节)
|
|
variable_value = struct.unpack_from(">f", data, offset)[0]
|
|
offset += 4
|
|
|
|
# 根据变量索引设置字段名
|
|
if i == 0:
|
|
out["primary_variable_units"] = unit_enum
|
|
out["primary_variable"] = variable_value
|
|
elif i == 1:
|
|
out["secondary_variable_units"] = unit_enum
|
|
out["secondary_variable"] = variable_value
|
|
elif i == 2:
|
|
out["tertiary_variable_units"] = unit_enum
|
|
out["tertiary_variable"] = variable_value
|
|
elif i == 3:
|
|
out["quaternary_variable_units"] = unit_enum
|
|
out["quaternary_variable"] = variable_value
|
|
elif command in [6]:
|
|
out["command_name"] = "write_polling_address"
|
|
polling_address = struct.unpack_from(">B", data)[0]
|
|
out["polling_address"] = polling_address
|
|
elif command in [12]:
|
|
# print(data)
|
|
out["command_name"] = "read_message"
|
|
out["message"] = data[0:24]
|
|
# print(out, 111111111)
|
|
elif command in [13]:
|
|
out["command_name"] = "read_tag_descriptor_date"
|
|
# Tag is 8 chars packed into 6 bytes
|
|
out["device_tag_name"] = tools.unpack_packed_ascii(data[0:6], 8)
|
|
# Descriptor is 16 chars packed into 12 bytes
|
|
out["device_descriptor"] = tools.unpack_packed_ascii(data[6:18], 16)
|
|
# Date is 3 bytes: day, month, year (year is offset from 1900)
|
|
day, month, year_offset = struct.unpack_from(">BBB", data, 18)
|
|
out["date"] = {"day": day, "month": month, "year": year_offset + 1900}
|
|
elif command in [14]:
|
|
out["command_name"] = "read_primary_variable_information"
|
|
out["serial_no"] = data[0:3]
|
|
sensor_limits_code, upper_limit, lower_limit, min_span = struct.unpack_from(
|
|
">xxxBfff", data
|
|
)
|
|
out["sensor_limits_code"] = sensor_limits_code
|
|
out["upper_limit"] = upper_limit
|
|
out["lower_limit"] = lower_limit
|
|
out["min_span"] = min_span
|
|
elif command in [15]:
|
|
out["command_name"] = "read_output_information"
|
|
(
|
|
alarm_code,
|
|
transfer_fn_code,
|
|
primary_variable_range_code,
|
|
upper_range_value,
|
|
lower_range_value,
|
|
damping_value,
|
|
write_protect,
|
|
private_label,
|
|
) = struct.unpack_from(">BBBfffBB", data)
|
|
out["alarm_code"] = alarm_code
|
|
out["transfer_fn_code"] = transfer_fn_code
|
|
out["primary_variable_range_code"] = primary_variable_range_code
|
|
out["upper_range_value"] = upper_range_value
|
|
out["lower_range_value"] = lower_range_value
|
|
out["damping_value"] = damping_value
|
|
out["write_protect"] = write_protect
|
|
out["private_label"] = private_label
|
|
elif command in [16]:
|
|
out["command_name"] = "read_final_assembly_number"
|
|
# print(data)
|
|
out["final_assembly_no"] = int.from_bytes(data[0:3], "big")
|
|
elif command in [17]:
|
|
out["command_name"] = "write_message"
|
|
out["message"] = data[0:24]
|
|
elif command in [18]:
|
|
out["command_name"] = "write_tag_descriptor_date"
|
|
out["device_tag_name"] = data[0:6]
|
|
out["device_descriptor"] = data[6:18]
|
|
out["date"] = data[18:21]
|
|
elif command in [19]:
|
|
out["command_name"] = "write_final_assembly_number"
|
|
out["final_assembly_no"] = int.from_bytes(data[0:2], "big")
|
|
elif command in [34]:
|
|
out["command_name"] = "write_primary_variable_damping"
|
|
out["damping_time"] = struct.unpack_from(">f", data)[0]
|
|
elif command in [35]:
|
|
out["command_name"] = "write_primary_variable_range"
|
|
out["upper_range"] = struct.unpack_from(">f", data)[0]
|
|
out["lower_range"] = struct.unpack_from(">f", data[4:])[0]
|
|
elif command in [36]:
|
|
out["command_name"] = "calibrate_20ma"
|
|
# 20mA校准命令无返回数据
|
|
elif command in [37]:
|
|
out["command_name"] = "calibrate_4ma"
|
|
# 4mA校准命令无返回数据
|
|
elif command in [40]:
|
|
out["command_name"] = "set_fixed_current_output"
|
|
if len(data) > 0:
|
|
out["fixed_output_enabled"] = data[0] == 1
|
|
if len(data) > 1:
|
|
out["fixed_current_value"] = struct.unpack_from(">f", data[1:])[0]
|
|
elif command in [43]:
|
|
out["command_name"] = "calibrate_zero_point"
|
|
# 零点校准命令无返回数据
|
|
elif command in [44]:
|
|
out["command_name"] = "write_primary_variable_units"
|
|
out["units_code"] = data[0]
|
|
elif command in [45]:
|
|
out["command_name"] = "trim_loop_current_4ma"
|
|
out["measured_current"] = struct.unpack_from(">f", data)[0]
|
|
elif command in [46]:
|
|
out["command_name"] = "trim_loop_current_20ma"
|
|
out["measured_current"] = struct.unpack_from(">f", data)[0]
|
|
elif command in [47]:
|
|
out["command_name"] = "write_primary_variable_output_function"
|
|
out["transfer_function_code"] = data[0]
|
|
|
|
|
|
# COMMON COMMANDS
|
|
|
|
# elif command in [37]:
|
|
# out["command_name"] = "set_primary_variable_lower_range_value"
|
|
# out[""] =
|
|
# request data bytes = NONE, response data bytes = NONE
|
|
|
|
# elif command in [38]:
|
|
# out["command_name"] = "reset_configuration_changed_flag"
|
|
# out[""] =
|
|
# request data bytes = NONE, response data bytes = NONE
|
|
|
|
# elif command in [42]:
|
|
# out["command_name"] = "perform_master_reset"
|
|
# out[""] =
|
|
# request data bytes = NONE, response data bytes = NONE
|
|
|
|
# elif command in [48]:
|
|
# out["command_name"] = "read_additional_transmitter_status"
|
|
# out[""] =
|
|
# request data bytes = NONE, response data bytes = NONE
|
|
|
|
elif command in [50]:
|
|
out["command_name"] = "read_dynamic_variable_assignments"
|
|
(
|
|
primary_transmitter_variable,
|
|
secondary_transmitter_variable,
|
|
tertiary_transmitter_variable,
|
|
quaternary_transmitter_variable,
|
|
) = struct.unpack_from(">BBBB", data)
|
|
out["primary_transmitter_variable"] = primary_transmitter_variable
|
|
out["secondary_transmitter_variable"] = secondary_transmitter_variable
|
|
out["tertiary_transmitter_variable"] = tertiary_transmitter_variable # NOT USED
|
|
out["quaternary_transmitter_variable"] = quaternary_transmitter_variable # NOT USED
|
|
elif command in [59]:
|
|
out["command_name"] = "write_number_of_response_preambles"
|
|
n_response_preambles = struct.unpack_from(">B", data)[0]
|
|
out["n_response_preambles"] = n_response_preambles
|
|
elif command in [66]:
|
|
out["command_name"] = "toggle_analog_output_mode"
|
|
(
|
|
analog_output_selection,
|
|
analog_output_units_code,
|
|
fixed_analog_output,
|
|
) = struct.unpack_from(">BBf", data)
|
|
out["analog_output_selection"] = analog_output_selection
|
|
out["analog_output_units_code"] = analog_output_units_code
|
|
out["fixed_analog_output"] = fixed_analog_output
|
|
elif command in [67]:
|
|
out["command_name"] = "trim_analog_output_zero"
|
|
analog_output_code, analog_output_units_code, measured_analog_output = struct.unpack_from(
|
|
">BBf", data
|
|
)
|
|
out["analog_output_code"] = analog_output_code
|
|
out["analog_output_units_code"] = analog_output_units_code
|
|
out["measured_analog_output"] = measured_analog_output
|
|
elif command in [68]:
|
|
out["command_name"] = "trim_analog_output_span"
|
|
analog_output_code, analog_output_units_code, measured_analog_output = struct.unpack_from(
|
|
">BBf", data
|
|
)
|
|
out["analog_output_code"] = analog_output_code
|
|
out["analog_output_units_code"] = analog_output_units_code
|
|
out["measured_analog_output"] = measured_analog_output
|
|
elif command in [123]:
|
|
out["command_name"] = "select_baud_rate"
|
|
out["baud_rate"] = int.from_bytes(data, "big")
|
|
|
|
return out
|