""" 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 Für Konfiguration: subjects = ABCDEFGHIJKLTVWXYZ 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 siehe auch: https://www.icselectronics.co.uk/support/info/navtexdb """ 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]