Files
templates/python/set_devices/can_ping.py

84 lines
2.7 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.
# Адресный PING SETProtocol v2 через сегментированный classic CAN. Ответ сопоставляется с
# ожидаемым обменом; успешная отправка CAN-пакета ещё не подтверждает доступность прикладного
# сервиса.
"""Addressed SETProtocol v2 PING exchange over segmented classic CAN."""
from __future__ import annotations
from dataclasses import dataclass
from time import perf_counter
from setprotocol.can import CanAddress, CanFrame, CanReassembler, segment
from setprotocol.core import (
Frame as SetFrame,
FrameFlag,
MessageType,
SetProtocolError,
Status,
build_frame,
decode_datagram,
decode_response,
)
from . import can_transport as tr
@dataclass(frozen=True)
class CanPingReply:
node_id: int
uptime_ms: int
protocol_version: int = 2
class CanPingExchange:
def __init__(self, node_id: int, sequence: int = 1, channel: int = 1) -> None:
if not 0 <= node_id <= 0xFF:
raise ValueError("SETP node ID вне диапазона 0..255")
self.node_id = node_id
self.sequence = sequence & 0xFFFF or 1
self.channel = channel
self._reassembler = CanReassembler()
def request(self) -> list[tr.Frame]:
packet = build_frame(
SetFrame(
MessageType.PING,
self.sequence,
flags=FrameFlag.ACK_REQUIRED | FrameFlag.PRIORITY,
source=0,
destination=self.node_id,
)
)
return [
tr.build_frame(item.can_id, item.data, to_can=True)
for item in segment(packet, CanAddress(self.node_id, 0, 1, self.channel))
]
def feed(self, frame: tr.Frame) -> CanPingReply | None:
if frame.to_can or not frame.ide:
return None
try:
address = CanAddress.unpack(frame.can_id)
if address.source != self.node_id or address.destination != 0:
return None
packet = self._reassembler.feed(
CanFrame(frame.can_id, frame.data), int(perf_counter() * 1000)
)
if packet is None:
return None
response = decode_datagram(packet)
status, body = decode_response(response)
except SetProtocolError:
return None
if (
response.message_type != MessageType.PING
or response.sequence != self.sequence
or response.source != self.node_id
or response.destination != 0
or status != Status.OK
or len(body) != 4
):
return None
return CanPingReply(self.node_id, int.from_bytes(body, "little"))