Segmentation¶
Automatic segmented message assembly and transmission per Clause 5.2. Messages exceeding the maximum APDU size are transparently split into segments and reassembled. This works in both directions – sending segmented requests and receiving segmented responses.
APDU segmentation and reassembly per ASHRAE 135-2016 Clause 5.2/5.4.
Segmentation Manager¶
APDU segmentation and reassembly logic per ASHRAE 135-2016 Clause 5.2/5.4.
This module contains pure segmentation logic with no I/O dependencies. TSMs drive instances of SegmentSender/SegmentReceiver via method calls.
- exception bac_py.segmentation.manager.SegmentationError(abort_reason, message='')[source]¶
Bases:
ExceptionRaised when segmentation fails (e.g., APDU too long for peer).
- Parameters:
abort_reason (AbortReason)
message (str)
- Return type:
None
- class bac_py.segmentation.manager.SegmentAction(*values)[source]¶
Bases:
EnumAction the receiver should take after processing a segment.
- CONTINUE = 'continue'¶
- SEND_ACK = 'send_ack'¶
- RESEND_LAST_ACK = 'resend'¶
- COMPLETE = 'complete'¶
- ABORT = 'abort'¶
- bac_py.segmentation.manager.in_window(seq_a, seq_b, actual_window_size)[source]¶
Determine if segment seq_a is within the window starting at seq_b.
Per Clause 5.4:
(seqA - seqB) mod 256 < ActualWindowSize.
- bac_py.segmentation.manager.duplicate_in_window(seq_a, seq_b, actual_window_size, proposed_window_size)[source]¶
Determine if segment seq_a is a duplicate the receiver has already seen.
Per Clause 5.4:
Wm < (seqA - seqB) mod 256 <= 255whereWm = max(ActualWindowSize, ProposedWindowSize).
- bac_py.segmentation.manager.compute_max_segment_payload(max_apdu_length, pdu_type)[source]¶
Return the maximum service data bytes that fit in one segment.
- bac_py.segmentation.manager.split_payload(payload, max_segment_size)[source]¶
Split a byte payload into segments of at most max_segment_size bytes.
- Parameters:
- Return type:
- Returns:
List of byte segments. At least one segment is always returned (which may be empty if the payload is empty).
- Raises:
ValueError – If max_segment_size is not positive.
- bac_py.segmentation.manager.check_segment_count(num_segments, max_segments)[source]¶
Check that the segment count does not exceed the peer’s limit.
- class bac_py.segmentation.manager.SegmentSender(segments, invoke_id, service_choice, proposed_window_size, actual_window_size, _window_start_idx=0)[source]¶
Bases:
objectManages the send side of a segmented transaction.
Tracks segments by absolute 0-based index and converts to 8-bit sequence numbers (
index & 0xFF) for the wire protocol.- Parameters:
- classmethod create(payload, invoke_id, service_choice, max_apdu_length, pdu_type, proposed_window_size=16, peer_max_segments=None)[source]¶
Create a SegmentSender by splitting the payload.
- Raises:
SegmentationError – If the segment count exceeds peer_max_segments.
- Return type:
- Parameters:
- class bac_py.segmentation.manager.SegmentReceiver(_segments=<factory>, _expected_idx=0, actual_window_size=16, proposed_window_size=1, service_choice=0, _final_idx=None, _last_ack_seq=0, _window_start_idx=0, _total_bytes=0, created_at=<factory>)[source]¶
Bases:
objectManages reassembly of received segments.
Tracks segments by absolute 0-based index.
- Parameters:
- classmethod create(first_segment_data, service_choice, proposed_window_size, more_follows=True, our_window_size=16)[source]¶
Create a receiver from the first segment (sequence number 0).
- Parameters:
first_segment_data (
bytes) – Payload of the first segment.service_choice (
int) – Service choice from the PDU header.proposed_window_size (
int) – Window size proposed by the sender.more_follows (
bool(default:True)) – The more-follows flag from the first segment.our_window_size (
int(default:16)) – Our preferred window size.
- Return type:
- Returns:
New
SegmentReceiverwith the first segment stored.
- receive_segment(seq_num, data, more_follows)[source]¶
Process a received segment.
Per Clause 5.4, SegmentACKs are sent at window boundaries (when the window is full) or when the transfer is complete, not for every individual segment.
- Parameters:
- Return type:
- Returns:
(action, ack_sequence_number)indicating what the caller should do next. For ABORT, ack_sequence_number is -1. CONTINUE means the segment was stored but no ACK is needed yet.
- reassemble()[source]¶
Concatenate all segments in order.
- Raises:
ValueError – If not all segments have been received.
- Return type: