682 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			682 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
'''
 | 
						|
!!! Dies ist noch im Ideen-Stadium 
 | 
						|
WIP TBD
 | 
						|
 | 
						|
Die Werte der Daten werden wie in NMEA2000 gespeichert:
 | 
						|
Längen: m
 | 
						|
Geschwindigkeiten: m/s
 | 
						|
Winkel, Kurse: radiant (Bogenmaß)
 | 
						|
Temperaturen: K
 | 
						|
Druck: Pa
 | 
						|
Geokoordinaten sind eine vorzeichenbehaftete Fileßkommazahl
 | 
						|
 | 
						|
Liste der Daten mit Langbezeichnung siehe: valdesc.py
 | 
						|
 | 
						|
Die format()-Funktion liefert immer einen String zurück.
 | 
						|
Unterscheidung zwischen "kein Wert" und "ungültiger Wert"?
 | 
						|
 | 
						|
Normale Daten
 | 
						|
-------------
 | 
						|
ALT - Altitude, Höhe über Grund
 | 
						|
AWA - Apparant Wind Angle, scheinbare Windrichtung
 | 
						|
AWS - Apparant Wind Speed, scheinbare Windgeschwindigkeit
 | 
						|
BTW - Bearing To Waipoynt, Winkel zum aktuellen Wegpunkt
 | 
						|
COG - Course over Ground, Kurs über Grund
 | 
						|
DBS - Depth Below Surface, Tiefe unter Wasseroberfläche
 | 
						|
DBT - Depth Below Transducer, Tiefe unter Sensor
 | 
						|
DEV - Deviation, Kursabweichung
 | 
						|
DTW - Distance To Waypoint, Entfernung zum aktuellen Wegpunkt
 | 
						|
GPSD - GPS Date, GPS-Datum
 | 
						|
GPDT - GPS Time, GPS-Zeit als UTC (Weltzeit)
 | 
						|
HDM - Magnetic Heading, magnetischer rechtweisender Kurs
 | 
						|
HDT - Heading, wahrer rechtweisender Kurs
 | 
						|
HDOP - GPS-Genauigkeit in der Horizontalen
 | 
						|
LAT - Latitude, geografische Breite
 | 
						|
LON - Longitude, geografische Höhe
 | 
						|
Log - Log, Entfernung
 | 
						|
MaxAws - Maximum Apperant Wind Speed, Maximum der relativen Windgeschwindigkeit seit Gerätestart
 | 
						|
MaxTws - Maximum True Wind Speed, Maximum der wahren Windgeschwindigkeit seit Gerätestart
 | 
						|
PDOP - GPS-Genauigkeit über alle 3 Raumachsen
 | 
						|
PRPOS - Auslenkung Sekundärruder
 | 
						|
ROT - Rotation, Drehrate
 | 
						|
RPOS - Rudder Position, Auslenkung Hauptruder
 | 
						|
SOG - Speed Over Ground, Geschwindigkeit über Grund
 | 
						|
STW - Speed Through Water, Geschwindigkeit durch das Wasser
 | 
						|
SatInfo - Satellit Info, Anzahl der sichtbaren Satelliten
 | 
						|
TSPOS - Timestamp des Position-Fixes (sie z.B. 0183/GLL)
 | 
						|
TWD - True Wind Direction, wahre Windrichtung
 | 
						|
TWS - True Wind Speed, wahre Windgeschwindigkeit
 | 
						|
TXT - Textnachricht / Alarm
 | 
						|
TZ - Time Zone, Zeitzone
 | 
						|
TripLog - Trip Log, Tages-Entfernungszähler
 | 
						|
VAR - Variation, Abweichung vom Sollkurs
 | 
						|
VDOP - GPS-Genauigkeit in der Vertikalen
 | 
						|
WPLat - Waypoint Latitude, geogr. Breite des Wegpunktes
 | 
						|
WPLon - Waypoint Longitude, geogr. Länge des Wegpunktes
 | 
						|
WTemp - Water Temperature, Wassertemperatur
 | 
						|
XTE - Cross Track Error, Kursfehler
 | 
						|
 | 
						|
Normale Daten erweitert
 | 
						|
-----------------------
 | 
						|
 | 
						|
ROLL - Roll - Krängen / Rotation in Querrichtung
 | 
						|
PTCH - Pitch - Stampfen / Rotation in Längsrichtung
 | 
						|
YAW - Yaw - Gieren / Rotation um die Senkrechte Achse
 | 
						|
 | 
						|
XDR-Daten
 | 
						|
---------
 | 
						|
xdrVBat - Bordspannung
 | 
						|
xdrHum - Luftfeuchte
 | 
						|
xdrPress - Luftdruck
 | 
						|
xdrTemp - Temperatur
 | 
						|
 | 
						|
xdrRotK - Kielrotation
 | 
						|
xdrRoll
 | 
						|
xdrPitch
 | 
						|
xdrYaw
 | 
						|
 | 
						|
'''
 | 
						|
 | 
						|
