Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import json
- import re
- import socket
- from collections import Counter
- import serial
- SERIAL_PORT = "COM4"
- BAUD_RATE = 921600
- MAC_ZONE_A = "1a:00:00:00:00:00"
- MAC_ZONE_B = "1a:00:00:00:00:01"
- UDP_IP = "127.0.0.1"
- PORT_ZONE_A = 5005
- PORT_ZONE_B = 5006
- SEND_METADATA = True
- EXPECTED_CSI_LENGTHS = {128, 256, 376, 380, 384, 612}
- MAX_BUFFER_BYTES = 65536
- MAX_FRAME_BYTES = 12000
- CSI_MARKER = b"CSI_DATA"
- MAC_RE = re.compile(r"(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}")
- def normalize_mac(mac):
- return mac.lower()
- def parse_csi_frame(frame_bytes):
- line = frame_bytes.replace(b"\x00", b"").decode("utf-8", errors="ignore").strip()
- if not line.startswith("CSI_DATA"):
- return None, "prefix"
- array_start = line.rfind("[")
- array_end = line.find("]", array_start + 1)
- if array_start < 0 or array_end < 0:
- return None, "brackets"
- header = line[:array_start]
- csi_text = line[array_start + 1:array_end]
- macs = [normalize_mac(m.group(0)) for m in MAC_RE.finditer(header)]
- if not macs:
- return None, "mac"
- tokens = [token.strip() for token in csi_text.split(",")]
- if not tokens or any(token == "" for token in tokens):
- return None, "empty_token"
- csi = []
- for token in tokens:
- try:
- value = int(token)
- except ValueError:
- return None, "bad_int"
- if value < -128 or value > 127:
- return None, "byte_range"
- csi.append(value)
- if len(csi) not in EXPECTED_CSI_LENGTHS:
- return None, f"bad_len:{len(csi)}"
- header_without_macs = MAC_RE.sub("", header)
- header_numbers = [int(x) for x in re.findall(r"-?\d+", header_without_macs)]
- reported_len = header_numbers[-2] if len(header_numbers) >= 2 else None
- first_word_invalid = header_numbers[-1] if len(header_numbers) >= 1 else None
- if reported_len is not None and reported_len != len(csi):
- return None, f"len_mismatch:{reported_len}!={len(csi)}"
- return {
- "macs": macs,
- "csi": csi,
- "len": len(csi),
- "first_word_invalid": first_word_invalid,
- }, None
- def pop_next_frame(buffer):
- start = buffer.find(CSI_MARKER)
- if start < 0:
- if len(buffer) > MAX_FRAME_BYTES:
- del buffer[:-len(CSI_MARKER)]
- return None
- if start > 0:
- del buffer[:start]
- newline = buffer.find(b"\n", len(CSI_MARKER))
- next_marker = buffer.find(CSI_MARKER, 1)
- if next_marker >= 0 and (newline < 0 or next_marker < newline):
- frame = bytes(buffer[:next_marker])
- del buffer[:next_marker]
- return frame
- if newline >= 0:
- frame = bytes(buffer[:newline])
- del buffer[:newline + 1]
- return frame
- if len(buffer) > MAX_FRAME_BYTES:
- del buffer[:len(CSI_MARKER)]
- return b""
- return None
- def main():
- mac_zone_a = normalize_mac(MAC_ZONE_A)
- mac_zone_b = normalize_mac(MAC_ZONE_B)
- sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- print(f"[*] Запуск Dual Router на порту {SERIAL_PORT}...")
- try:
- ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=0)
- ser.setDTR(False)
- ser.setRTS(False)
- ser.reset_input_buffer()
- print("[+] COM-порт открыт. Ожидание эфира...")
- except Exception as exc:
- print(f"[-] Ошибка: {exc}")
- return
- buffer = bytearray()
- packets_a = 0
- packets_b = 0
- dropped = Counter()
- try:
- while True:
- chunk = ser.read(ser.in_waiting or 4096)
- if chunk:
- buffer.extend(chunk)
- if len(buffer) > MAX_BUFFER_BYTES:
- del buffer[: len(buffer) - MAX_BUFFER_BYTES]
- dropped["buffer_overflow"] += 1
- while True:
- frame = pop_next_frame(buffer)
- if frame is None:
- break
- if not frame:
- dropped["oversized_frame"] += 1
- continue
- packet, error = parse_csi_frame(frame)
- if error:
- dropped[error] += 1
- continue
- macs = packet["macs"]
- if mac_zone_a in macs:
- port = PORT_ZONE_A
- packets_a += 1
- elif mac_zone_b in macs:
- port = PORT_ZONE_B
- packets_b += 1
- else:
- dropped["unknown_mac"] += 1
- continue
- payload_obj = packet if SEND_METADATA else packet["csi"]
- payload = json.dumps(payload_obj, separators=(",", ":")).encode("utf-8")
- sock.sendto(payload, (UDP_IP, port))
- total = packets_a + packets_b
- if total % 50 == 0:
- common_drops = ", ".join(f"{k}:{v}" for k, v in dropped.most_common(4))
- print(
- f"OK A:{packets_a} B:{packets_b} | drop {sum(dropped.values())}"
- f" [{common_drops}]",
- end="\r",
- )
- except KeyboardInterrupt:
- print("\n[*] Роутер остановлен.")
- finally:
- ser.close()
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment