Files
templates/python/set_devices/firmware.py

82 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""@file firmware.py
@brief Проверка образов и переносимая машина блочной передачи прошивки.
Реализует загрузку и валидацию образов прошивки из различных форматов файлов,
проверку целостности данных, и логику передачи образов на устройство.
"""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from firmware_image.core import FirmwareImageError, contiguous_image, image_digest
@dataclass(frozen=True)
class FirmwareImage:
"""@brief Загруженный образ прошивки с метаданными.
@param path Путь к исходному файлу образа
@param data Бинарные данные образа
@param base_address Адрес начала загрузки (для Intel HEX формата)
@param version Номер версии прошивки
"""
path: Path
data: bytes
base_address: int = 0
version: int = 0
@property
def crc32(self) -> int:
return int(image_digest(self.data)[0], 16)
@property
def sha256(self) -> str:
return image_digest(self.data)[1].lower()
@classmethod
def load(cls, path: str | Path, maximum_size: int = 2 * 1024 * 1024) -> "FirmwareImage":
file_path = Path(path)
raw = file_path.read_bytes()
if file_path.suffix.lower() == ".hex":
data, base = _parse_intel_hex(raw, maximum_size)
elif file_path.suffix.lower() == ".bin":
data, base = raw, 0
else:
raise FirmwareImageError("Поддерживаются только .bin и Intel HEX")
if not data:
raise FirmwareImageError("Образ пуст")
if len(data) > maximum_size:
raise FirmwareImageError(f"Размер образа превышает {maximum_size} байт")
return cls(file_path, data, base)
def _parse_intel_hex(text: str, maximum_size=2 * 1024 * 1024) -> tuple[bytes, int]:
return contiguous_image(text, maximum_size)
@dataclass
class FirmwareTransfer:
image: FirmwareImage
block_size: int = 480
offset: int = 0
def next_block(self) -> tuple[int, bytes] | None:
if self.offset >= len(self.image.data):
return None
offset = self.offset
return offset, self.image.data[offset : offset + self.block_size]
def acknowledge(self, next_offset: int) -> None:
current = self.next_block()
if current is None or next_offset != current[0] + len(current[1]):
raise FirmwareImageError("МК подтвердил неожиданный offset")
self.offset = next_offset
@property
def percent(self) -> int:
return int(self.offset * 100 / len(self.image.data))