From bdbd168123f4fa5a04566d2f875d6c309976c722 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Fri, 7 Feb 2025 19:48:32 +0100 Subject: [PATCH] NMEA2000-Code weiterbearbeitet --- nmea2000/boatdata.py | 25 ++++++--- nmea2000/device.py | 127 ++++++++++++++++++++++++++++++------------ nmea2000/lookup.py | 5 +- nmea2000/parser.py | 64 ++++++++++++++++----- obp60.conf | 2 +- obp60.py | 13 ++++- pages/apparentwind.py | 46 ++++++++++++++- pages/page.py | 49 +++++++++++++--- 8 files changed, 259 insertions(+), 72 deletions(-) diff --git a/nmea2000/boatdata.py b/nmea2000/boatdata.py index 48e753a..74b5254 100644 --- a/nmea2000/boatdata.py +++ b/nmea2000/boatdata.py @@ -79,11 +79,13 @@ import datetime import time import math import random +#from .lookup import fluidtype +from . import lookup class BoatValue(): - ''' + """ Wert mit Datentyp, Einheit, Validekennzeichen und Skalierungsfaktor - ''' + """ placeholder = '---' @@ -299,18 +301,23 @@ class BoatValuePressure(BoatValue): return self.placeholder 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.instance = instance self.fluidtype = 1 # water -> lookup - self.volume = None - self.capacity = None + self.instance = instance + self.level = None # percent + self.capacity = None # liter self.desc = "" # long description def __str__(self): - out = f" Tank #{self.instance}" + typedesc = lookup.fluidtype[self.fluidtype] + out = f" Tank / {typedesc}: #{self.instance}\n" out += f" Capacity: {self.capacity} l\n" - out += f" Fluid level: {self.volume} l\n" + out += f" Fluid level: {self.level} %\n" return out class Engine(): @@ -505,9 +512,9 @@ class BoatData(): out += f" Longitude: {self.lon.value}\n" out += f" SOG: {self.sog}\n" for e in self.engine.values(): - print(e) + out += str(e) for t in self.tank.values(): - print(t) + out += str(t) out += " Satellite info\n" for s in self.sat.values(): out += str(s) diff --git a/nmea2000/device.py b/nmea2000/device.py index 06a950f..9c9dc00 100644 --- a/nmea2000/device.py +++ b/nmea2000/device.py @@ -1,32 +1,39 @@ +""" +NMEA2000-Gerät +- auf dem Bus erkannte Geräte +- für das eigene Gerät steht initUid() zur Verfügung -''' -Platzhalter WIP -- ausprogrammieren nach Bedarf -Geräteliste - - wird regelmäßig aktualisiert - -''' +""" import time import struct +from . import lookup class Device(): def __init__(self, address): - #WIP - #Felder können sich noch ändern! - self.address = address - self.instance = 0 # default 0 - self.sysinstance = 0 # used with bridged networks, default 0 + # WIP: Felder können sich noch ändern! + self.address = address # Kann sich zur Laufzeit ändern self.lastseen = time.time() - self.uniqueid = None - self.manufacturercode = None + self.lastpinfo = None # Wann letztes Mal Productinfo erhalten? + self.lastcinfo = None # Wann letztes Mal Configurationinfo erhalten? + self.has_cinfo = True # Weitere Abfragen können verhindert werden + + # Device info + self.NAME = 0 # Wird über Address-Claim gefüllt + self.uniqueid = None # Z.B. aus der Geräteseriennummer abgeleitet + self.manufacturercode = 2046 # Open Boat Projects + self.instance = 0 # default 0 + self.instlower = 0 # 3bit, ISO ECU Instance + self.instupper = 0 # 5bit, ISO Function Instance + self.sysinstance = 0 # used with bridged networks, default 0 self.industrygroup = None - self.name = None # User defined device name - self.product = None # Product name - self.productcode = None # Product code self.devicefunction = None self.deviceclass = None + + # Product info + self.product = None # Product name + self.productcode = None # Product code self.serial = None self.modelvers = None # Hardware Version self.softvers = None # Current Software Version @@ -34,30 +41,80 @@ class Device(): self.certlevel = None # NMEA2000 Certification Level self.loadequiv = None # NMEA2000 LEN - # Configuration Info + # Configuration info self.instdesc1 = None self.instdesc2 = None self.manufinfo = None - def getName(self): - # NAME errechnen aus den Claim-Daten - # TODO Das hier ist noch fehlerhaft! + # Additional data + self.customname = None # User defined device name + + def _ISOtime(self, epoch): + if not epoch: + return "n/a" + return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(epoch)) + + def initUid(self): + # Initialize unique id from raspi cpu id and return 21bit value + with open("/sys/firmware/devicetree/base/serial-number", "r") as f: + hexid = f.read().rstrip('\x00') + return int(hexid, 16) & 0x001FFFFF # 21bit mask + + def getNAME(self): + """ + NAME is unique on bus + """ data = bytearray() - data.append((self.deviceclass << 4) | (self.devicefunction & 0x0f)) - data.extend(struct.pack('> 3 + device.manufacturercode = (buf[3] * 256 + buf[2]) >> 5 + device.instance = buf[4] + device.instlower = buf[4] & 0x07 + device.instupper = buf[4] >> 3 + device.devicefunction = buf[5] + device.deviceclass = (buf[6] & 0x7f) >> 1 + device.industrygroup = (buf[7] >> 4) & 0x07 # 3bit + device.sysinstance = buf[7] & 0x0f # 4bit + return struct.unpack_from('>Q', buf, 0)[0] + +def parse_126992(buf, source): + # System time + print(f"PGN 126992 System time from {source}") + sid = buf[0] + src = buf[1] & 0x0f + dval = date(1970,1,1) + timedelta(days=(buf[3] << 8) + buf[2]) + secs = struct.unpack_from('> 4 - boatdata.tank[instance].capacity = struct.unpack_from('> 4 + boatdata.tank[instance].level = struct.unpack_from('