import datetime
 | 
						|
import time
 | 
						|
import math
 | 
						|
import random
 | 
						|
from enum import Enum
 | 
						|
from io import StringIO
 | 
						|
 | 
						|
#from .lookup import fluidtype
 | 
						|
from . import lookup
 | 
						|
from . import routing
 | 
						|
 | 
						|
class CardinalDirection(Enum):
 | 
						|
    NORTH = 'N'
 | 
						|
    SOUTH = 'S'
 | 
						|
    EAST = 'E'
 | 
						|
    WEST = 'W'
 | 
						|
 | 
						|
 | 
						|
class BoatValue():
 | 
						|
    """
 | 
						|
    Wert mit Datentyp, Einheit, Validekennzeichen und Skalierungsfaktor
 | 
						|
    """
 | 
						|
 | 
						|
    placeholder = '---'
 | 
						|
 | 
						|
    def __init__(self, shortname, unit=''):
 | 
						|
        self.valname = shortname
 | 
						|
        self.unit = unit
 | 
						|
        self.value = None
 | 
						|
        self.valid = False
 | 
						|
        self.resolution = 1
 | 
						|
        self.decpl = None # decimal places for format
 | 
						|
        self.desc = "" # long description
 | 
						|
        self.timestamp = time.time()
 | 
						|
        self.simulated = False
 | 
						|
        self.history = False
 | 
						|
        self.hbuf = None
 | 
						|
 | 
						|
    def getValue(self):
 | 
						|
        # Wert unter Beachtung der Unit zurückgeben
 | 
						|
        if (self.value is not None) and self.valid:
 | 
						|
            return self.value
 | 
						|
        else:
 | 
						|
            return self.placeholder
 | 
						|
 | 
						|
    def getValueRaw(self, ignorevalid=False):
 | 
						|
        if ignorevalid or self.valid:
 | 
						|
            return self.value
 | 
						|
        return None
 | 
						|
 | 
						|
    def setValue(self, newvalue):
 | 
						|
        self.value = newvalue
 | 
						|
        self.timestamp = time.time()
 | 
						|
        self.valid = True
 | 
						|
        # if self.history:
 | 
						|
        # TODO es kann mehrere verschiedene Zeitreihen geben!
 | 
						|
        # Implementierung noch unklar.
 | 
						|
 | 
						|
    def getPlaceholder(self):
 | 
						|
        return self.placeholder
 | 
						|
 | 
						|
    def enableHistory(self, size, delta_t):
 | 
						|
        if self.history:
 | 
						|
            # Wiederholter Aufruf löscht die bisherige Historie
 | 
						|
            self.hbuf.clear()
 | 
						|
            return
 | 
						|
        self.history = True
 | 
						|
        self.hbuf = HistoryBuffer(size, delta_t)
 | 
						|
 | 
						|
    def format(self):
 | 
						|
        # TODO kein schöner Code hier!
 | 
						|
        if not type(self.value) in (int, float):
 | 
						|
            return self.value
 | 
						|
        if self.simulated:
 | 
						|
            if self.valname == "xdrVBat":
 | 
						|
                return "{:.1f}".format(random.uniform(11.8, 14.2))
 | 
						|
            else:
 | 
						|
                return "{:.0f}".format(random.uniform(95, 130))
 | 
						|
        if not self.value or not self.valid:
 | 
						|
            return self.placeholder
 | 
						|
        if not self.decpl:
 | 
						|
            if self.value < 10:
 | 
						|
                self.decpl = 1
 | 
						|
            else:
 | 
						|
                self.decpl = 0
 | 
						|
        return "{0:3.{1}f}".format(self.value, self.decpl)
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        out = self.valname
 | 
						|
        #out += self.format()
 | 
						|
        if not self.valid:
 | 
						|
            out += (" (invalid)")
 | 
						|
        return out
 | 
						|
 | 
						|
