zu socket

This commit is contained in:
2026-02-08 20:01:06 +01:00
parent e727f37c27
commit a84497eb32

102
mmp_logger.py Normal file → Executable file
View File

@ -7,7 +7,7 @@ import json
import os import os
import re import re
import sqlite3 import sqlite3
import telnetlib import socket
import time import time
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
@ -207,36 +207,73 @@ def cost_center_for(outlet_name: str, cc_map: Dict[str, str]) -> Tuple[str, str]
# Telnet / Fetch / Parse # Telnet / Fetch / Parse
# ------------------------- # -------------------------
def telnet_read_until_prompt(tn: telnetlib.Telnet, timeout: int) -> bytes: PROMPT_END = b">"
# robust: lesen bis '>' auftaucht, nicht zwingend am Zeilenende
return tn.read_until(PROMPT_END, timeout=timeout)
def telnet_fetch_ostatus(dev: DeviceCfg, cmd: str, connect_timeout: int, prompt_timeout: int, read_timeout: int) -> bytes: def tcp_read_all(sock: socket.socket, read_timeout: int) -> bytes:
""" """
Öffnet Telnet, wartet auf Prompt ('>'), sendet cmd, liest Antwort (bis nächstes Prompt). Liest bis:
Optionales Login best-effort, falls Username/Password gesetzt sind. - Prompt '>' erkannt wird, ODER
- Gegenseite schließt (recv == b''), ODER
- read_timeout abläuft (socket.timeout)
""" """
tn = telnetlib.Telnet() sock.settimeout(read_timeout)
tn.open(dev.host, dev.port, timeout=connect_timeout) buf = bytearray()
while True:
try:
chunk = sock.recv(4096)
except socket.timeout:
break
if not chunk:
# Session geschlossen
break
buf += chunk
# Prompt irgendwo im Buffer?
if PROMPT_END in buf:
break
return bytes(buf)
buf = telnet_read_until_prompt(tn, timeout=prompt_timeout) def tcp_fetch_ostatus_raw(
host: str,
port: int,
cmd: str,
connect_timeout: int,
prompt_timeout: int,
read_timeout: int,
enter_first: bool = True,
prompt_pause_sec: float = 2.0,
cmd_pause_sec: float = 0.2,
) -> bytes:
"""
Raw TCP (seriell->TCP). Ablauf:
1) connect
2) optional: ENTER senden, kurz warten
3) kurz lesen (prompt_timeout)
- falls kein '>' kommt: nochmal ENTER, Pause
4) cmd senden, kurze Pause
5) lesen bis prompt / close / timeout
"""
with socket.create_connection((host, port), timeout=connect_timeout) as s:
# 1) "Session wecken"
if enter_first:
s.sendall(b"\r\n")
time.sleep(prompt_pause_sec)
# Best-effort login flow # 2) kurzer Versuch: gibt's schon Output/Prompt?
if dev.username: pre = tcp_read_all(s, read_timeout=prompt_timeout)
low = buf.lower()
if b"login" in low or b"user" in low:
tn.write(dev.username.encode("utf-8") + b"\n")
buf = telnet_read_until_prompt(tn, timeout=prompt_timeout)
if dev.password:
low = buf.lower()
if b"pass" in low:
tn.write(dev.password.encode("utf-8") + b"\n")
buf = telnet_read_until_prompt(tn, timeout=prompt_timeout)
tn.write(cmd.encode("utf-8") + b"\n") # 3) falls kein Prompt: nochmal Enter + Pause
out = tn.read_until(PROMPT_END, timeout=read_timeout) if PROMPT_END not in pre:
tn.close() s.sendall(b"\r\n")
return out time.sleep(prompt_pause_sec)
pre += tcp_read_all(s, read_timeout=prompt_timeout)
# 4) Kommando senden
s.sendall(cmd.encode("utf-8") + b"\r\n")
time.sleep(cmd_pause_sec)
# 5) Antwort lesen (Bridge kann danach schließen)
out = tcp_read_all(s, read_timeout=read_timeout)
return pre + out
def parse_ostatus(text: str) -> List[dict]: def parse_ostatus(text: str) -> List[dict]:
""" """
@ -317,11 +354,16 @@ def poll_device(con: sqlite3.Connection, dev: DeviceCfg, cc_map: Dict[str, str],
fields_filled = 0 fields_filled = 0
try: try:
raw = telnet_fetch_ostatus(dev, cmd, connect_timeout, prompt_timeout, read_timeout) raw = tcp_fetch_ostatus_raw(
text = raw.decode("utf-8", errors="replace") host=dev.host,
rows = parse_ostatus(text) port=dev.port,
ts = utc_now_iso() cmd=cmd,
outlets_received = len(rows) connect_timeout=connect_timeout,
prompt_timeout=prompt_timeout,
read_timeout=read_timeout,
enter_first=True,
prompt_pause_sec=2.0
)
for r in rows: for r in rows:
outlet_name = r["outlet_name"].strip() outlet_name = r["outlet_name"].strip()