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 re
import sqlite3
import telnetlib
import socket
import time
from dataclasses import dataclass
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
# -------------------------
def telnet_read_until_prompt(tn: telnetlib.Telnet, timeout: int) -> bytes:
# robust: lesen bis '>' auftaucht, nicht zwingend am Zeilenende
return tn.read_until(PROMPT_END, timeout=timeout)
PROMPT_END = b">"
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).
Optionales Login best-effort, falls Username/Password gesetzt sind.
Liest bis:
- Prompt '>' erkannt wird, ODER
- Gegenseite schließt (recv == b''), ODER
- read_timeout abläuft (socket.timeout)
"""
tn = telnetlib.Telnet()
tn.open(dev.host, dev.port, timeout=connect_timeout)
sock.settimeout(read_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
if dev.username:
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)
# 2) kurzer Versuch: gibt's schon Output/Prompt?
pre = tcp_read_all(s, read_timeout=prompt_timeout)
tn.write(cmd.encode("utf-8") + b"\n")
out = tn.read_until(PROMPT_END, timeout=read_timeout)
tn.close()
return out
# 3) falls kein Prompt: nochmal Enter + Pause
if PROMPT_END not in pre:
s.sendall(b"\r\n")
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]:
"""
@ -317,11 +354,16 @@ def poll_device(con: sqlite3.Connection, dev: DeviceCfg, cc_map: Dict[str, str],
fields_filled = 0
try:
raw = telnet_fetch_ostatus(dev, cmd, connect_timeout, prompt_timeout, read_timeout)
text = raw.decode("utf-8", errors="replace")
rows = parse_ostatus(text)
ts = utc_now_iso()
outlets_received = len(rows)
raw = tcp_fetch_ostatus_raw(
host=dev.host,
port=dev.port,
cmd=cmd,
connect_timeout=connect_timeout,
prompt_timeout=prompt_timeout,
read_timeout=read_timeout,
enter_first=True,
prompt_pause_sec=2.0
)
for r in rows:
outlet_name = r["outlet_name"].strip()