class BoatValueGeo(BoatValue): 
 | 
						|
    # geofmt = lat | lon
 | 
						|
    # unit = rad | deg
 | 
						|
    def __init__(self, shortname, geofmt, unit='deg'):
 | 
						|
        super().__init__(shortname, unit)
 | 
						|
        self.geofmt = geofmt
 | 
						|
        self.decpl = 3
 | 
						|
        #print(self.valname)
 | 
						|
    def format(self):
 | 
						|
        # latitude, longitude
 | 
						|
        if not self.value or not self.valid:
 | 
						|
            return self.placeholder
 | 
						|
        degrees = int(self.value)
 | 
						|
        minutes = (self.value - degrees) * 60
 | 
						|
        if self.geofmt == 'lat':
 | 
						|
            direction = 'N' if self.value > 0 else 'S'
 | 
						|
            formatted = "{0}° {1:.{3}f}' {2}".format(degrees, minutes, direction, self.decpl)
 | 
						|
        elif self.geofmt == 'lon':
 | 
						|
            direction = 'E' if self.value > 0 else 'W'
 | 
						|
            formatted = "{0}° {1:.{3}f}' {2}".format(degrees, minutes, direction, self.decpl)
 | 
						|
        else:
 | 
						|
            formatted = str(self.placeholder)
 | 
						|
        return formatted
 | 
						|
 | 
						|
class BoatValueDate(BoatValue): 
 | 
						|
    # datefmt = GB | US | DE | ISO
 | 
						|
    def __init__(self, shortname, datefmt='ISO'):
 | 
						|
        super().__init__(shortname)
 | 
						|
        self.datefmt = datefmt
 | 
						|
    def format(self):
 | 
						|
        if self.datefmt == 'DE':
 | 
						|
            formatted = self.value.strftime("%d.%m.%Y")
 | 
						|
        elif self.datefmt == 'GB':
 | 
						|
            formatted = self.value.strftime("%d/%m/%Y")
 | 
						|
        elif self.datefmt == 'US':
 | 
						|
            formatted = self.value.strftime("%m/%d/%Y")
 | 
						|
        elif self.datefmt == 'ISO':
 | 
						|
            formatted = self.value.strftime("%Y-%m-%d")
 | 
						|
        return formatted
 | 
						|
 | 
						|
class BoatValueTime(BoatValue):
 | 
						|
    # TODO Formatting!
 | 
						|
    def __init__(self, shortname, timezone='UTC'):
 | 
						|
        super().__init__(shortname)
 | 
						|
        self.tz = timezone
 | 
						|
        self.timefmt = 'hh:mm' # TODO hh:mm | hh:mm:ss | hh:mm:ss.000 | ... ?
 | 
						|
    def format(self):
 | 
						|
        if self.timefmt == 'hh:mm':
 | 
						|
            formatted = self.value.strftime("%H:%M")
 | 
						|
        elif self.timefmt == 'hh:mm:ss':
 | 
						|
            formatted = self.value.strftime("%H:%M:%S")
 | 
						|
        else:
 | 
						|
            # unbekanntes Format
 | 
						|
            formatted = self.value.strftime("%H%M%S")
 | 
						|
        return formatted
 | 
						|
 | 
						|
class BoatValueSpeed(BoatValue):
 | 
						|
    # unit = m/s | kt | km/h
 | 
						|
    # unsigned? Was ist mit Rückwärts?
 | 
						|
    def format(self):
 | 
						|
        if self.simulated:
 | 
						|
            return "5.3"
 | 
						|
        if (self.value is None) or not self.valid:
 | 
						|
            return self.placeholder
 | 
						|
        if self.value < 20:
 | 
						|
            formatted = f"{self.value:3.1f}"
 | 
						|
        else:
 | 
						|
            formatted = f"{self.value:3.0f}"
 | 
						|
        return formatted
 | 
						|
 | 
						|
