""" DWD NAVTEX Funkmodul (radio) noch nicht implementiert mangels Hardware B1: Transmitter identity B2: Subject indicator character A Navigational warnings B Meteorological warnings C Ice reports D Search & rescue information, and pirate warnings E Meteorological forecasts F Pilot service messages G AIS messages (formerly Decca messages[6]) H LORAN messages I Not used (formerly OMEGA messages[6]) J SATNAV messages (i.e. GPS or GLONASS) K Other electronic navaid messages L Navigational warnings — additional to letter A (Should not be rejected by the receiver) T Test transmissions (UK only — not official) V Notice to fishermen (U.S. only — currently not used) W Environmental (U.S. only — currently not used) X Special services — allocation by IMO NAVTEX Panel Y Special services — allocation by IMO NAVTEX Panel Z No message on hand B3, B4: Serial number 01-99, 00: immediate printout Timecode: DDHHmm UTC MMM YY Sqlite database schema .schema message msgid TEXT PRIMARY KEY timestamp TEXT station TEXT content TEXT received TEXT """ import os import http.client import ssl import re import sqlite3 import datetime from gi.repository import GLib class NAVTEX(): def __init__(self, logger, cfg): self.log = logger self.source = cfg['ntx_source'].lower() # net | radio self.maxage = cfg['ntx_housekeeping'] # message hold time in hours self.running = False dbpath = os.path.join(cfg['histpath'], "navtex.db") try: self.conn = sqlite3.connect(dbpath) self.cur = self.conn.cursor() except: self.log.error(f"Failed to open local database: {dbpath}") return # Datenbank erstellen wenn nicht vorhanden sql = "SELECT name FROM sqlite_master WHERE type='table' AND name='message'" self.cur.execute(sql) if self.cur.fetchone() == None: sql = ("CREATE TABLE IF NOT EXISTS message (" "msgid TEXT PRIMARY KEY NOT NULL," "station TEXT," "timestamp TEXT," "content TEXT NOT NULL," "received TEXT NOT NULL DEFAULT current_timestamp)" ) self.cur.execute(sql) self.log.info(f"Created NAVTEX database: {dbpath}") # Aktualisieren bei Programmstart # TODO Ausgeschaltet für Programmentwicklung if self.source == 'net': self.refresh() # In der Konfiguration werden Minuten angegeben GLib.timeout_add_seconds(cfg['ntx_refresh'] * 60, self.on_timer) self.running = True def __del__(self): self.conn.close() def on_timer(self): """ NAVTEX data handling """ self.refresh() self.housekeeping() return True def parse_message(self, plainmsg): """ Zeile 1: ZCZC Zeile 2: Stationskennung Zeile 3: Zeitstempel (meistens) Zeile 4 bis n-1: Nachrichteninhalt Zeile n: NNNN Je nach Code B1, B2 kann das folgende Format unterschiedlich sein """ msg = {} data = plainmsg.splitlines() msg['id'] = data[0][5:9] msg['station'] = data[1].strip() timestamp = data[2] if len(timestamp) == 17: day = int(timestamp[0:2]) hour = int(timestamp[2:4]) minute = int(timestamp[4:6]) monmap = ('JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC') month = monmap.index(timestamp[11:14]) + 1 year = 2000 + int(timestamp[15:17]) try: msg['timestamp'] = datetime.datetime(year, month, day, hour, minute, 0) except: msg['timestamp'] = None else: msg['timestamp'] = None return msg def dwd_get_data(self, local=False): """ net: Webseite auslesen https://www.dwd.de/DE/fachnutzer/schifffahrt/funkausstrahlung/navtex """ if local: # Für Tests um nicht permanent die Webseite abzufragen with open("490_emd.html", "r") as fh: content = fh.read() else: ssl_context = ssl.create_default_context() conn = http.client.HTTPSConnection("www.dwd.de", 443, context=ssl_context) url = "https://www.dwd.de/DE/fachnutzer/schifffahrt/funkausstrahlung/navtex/490_emd.html" try: conn.request("GET", url) response = conn.getresponse() if response.status == 200: content = response.read().decode() else: print(f"Error: {response.status}") return [] except http.client.HTTPException as e: self.log.warning(f"HTTP error occurred: {e}") return [] except ssl.SSLError as ssl_error: self.log.warning(f"SSL error occurred: {ssl_error}") return [] expr = re.compile("(ZCZC.*?NNNN)", re.DOTALL) matches = re.findall(expr, content) return matches def refresh(self): self.log.info("NAVTEX refresh") messages = self.dwd_get_data(False) sql = "INSERT INTO message (msgid, station, content) VALUES (?, ?, ?)" for m in messages: msg = self.parse_message(m) self.cur.execute("SELECT COUNT(*) FROM message WHERE msgid=?", (msg['id'],)) result = self.cur.fetchone() if result[0] == 0: self.log.debug(f"NAVTEX: insert new message '{msg['id']}'") self.cur.execute(sql, (msg['id'], msg['station'], m)) self.conn.commit() def housekeeping(self): self.log.info("NAVTEX housekeeping") sql = "DELETE FROM message WHERE (julianday('now') - julianday(received)) * 24 > ?" self.cur.execute(sql, (self.maxage, )) def get_count(self): sql = "SELECT COUNT(*) FROM message" self.cur.execute(sql) result = self.cur.fetchone() return result[0] def get_ids(self): sql = "SELECT msgid FROM message" result = self.cur.execute(sql) msgids = [] for row in result.fetchall(): msgids.append(row[0]) return msgids def get_message(self, msgid): sql = "SELECT content FROM message WHERE msgid=?" self.cur.execute(sql, (msgid, )) result = self.cur.fetchone() return result[0]