class BoatValueAngle(BoatValue):
 | 
						|
    # unit = rad | deg
 | 
						|
    # course, wind, heading, bearing
 | 
						|
    # roll, pitch, yaw, rudder, keel
 | 
						|
    def format(self):
 | 
						|
        if self.simulated:
 | 
						|
            if self.valname == "BTW":
 | 
						|
                return "253"
 | 
						|
            if self.valname == "TWD":
 | 
						|
                return "62"
 | 
						|
            else:
 | 
						|
                return "120"
 | 
						|
        if self.value:
 | 
						|
            return f"{self.value:03.0f}"
 | 
						|
        else:
 | 
						|
            return self.placeholder
 | 
						|
 | 
						|
class BoatValueRotation(BoatValue):
 | 
						|
    # unit = rad | deg
 | 
						|
    # signed
 | 
						|
    def format(self):
 | 
						|
        if self.value < 10 and self.value > -10:
 | 
						|
            formatted = f"{self.value:.1f}"
 | 
						|
        else:
 | 
						|
            formatted = f"{self.value:.1f}"
 | 
						|
        return formatted
 | 
						|
 | 
						|
class BoatValueDepth(BoatValue):
 | 
						|
    # unit = m | ft
 | 
						|
    # unsigned
 | 
						|
    def format(self):
 | 
						|
        if self.simulated:
 | 
						|
            if self.valname == "DBT":
 | 
						|
                return "6.2"
 | 
						|
            else:
 | 
						|
                return "6.5"
 | 
						|
        if not self.value or not self.valid:
 | 
						|
            return self.placeholder
 | 
						|
        if self.value < 100:
 | 
						|
            formatted = f"{self.value:3.1f}"
 | 
						|
        else:
 | 
						|
            formatted = f"{self.value:3.0f}"
 | 
						|
        return formatted
 | 
						|
 | 
						|
class BoatValueDistance(BoatValue):
 | 
						|
    # unsigned integer?
 | 
						|
    def format(self, signed=False):
 | 
						|
        if self.value:
 | 
						|
            return f"{self.value:d}"
 | 
						|
        else:
 | 
						|
            return self.placeholder
 | 
						|
 | 
						|
class BoatValueTemperature(BoatValue):
 | 
						|
    # signed
 | 
						|
    def __init__(self, shortname, unit='K'):
 | 
						|
        super().__init__(shortname, unit)
 | 
						|
        self.instance = None
 | 
						|
        self.sensortype = None
 | 
						|
        self.decpl = 0
 | 
						|
    def format(self):
 | 
						|
        if self.value < 100:
 | 
						|
            formatted = f"{self.value:3.1f}"
 | 
						|
        else:
 | 
						|
            formatted = f"{self.value:3.0f}"
 | 
						|
        return formatted
 | 
						|
 | 
						|
class BoatValueHumidity(BoatValue):
 | 
						|
    # unsigned integer
 | 
						|
    # range 0 .. 100
 | 
						|
    def __init__(self, shortname, unit='%'):
 | 
						|
        super().__init__(shortname, unit)
 | 
						|
        self.instance = None
 | 
						|
        self.sensortype = None
 | 
						|
        self.decpl = 0
 | 
						|
    def format(self):
 | 
						|
        return f"{self.value:d}"
 | 
						|
 | 
						|
class BoatValuePressure(BoatValue):
 | 
						|
    # unsigned integer
 | 
						|
    # range ca. 800 .. 1100 for athmospheric pressure
 | 
						|
    def __init__(self, shortname, unit='Pa'):
 | 
						|
        super().__init__(shortname, unit)
 | 
						|
        self.instance = None
 | 
						|
        self.sensortype = None
 | 
						|
        self.decpl = 1
 | 
						|
    def format(self):
 | 
						|
        if self.value and self.valid:
 | 
						|
            return f"{self.value:4.{self.decpl}f}"
 | 
						|
        else:
 | 
						|
            return self.placeholder
 | 
						|
 | 
						|
class BoatValueText(BoatValue):
 | 
						|
    # generic simple text
 | 
						|
    def __init__(self, shortname):
 | 
						|
        super().__init__(shortname, '')
 | 
						|
        self.instance = None
 | 
						|
        self.sensortype = None
 | 
						|
    def format(self):
 | 
						|
        if self.value and self.valid:
 | 
						|
            return self.value
 | 
						|
 | 
						|
class Tank():
 | 
						|
    """
 | 
						|
    Die Instanz beziegt sich auf den Typ. So kann die Instanz 0
 | 
						|
    für einen Wassertank und einen Dieseltank existieren
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, instance=0):
 | 
						|
        self.fluidtype = 1 # water -> lookup
 | 
						|
        self.instance = instance
 | 
						|
        self.level = None # percent
 | 
						|
        self.capacity = None # liter
 | 
						|
        self.desc = "" # long description
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        typedesc = lookup.fluidtype[self.fluidtype]
 | 
						|
        out = f"  Tank / {typedesc}: #{self.instance}\n"
 | 
						|
        out += f"    Capacity: {self.capacity} l\n"
 | 
						|
        out += f"    Fluid level: {self.level} %\n"
 | 
						|
        return out
 | 
						|
 | 
						|
class Engine():
 | 
						|
 | 
						|
    def __init__(self, instance=0):
 | 
						|
        self.instance = instance
 | 
						|
        self.speed_rpm = None
 | 
						|
        self.exhaust_temp = None
 | 
						|
        self.desc = "" # long description
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        out = f"  Engine #{self.instance}\n"
 | 
						|
        if self.exhaust_temp:
 | 
						|
            out += f"    Exhaust temp: {self.exhaust_temp:.1f} °C\n"
 | 
						|
        else:
 | 
						|
            out += "     Exhaust temp: no data\n"
 | 
						|
        return out
 | 
						|
 | 
						|
class Satellite():
 | 
						|
 | 
						|
    def __init__(self, prn_num):
 | 
						|
        self.prn_num = prn_num 
 | 
						|
        self.elevation = None
 | 
						|
        self.azimuth = None
 | 
						|
        self.snr = None   # signal noise ratio
 | 
						|
        self.rres = None  # range residuals
 | 
						|
        self.status = None # lookup -> prnusage
 | 
						|
        self.lastseen = None
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        out = f"SAT {self.prn_num:02d}: "
 | 
						|
        if self.snr:
 | 
						|
            out += f"snr={self.snr}dB elevation={self.elevation:.4f} azimuth={self.azimuth:.4f} status={self.status}\n"
 | 
						|
        else:
 | 
						|
            out += "no signal\n"
 | 
						|
        return out
 | 
						|
 | 
						|
class SatelliteList():
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        sat = {}
 | 
						|
        rrmode = None
 | 
						|
        maxage = 300 # sec
 | 
						|
    def getCount(self):
 | 
						|
        return len(sat)
 | 
						|
    def addSat(self, pnr_num):
 | 
						|
        pass
 | 
						|
    def delSat(self, pnr_num):
 | 
						|
        pass
 | 
						|
 | 
						|
class AISTarget():
 | 
						|
    # FUTURE
 | 
						|
    def __init__(self):
 | 
						|
        self.mmsi = None
 | 
						|
        self.aiscls = None # class A or B
 | 
						|
        self.name = None
 | 
						|
        self.cog = None
 | 
						|
        self.sog = None
 | 
						|
 | 
						|
class AIS():
 | 
						|
    def __init__(self):
 | 
						|
        target = {}
 | 
						|
 | 
						|
class BoatData():
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
 | 
						|
        self.simulation = False
 | 
						|
 | 
						|
        # aktueller Alarm
 | 
						|
        self.alarm = False
 | 
						|
        self.alarm_id = -1
 | 
						|
        self.alarm_src = ""
 | 
						|
        self.alarm_msg = ""
 | 
						|
        self.alarm_ts = None
 | 
						|
 | 
						|
        # nach Überschreiten dieser Schwelle in Sekunden wird
 | 
						|
        # ein Meßwert als ungültig angesehen
 | 
						|
        self.maxage = 5
 | 
						|
 | 
						|
        # Systemspannung; temporär. später auf bessere Weise speichern
 | 
						|
        self.voltage = BoatValue("xdrVBat", "V")
 | 
						|
 | 
						|
        # Navigationsdaten
 | 
						|
        self.awa = BoatValueAngle("AWA", "deg")
 | 
						|
        self.aws = BoatValueSpeed("AWS", "kn")
 | 
						|
        self.twd = BoatValueAngle("TWD", "deg")
 | 
						|
        self.tws = BoatValueSpeed("TWS", "kn")
 | 
						|
        self.lat = BoatValueGeo("LAT", "lat", "deg")
 | 
						|
        self.lon = BoatValueGeo("LON", "lon", "deg")
 | 
						|
        self.gpsd = BoatValueDate("GPSD", "ISO")
 | 
						|
        self.gpst = BoatValueTime("GPST")
 | 
						|
        self.hdop = BoatValue("HDOP")
 | 
						|
        self.tspos = BoatValueTime("TSPOS")
 | 
						|
        self.sog = BoatValueSpeed("SOG", "kn")
 | 
						|
        self.cog = BoatValueAngle("COG", "deg")
 | 
						|
        self.hdm = BoatValueAngle("HDM", "deg")
 | 
						|
        self.hdt = BoatValueAngle("HDT", "deg")
 | 
						|
        self.xte = BoatValueDistance("XTE", "m")
 | 
						|
        self.stw = BoatValueSpeed("STW", "kn")
 | 
						|
        self.dbt = BoatValueDepth("DBT", "m")
 | 
						|
        self.dbs = BoatValueDepth("DBS", "m")
 | 
						|
        self.rot = BoatValueAngle("ROT", "deg")
 | 
						|
        self.roll = BoatValueAngle("ROLL", "deg")
 | 
						|
        self.pitch = BoatValueAngle("PTCH", "deg")
 | 
						|
        self.yaw = BoatValueAngle("YAW", "deg")
 | 
						|
        self.rpos = BoatValueAngle("RPOS", "deg")
 | 
						|
        self.prpos = BoatValueAngle("PRPOS", "deg")
 | 
						|
 | 
						|
        # Text / Alarm
 | 
						|
        self.txt = BoatValueText("TXT")
 | 
						|
 | 
						|
        # Nächster Wegepunkt
 | 
						|
        self.wpno = BoatValue("WP")
 | 
						|
        self.wpname = BoatValue("WPname")
 | 
						|
        self.wplat = BoatValueGeo("WPLat", "lat", "deg")
 | 
						|
        self.wplon = BoatValueGeo("WPLon", "lon", "deg")
 | 
						|
        self.wpdist = BoatValueDistance("DTW", "m")
 | 
						|
        self.bearing = BoatValueAngle("BTW", "kn")
 | 
						|
 | 
						|
        # Umgebung
 | 
						|
        self.temp_water = BoatValueTemperature("WTemp", "°C")
 | 
						|
        self.temp_air =  BoatValueTemperature("xdrTemp", "°C")
 | 
						|
        self.pressure =  BoatValuePressure("xdrPress", "hPa")
 | 
						|
        self.humidity = BoatValueHumidity("xdrHum", "%")
 | 
						|
        self.temp = {}  # Erweiterte Temperaturdaten
 | 
						|
        self.press = {} # Erweiterte Druckdaten
 | 
						|
 | 
						|
        # Sonderdaten
 | 
						|
        self.rotk = BoatValueAngle("xdrRotK", "deg") # Kielrotation
 | 
						|
 | 
						|
        # Routen
 | 
						|
        self.routes = {}
 | 
						|
 | 
						|
        # Wegepunkte ohne Routenzuordnung
 | 
						|
        self.wps = {}
 | 
						|
 | 
						|
        # Maschinen
 | 
						|
        self.engine = {}
 | 
						|
 | 
						|
        # Tanks
 | 
						|
        self.tank = {}
 | 
						|
 | 
						|
        # Mehrere getrennte Batteriekreise
 | 
						|
        # - Starter
 | 
						|
        # - Verbrauchen
 | 
						|
        # - Ankerwinsch / Bugstrahlruder
 | 
						|
 | 
						|
        # Stromerzeugung
 | 
						|
        #   Solarleistung
 | 
						|
        #   Generatorleistung
 | 
						|
        #   Benzingenerator
 | 
						|
        #   Windgenerator
 | 
						|
        #   Wasser-/Schleppgenerator
 | 
						|
        #   Maschine Rekuperation
 | 
						|
 | 
						|
        # Satelliten
 | 
						|
        self.sat = {}
 | 
						|
 | 
						|
        # Zeitreihen für diverse Daten
 | 
						|
        self.history = {}
 | 
						|
 | 
						|
        self.valref = {
 | 
						|
            'AWA': self.awa,
 | 
						|
            'AWS': self.aws,
 | 
						|
            'BTW': self.bearing,
 | 
						|
            'COG': self.cog,
 | 
						|
            'HDM': self.hdm,
 | 
						|
            'HDT': self.hdt,
 | 
						|
            'DBT': self.dbt,
 | 
						|
            'DBS': self.dbs,
 | 
						|
            'DTW': self.wpdist,
 | 
						|
            'GPSD': self.gpsd,
 | 
						|
            'GPST': self.gpst,
 | 
						|
            'HDOP': self.hdop,
 | 
						|
            'LAT': self.lat,
 | 
						|
            'LON': self.lon,
 | 
						|
            'PRPOS': self.prpos,
 | 
						|
            'PTCH': self.pitch,
 | 
						|
            'RPOS': self.rpos,
 | 
						|
            'ROLL': self.roll,
 | 
						|
            'ROT': self.rot,
 | 
						|
            'SOG': self.sog,
 | 
						|
            'STW': self.stw,
 | 
						|
            'TSPOS': self.tspos,
 | 
						|
            'TWD': self.twd,
 | 
						|
            'TWS': self.tws,
 | 
						|
            'TXT': self.txt,
 | 
						|
            'WTemp': self.temp_water,
 | 
						|
            'WPLat': self.wplat,
 | 
						|
            'WPLon': self.wplon,
 | 
						|
            'WPname': self.wpname,
 | 
						|
            'XTE': self.xte,
 | 
						|
            'xdrRotK': self.rotk,
 | 
						|
            'xdrVBat': self.voltage,
 | 
						|
            'xdrTemp': self.temp_air,
 | 
						|
            'xdrPress': self.pressure,
 | 
						|
            'xdrHum': self.humidity,
 | 
						|
            'YAW': self.yaw
 | 
						|
        }
 | 
						|
 | 
						|
    def addTank(self, instance):
 | 
						|
        self.tank[instance] = Tank(instance)
 | 
						|
 | 
						|
    def addEngine(self, instance):
 | 
						|
        self.engine[instance] = Engine(instance)
 | 
						|
 | 
						|
    def addSensor(self, sensortype, instance):
 | 
						|
        if sensortype == 'temp':
 | 
						|
            if not instance in self.temp:
 | 
						|
                self.temp[instance] = BoatValueTemperature()
 | 
						|
            else:
 | 
						|
                raise ValueError(f"duplicate key '{instance}'")
 | 
						|
        elif sensortype == 'press':
 | 
						|
            if not instance in self.press:
 | 
						|
                self.press[instance] = BoatValuePressure()
 | 
						|
            else:
 | 
						|
                raise ValueError(f"duplicate key '{instance}'")
 | 
						|
 | 
						|
    def addSatellite(self, prn_num):
 | 
						|
        if not prn_num in self.sat:
 | 
						|
            self.sat[prn_num] = Satellite(prn_num)
 | 
						|
            self.sat[prn_num].status = 0
 | 
						|
 | 
						|
    def updateSatellite(self, prn_num, elevation, azimuth, snr, rres, status):
 | 
						|
        if not prn_num in self.sat:
 | 
						|
            self.sat[prn_num] = Satellite(prn_num)
 | 
						|
        self.sat[prn_num].elevation = elevation
 | 
						|
        self.sat[prn_num].azimuth = azimuth
 | 
						|
        self.sat[prn_num].snr = snr
 | 
						|
        self.sat[prn_num].rres = rres
 | 
						|
        self.sat[prn_num].status = status
 | 
						|
        self.sat[prn_num].lastseen = time.time()
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        out = StringIO()
 | 
						|
        out.write("Boat Data\n")
 | 
						|
        out.write("  Voltage: {}\n".format(self.voltage))
 | 
						|
        out.write("  Latitude: {}\n".format(self.lat.format()))
 | 
						|
        out.write("  Longitude: {}\n".format(self.lon.format()))
 | 
						|
        out.write("  SOG: {}\n".format(self.sog.format()))
 | 
						|
        out.write("  Waypoint: {}\n".format(self.wpname.format()))
 | 
						|
        out.write("    Distance: {}\n".format(self.wpdist.format()))
 | 
						|
        out.write("    Bearing: {}\n".format(self.bearing.format()))
 | 
						|
        for e in self.engine.values():
 | 
						|
            out.write(str(e))
 | 
						|
        for t in self.tank.values():
 | 
						|
            out.write(str(t))
 | 
						|
        out.write("  Routes\n")
 | 
						|
        for r in self.routes:
 | 
						|
            out.write("    {}".format(r))
 | 
						|
        out.write("  Waypoints\n") 
 | 
						|
        for w in self.wps:
 | 
						|
            out.write("    {}".format(w))
 | 
						|
        out.write("  Satellite info\n")
 | 
						|
        for s in self.sat.values():
 | 
						|
            out.write("    {}".format(str(s)))
 | 
						|
        return out.getvalue()
 | 
						|
 | 
						|
    def updateValid(self, age=None):
 | 
						|
        # age: Alter eines Meßwerts in Sekunden
 | 
						|
        if not age:
 | 
						|
            age = self.maxage
 | 
						|
        t = time.time()
 | 
						|
        for v in vars(self).values():
 | 
						|
            if isinstance(v,BoatValue):
 | 
						|
                if t - v.timestamp > age:
 | 
						|
                    v.valid = False
 | 
						|
 | 
						|
    def getRef(self, shortname):
 | 
						|
        '''
 | 
						|
        Referenz auf ein BoatValue-Objekt
 | 
						|
        '''
 | 
						|
        try:
 | 
						|
            bv = self.valref[shortname]
 | 
						|
        except KeyError:
 | 
						|
            bv = None
 | 
						|
        return bv
 | 
						|
 | 
						|
    def getValue(self, shortname):
 | 
						|
        '''
 | 
						|
        Wert aufgrund textuellem Kurznamen zurückliefern
 | 
						|
        '''
 | 
						|
        try:
 | 
						|
            value = self.valref[shortname].value
 | 
						|
        except KeyError:
 | 
						|
            value = None
 | 
						|
        return value
 | 
						|
 | 
						|
    def setValue(self, shortname, newvalue):
 | 
						|
        '''
 | 
						|
        Rückgabewert True bei erfolgreichem Speichern des Werts
 | 
						|
        '''
 | 
						|
        if not shortname in self.valref:
 | 
						|
            return False
 | 
						|
        field = self.valref[shortname]
 | 
						|
        field.value = newvalue
 | 
						|
        field.timestamp = time.time()
 | 
						|
        field.valid = True
 | 
						|
        return True
 | 
						|
 | 
						|
    def enableSimulation(self):
 | 
						|
        self.simulation = True
 | 
						|
        for v in self.valref.values():
 | 
						|
            v.simulated = True
 | 
						|
 | 
						|
    def addHistory(self, history, htype):
 | 
						|
        """
 | 
						|
        htype: press, temp, hum
 | 
						|
        """
 | 
						|
        self.history[htype] = history
 | 
						|
 | 
						|
    def addRoute(self, route_id):
 | 
						|
        if not route_id in self.routes:
 | 
						|
            self.routes[route_id] = routing.Route()
 | 
						|
 | 
						|
    def addWaypoint(self, waypoint_id):
 | 
						|
        if not waypoint_id in self.wps:
 | 
						|
            self.wps[waypoint_id] = routing.Waypoint()
 |