mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2026-03-28 18:06:37 +01:00
Integrate many changes from master
This commit is contained in:
32
tools/gen3byte.py
Executable file
32
tools/gen3byte.py
Executable file
@@ -0,0 +1,32 @@
|
||||
#! /usr/bin/env python3
|
||||
#generate 3 byte codes for the RGB bytes
|
||||
#refer to https://controllerstech.com/ws2812-leds-using-spi/
|
||||
ONE_BIT='110'
|
||||
ZERO_BIT='100'
|
||||
|
||||
currentStr=''
|
||||
|
||||
def checkAndPrint(curr):
|
||||
if len(curr) >= 8:
|
||||
print("0b%s,"%curr[0:8],end='')
|
||||
return curr[8:]
|
||||
return curr
|
||||
first=True
|
||||
|
||||
print("uint8_t colorTo3Byte[256][3]=")
|
||||
print("{")
|
||||
for i in range(0,256):
|
||||
if not first:
|
||||
print("},")
|
||||
first=False
|
||||
print("{/*%02d*/"%i,end='')
|
||||
mask=0x80
|
||||
for b in range(0,8):
|
||||
if (i & mask) != 0:
|
||||
currentStr+=ONE_BIT
|
||||
else:
|
||||
currentStr+=ZERO_BIT
|
||||
mask=mask >> 1
|
||||
currentStr=checkAndPrint(currentStr)
|
||||
print("}")
|
||||
print("};")
|
||||
29
tools/getPgnType.py
Executable file
29
tools/getPgnType.py
Executable file
@@ -0,0 +1,29 @@
|
||||
#! /usr/bin/env python3
|
||||
import sys
|
||||
import json
|
||||
|
||||
def err(txt):
|
||||
print(txt,file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
HDR='''
|
||||
PGNM_Fast=0
|
||||
PGNM_Single=1
|
||||
PGNM_ISO=2
|
||||
PGN_MODES={
|
||||
'''
|
||||
FOOTER='''
|
||||
}
|
||||
'''
|
||||
with open(sys.argv[1],"r") as ih:
|
||||
data=json.load(ih)
|
||||
pgns=data.get('PGNs')
|
||||
if pgns is None:
|
||||
err("no pgns")
|
||||
print(HDR)
|
||||
for p in pgns:
|
||||
t=p['Type']
|
||||
pgn=p['PGN']
|
||||
if t and pgn:
|
||||
print(f" {pgn}: PGNM_{t},")
|
||||
print(FOOTER)
|
||||
19
tools/sendDelay.py
Executable file
19
tools/sendDelay.py
Executable file
@@ -0,0 +1,19 @@
|
||||
#! /usr/bin/env python3
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
def usage():
|
||||
print(f"usage: {sys.argv[0]} file delay")
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
usage()
|
||||
|
||||
delay=float(sys.argv[2])
|
||||
fn=sys.argv[1]
|
||||
with open (fn,"r") as fh:
|
||||
for line in fh:
|
||||
print(line,end="",flush=True)
|
||||
time.sleep(delay)
|
||||
|
||||
793
tools/sendN2K.py
Executable file
793
tools/sendN2K.py
Executable file
@@ -0,0 +1,793 @@
|
||||
#! /usr/bin/env python3
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
import datetime
|
||||
import getopt
|
||||
import time
|
||||
|
||||
###generated with getPgnType.py from canboat pgns.json
|
||||
PGNM_Fast=0
|
||||
PGNM_Single=1
|
||||
PGNM_ISO=2
|
||||
PGN_MODES={
|
||||
|
||||
59392: PGNM_Single,
|
||||
59904: PGNM_Single,
|
||||
60160: PGNM_Single,
|
||||
60416: PGNM_Single,
|
||||
60416: PGNM_Single,
|
||||
60416: PGNM_Single,
|
||||
60416: PGNM_Single,
|
||||
60416: PGNM_Single,
|
||||
60928: PGNM_Single,
|
||||
61184: PGNM_Single,
|
||||
61184: PGNM_Single,
|
||||
61184: PGNM_Single,
|
||||
65001: PGNM_Single,
|
||||
65002: PGNM_Single,
|
||||
65003: PGNM_Single,
|
||||
65004: PGNM_Single,
|
||||
65005: PGNM_Single,
|
||||
65006: PGNM_Single,
|
||||
65007: PGNM_Single,
|
||||
65008: PGNM_Single,
|
||||
65009: PGNM_Single,
|
||||
65010: PGNM_Single,
|
||||
65011: PGNM_Single,
|
||||
65012: PGNM_Single,
|
||||
65013: PGNM_Single,
|
||||
65014: PGNM_Single,
|
||||
65015: PGNM_Single,
|
||||
65016: PGNM_Single,
|
||||
65017: PGNM_Single,
|
||||
65018: PGNM_Single,
|
||||
65019: PGNM_Single,
|
||||
65020: PGNM_Single,
|
||||
65021: PGNM_Single,
|
||||
65022: PGNM_Single,
|
||||
65023: PGNM_Single,
|
||||
65024: PGNM_Single,
|
||||
65025: PGNM_Single,
|
||||
65026: PGNM_Single,
|
||||
65027: PGNM_Single,
|
||||
65028: PGNM_Single,
|
||||
65029: PGNM_Single,
|
||||
65030: PGNM_Single,
|
||||
65240: PGNM_ISO,
|
||||
65280: PGNM_Single,
|
||||
65284: PGNM_Single,
|
||||
65285: PGNM_Single,
|
||||
65285: PGNM_Single,
|
||||
65286: PGNM_Single,
|
||||
65286: PGNM_Single,
|
||||
65287: PGNM_Single,
|
||||
65287: PGNM_Single,
|
||||
65288: PGNM_Single,
|
||||
65289: PGNM_Single,
|
||||
65290: PGNM_Single,
|
||||
65292: PGNM_Single,
|
||||
65293: PGNM_Single,
|
||||
65293: PGNM_Single,
|
||||
65302: PGNM_Single,
|
||||
65305: PGNM_Single,
|
||||
65305: PGNM_Single,
|
||||
65305: PGNM_Single,
|
||||
65305: PGNM_Single,
|
||||
65305: PGNM_Single,
|
||||
65309: PGNM_Single,
|
||||
65312: PGNM_Single,
|
||||
65340: PGNM_Single,
|
||||
65341: PGNM_Single,
|
||||
65345: PGNM_Single,
|
||||
65350: PGNM_Single,
|
||||
65359: PGNM_Single,
|
||||
65360: PGNM_Single,
|
||||
65361: PGNM_Single,
|
||||
65371: PGNM_Single,
|
||||
65374: PGNM_Single,
|
||||
65379: PGNM_Single,
|
||||
65408: PGNM_Single,
|
||||
65409: PGNM_Single,
|
||||
65410: PGNM_Single,
|
||||
65420: PGNM_Single,
|
||||
65480: PGNM_Single,
|
||||
126208: PGNM_Fast,
|
||||
126208: PGNM_Fast,
|
||||
126208: PGNM_Fast,
|
||||
126208: PGNM_Fast,
|
||||
126208: PGNM_Fast,
|
||||
126208: PGNM_Fast,
|
||||
126208: PGNM_Fast,
|
||||
126464: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126720: PGNM_Fast,
|
||||
126983: PGNM_Fast,
|
||||
126984: PGNM_Fast,
|
||||
126985: PGNM_Fast,
|
||||
126986: PGNM_Fast,
|
||||
126987: PGNM_Fast,
|
||||
126988: PGNM_Fast,
|
||||
126992: PGNM_Single,
|
||||
126993: PGNM_Single,
|
||||
126996: PGNM_Fast,
|
||||
126998: PGNM_Fast,
|
||||
127233: PGNM_Fast,
|
||||
127237: PGNM_Fast,
|
||||
127245: PGNM_Single,
|
||||
127250: PGNM_Single,
|
||||
127251: PGNM_Single,
|
||||
127252: PGNM_Single,
|
||||
127257: PGNM_Single,
|
||||
127258: PGNM_Single,
|
||||
127488: PGNM_Single,
|
||||
127489: PGNM_Fast,
|
||||
127490: PGNM_Fast,
|
||||
127491: PGNM_Fast,
|
||||
127493: PGNM_Single,
|
||||
127494: PGNM_Fast,
|
||||
127495: PGNM_Fast,
|
||||
127496: PGNM_Fast,
|
||||
127497: PGNM_Fast,
|
||||
127498: PGNM_Fast,
|
||||
127500: PGNM_Single,
|
||||
127501: PGNM_Single,
|
||||
127502: PGNM_Single,
|
||||
127503: PGNM_Fast,
|
||||
127504: PGNM_Fast,
|
||||
127505: PGNM_Single,
|
||||
127506: PGNM_Fast,
|
||||
127507: PGNM_Fast,
|
||||
127508: PGNM_Single,
|
||||
127509: PGNM_Fast,
|
||||
127510: PGNM_Fast,
|
||||
127511: PGNM_Single,
|
||||
127512: PGNM_Single,
|
||||
127513: PGNM_Fast,
|
||||
127514: PGNM_Single,
|
||||
127744: PGNM_Single,
|
||||
127745: PGNM_Single,
|
||||
127746: PGNM_Single,
|
||||
127750: PGNM_Single,
|
||||
127751: PGNM_Single,
|
||||
128000: PGNM_Single,
|
||||
128001: PGNM_Single,
|
||||
128002: PGNM_Single,
|
||||
128003: PGNM_Single,
|
||||
128006: PGNM_Single,
|
||||
128007: PGNM_Single,
|
||||
128008: PGNM_Single,
|
||||
128259: PGNM_Single,
|
||||
128267: PGNM_Single,
|
||||
128275: PGNM_Fast,
|
||||
128520: PGNM_Fast,
|
||||
128538: PGNM_Fast,
|
||||
128768: PGNM_Single,
|
||||
128769: PGNM_Single,
|
||||
128776: PGNM_Single,
|
||||
128777: PGNM_Single,
|
||||
128778: PGNM_Single,
|
||||
128780: PGNM_Single,
|
||||
129025: PGNM_Single,
|
||||
129026: PGNM_Single,
|
||||
129027: PGNM_Single,
|
||||
129028: PGNM_Single,
|
||||
129029: PGNM_Fast,
|
||||
129033: PGNM_Single,
|
||||
129038: PGNM_Fast,
|
||||
129039: PGNM_Fast,
|
||||
129040: PGNM_Fast,
|
||||
129041: PGNM_Fast,
|
||||
129044: PGNM_Fast,
|
||||
129045: PGNM_Fast,
|
||||
129283: PGNM_Single,
|
||||
129284: PGNM_Fast,
|
||||
129285: PGNM_Fast,
|
||||
129291: PGNM_Single,
|
||||
129301: PGNM_Fast,
|
||||
129302: PGNM_Fast,
|
||||
129538: PGNM_Fast,
|
||||
129539: PGNM_Single,
|
||||
129540: PGNM_Fast,
|
||||
129541: PGNM_Fast,
|
||||
129542: PGNM_Fast,
|
||||
129545: PGNM_Fast,
|
||||
129546: PGNM_Single,
|
||||
129547: PGNM_Fast,
|
||||
129549: PGNM_Fast,
|
||||
129550: PGNM_Fast,
|
||||
129551: PGNM_Fast,
|
||||
129556: PGNM_Fast,
|
||||
129792: PGNM_Fast,
|
||||
129793: PGNM_Fast,
|
||||
129794: PGNM_Fast,
|
||||
129795: PGNM_Fast,
|
||||
129796: PGNM_Fast,
|
||||
129797: PGNM_Fast,
|
||||
129798: PGNM_Fast,
|
||||
129799: PGNM_Fast,
|
||||
129800: PGNM_Fast,
|
||||
129801: PGNM_Fast,
|
||||
129802: PGNM_Fast,
|
||||
129803: PGNM_Fast,
|
||||
129804: PGNM_Fast,
|
||||
129805: PGNM_Fast,
|
||||
129806: PGNM_Fast,
|
||||
129807: PGNM_Fast,
|
||||
129808: PGNM_Fast,
|
||||
129808: PGNM_Fast,
|
||||
129809: PGNM_Fast,
|
||||
129810: PGNM_Fast,
|
||||
130052: PGNM_Fast,
|
||||
130053: PGNM_Fast,
|
||||
130054: PGNM_Fast,
|
||||
130060: PGNM_Fast,
|
||||
130061: PGNM_Fast,
|
||||
130064: PGNM_Fast,
|
||||
130065: PGNM_Fast,
|
||||
130066: PGNM_Fast,
|
||||
130067: PGNM_Fast,
|
||||
130068: PGNM_Fast,
|
||||
130069: PGNM_Fast,
|
||||
130070: PGNM_Fast,
|
||||
130071: PGNM_Fast,
|
||||
130072: PGNM_Fast,
|
||||
130073: PGNM_Fast,
|
||||
130074: PGNM_Fast,
|
||||
130306: PGNM_Single,
|
||||
130310: PGNM_Single,
|
||||
130311: PGNM_Single,
|
||||
130312: PGNM_Single,
|
||||
130313: PGNM_Single,
|
||||
130314: PGNM_Single,
|
||||
130315: PGNM_Single,
|
||||
130316: PGNM_Single,
|
||||
130320: PGNM_Fast,
|
||||
130321: PGNM_Fast,
|
||||
130322: PGNM_Fast,
|
||||
130323: PGNM_Fast,
|
||||
130324: PGNM_Fast,
|
||||
130330: PGNM_Fast,
|
||||
130560: PGNM_Single,
|
||||
130561: PGNM_Fast,
|
||||
130562: PGNM_Fast,
|
||||
130563: PGNM_Fast,
|
||||
130564: PGNM_Fast,
|
||||
130565: PGNM_Fast,
|
||||
130566: PGNM_Fast,
|
||||
130567: PGNM_Fast,
|
||||
130569: PGNM_Fast,
|
||||
130570: PGNM_Fast,
|
||||
130571: PGNM_Fast,
|
||||
130572: PGNM_Fast,
|
||||
130573: PGNM_Fast,
|
||||
130574: PGNM_Fast,
|
||||
130576: PGNM_Single,
|
||||
130577: PGNM_Fast,
|
||||
130578: PGNM_Fast,
|
||||
130579: PGNM_Single,
|
||||
130580: PGNM_Fast,
|
||||
130581: PGNM_Fast,
|
||||
130582: PGNM_Single,
|
||||
130583: PGNM_Fast,
|
||||
130584: PGNM_Fast,
|
||||
130585: PGNM_Single,
|
||||
130586: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130816: PGNM_Fast,
|
||||
130817: PGNM_Fast,
|
||||
130817: PGNM_Fast,
|
||||
130818: PGNM_Fast,
|
||||
130819: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130820: PGNM_Fast,
|
||||
130821: PGNM_Fast,
|
||||
130821: PGNM_Fast,
|
||||
130822: PGNM_Fast,
|
||||
130823: PGNM_Fast,
|
||||
130824: PGNM_Fast,
|
||||
130824: PGNM_Fast,
|
||||
130825: PGNM_Fast,
|
||||
130827: PGNM_Fast,
|
||||
130828: PGNM_Fast,
|
||||
130831: PGNM_Fast,
|
||||
130832: PGNM_Fast,
|
||||
130833: PGNM_Fast,
|
||||
130834: PGNM_Fast,
|
||||
130835: PGNM_Fast,
|
||||
130836: PGNM_Fast,
|
||||
130836: PGNM_Fast,
|
||||
130837: PGNM_Fast,
|
||||
130837: PGNM_Fast,
|
||||
130838: PGNM_Fast,
|
||||
130839: PGNM_Fast,
|
||||
130840: PGNM_Fast,
|
||||
130842: PGNM_Fast,
|
||||
130842: PGNM_Fast,
|
||||
130842: PGNM_Fast,
|
||||
130843: PGNM_Fast,
|
||||
130843: PGNM_Fast,
|
||||
130845: PGNM_Fast,
|
||||
130845: PGNM_Fast,
|
||||
130846: PGNM_Fast,
|
||||
130846: PGNM_Fast,
|
||||
130847: PGNM_Fast,
|
||||
130850: PGNM_Fast,
|
||||
130850: PGNM_Fast,
|
||||
130850: PGNM_Fast,
|
||||
130851: PGNM_Fast,
|
||||
130856: PGNM_Fast,
|
||||
130860: PGNM_Fast,
|
||||
130880: PGNM_Fast,
|
||||
130881: PGNM_Fast,
|
||||
130944: PGNM_Fast,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
def logError(fmt,*args,keep=False):
|
||||
print("ERROR:" +fmt%(args),file=sys.stderr)
|
||||
if not keep:
|
||||
sys.exit(1)
|
||||
|
||||
def dataToSep(data,maxbytes=None):
|
||||
pd=None
|
||||
dl=int(len(data)/2)
|
||||
if maxbytes is not None and maxbytes < dl:
|
||||
dl=maxbytes
|
||||
for p in range(0,dl):
|
||||
i=2*p
|
||||
if pd is None:
|
||||
pd=data[i:i+2]
|
||||
else:
|
||||
pd+=","+data[i:i+2]
|
||||
return pd
|
||||
class Format:
|
||||
F_PLAIN=0
|
||||
N_PLAIN='plain'
|
||||
F_ACT=1
|
||||
N_ACT='actisense'
|
||||
F_PASS=2
|
||||
N_PASS='pass'
|
||||
F_SEASMART=3
|
||||
N_SEASMART="seasmart"
|
||||
def __init__(self,name,key,merge=True):
|
||||
self.key=key
|
||||
self.name=name
|
||||
self.merge=merge
|
||||
|
||||
class CanFrame:
|
||||
DUMP_PAT=re.compile(r'\(([^)]*)\) *([^ ]*) *([^#]*)#(.*)')
|
||||
|
||||
def __init__(self,ts,pgn,src=1,dst=255,prio=1,dev=None,hdr=None,data=None):
|
||||
self.pgn=pgn
|
||||
self.mode=PGN_MODES.get(pgn)
|
||||
self.ts=ts
|
||||
self.src=src
|
||||
self.dst=dst
|
||||
self.data=data
|
||||
self.prio=prio
|
||||
self.sequence=None
|
||||
self.frame=None
|
||||
self.len=8
|
||||
self.dev=dev
|
||||
self.hdr=hdr
|
||||
if self.mode == PGNM_Fast and data is not None and len(self.data) >= 2:
|
||||
fb=int(data[0:2],16)
|
||||
self.frame=fb & 0x1f
|
||||
self.sequence=fb >> 5
|
||||
|
||||
def key(self):
|
||||
if self.sequence is None or self.pgn == 0:
|
||||
return None
|
||||
return f"{self.pgn}-{self.sequence}-{self.src}"
|
||||
def getFPNum(self,bytes=False):
|
||||
if self.frame != 0:
|
||||
return None
|
||||
if len(self.data) < 4:
|
||||
return None
|
||||
numbytes=int(self.data[2:4],16)
|
||||
if bytes:
|
||||
return numbytes
|
||||
frames=int((numbytes-6-1)/7)+1+1 if numbytes > 6 else 1
|
||||
return frames
|
||||
|
||||
def _formatTs(self):
|
||||
dt=datetime.datetime.fromtimestamp(self.ts,tz=datetime.UTC)
|
||||
return dt.strftime("%F-%T.")+dt.strftime("%f")[0:3]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self._formatTs()},{self.prio},{self.pgn},{self.src},{self.dst},{self.len},{dataToSep(self.data)}"
|
||||
|
||||
def printOut(self,format:Format):
|
||||
if format.key == Format.F_PASS:
|
||||
return f"({self.ts:.6f}) {self.dev} {self.hdr}#{self.data}"
|
||||
else:
|
||||
return str(self)
|
||||
|
||||
|
||||
@classmethod
|
||||
def fromDump(cls,line:str):
|
||||
'''(1658058069.867835) can0 09F80103#ACAF6C20B79AAC06'''
|
||||
if line is None or line == '':
|
||||
return None
|
||||
match=cls.DUMP_PAT.search(line)
|
||||
if match is None:
|
||||
logError("no dump pattern in line %s",line,keep=True)
|
||||
return
|
||||
ts=match[1]
|
||||
data=match[4]
|
||||
hdr=match[3]
|
||||
hdrval=int(hdr,16)
|
||||
#see candump2analyzer
|
||||
src=hdrval & 0xff
|
||||
prio=(hdrval >> 26) & 0x7
|
||||
PF=(hdrval >> 16) & 0xff
|
||||
PS=(hdrval >> 8) & 0xff
|
||||
RDP=(hdrval >> 24) & 3
|
||||
pgn=0
|
||||
if PF < 240:
|
||||
dst=PS
|
||||
pgn=(RDP << 16) + (PF << 8)
|
||||
else:
|
||||
dst=0xff
|
||||
pgn=(RDP << 16) + (PF << 8)+PS
|
||||
return CanFrame(float(ts),pgn,src=src,dst=dst,prio=prio,data=data,dev=match[2],hdr=hdr)
|
||||
|
||||
class MultiFrame(CanFrame):
|
||||
def __init__(self,firstFrame: CanFrame):
|
||||
super().__init__(firstFrame.ts,firstFrame.pgn,
|
||||
src=firstFrame.src,dst=firstFrame.dst,prio=firstFrame.prio,
|
||||
dev=firstFrame.dev,hdr=firstFrame.hdr)
|
||||
self.data=""
|
||||
self.numFrames=firstFrame.getFPNum(bytes=False)
|
||||
self.len=firstFrame.getFPNum(bytes=True)
|
||||
self.finished=False
|
||||
self.addFrame(firstFrame)
|
||||
|
||||
def addFrame(self,frame:CanFrame):
|
||||
if self.finished:
|
||||
return False
|
||||
if frame.frame is None:
|
||||
return False
|
||||
if frame.frame == 0:
|
||||
self.data+=frame.data[4:]
|
||||
else:
|
||||
self.data+=frame.data[2:]
|
||||
if frame.frame >= (self.numFrames-1):
|
||||
self.finished=True
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
return f"{self._formatTs()},{self.prio},{self.pgn},{self.src},{self.dst},{self.len},{dataToSep(self.data,self.len)}"
|
||||
|
||||
def usage():
|
||||
print(f"usage: {sys.argv[0]} [-q] [-p pgn,pgn,...] [-w waitsec] [ -f plain|actisense] file",file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
|
||||
FORMATS=[
|
||||
Format(Format.N_PLAIN,Format.F_PLAIN),
|
||||
Format(Format.N_ACT,Format.F_ACT),
|
||||
Format(Format.N_PASS,Format.F_PASS,False),
|
||||
Format(Format.N_SEASMART,Format.F_SEASMART)
|
||||
]
|
||||
|
||||
MAX_ACT=400
|
||||
ACT_ESC=0x10
|
||||
ACT_START=0x2
|
||||
ACT_N2K=0x93
|
||||
ACT_END=0x3
|
||||
|
||||
class ActBuffer:
|
||||
def __init__(self):
|
||||
self.buf=bytearray(MAX_ACT)
|
||||
self.sum=0
|
||||
self.idx=0
|
||||
self.clear()
|
||||
def clear(self):
|
||||
self.sum=0
|
||||
self.idx=2
|
||||
self.buf[0:2]=(ACT_ESC,ACT_START)
|
||||
def add(self,val):
|
||||
#TODO: len check?
|
||||
val=val & 0xff
|
||||
self.buf[self.idx]=val
|
||||
self.sum = (self.sum + val) & 0xff
|
||||
self.idx+=1
|
||||
if val == ACT_ESC:
|
||||
self.buf[self.idx]=ACT_ESC
|
||||
self.idx+=1
|
||||
def finalize(self):
|
||||
self.sum=self.sum % 256
|
||||
self.sum = 256 - self.sum if self.sum != 0 else 0
|
||||
self.add(self.sum)
|
||||
self.buf[self.idx]=ACT_ESC
|
||||
self.idx+=1
|
||||
self.buf[self.idx]=ACT_END
|
||||
self.idx+=1
|
||||
|
||||
actBuffer=ActBuffer()
|
||||
|
||||
|
||||
LB=b'0000000000000'
|
||||
B_STAR=0x2a
|
||||
class SeasmartBuffer:
|
||||
def __init__(self):
|
||||
self.buf=bytearray(500)
|
||||
self.idx=0
|
||||
self.clear()
|
||||
def clear(self):
|
||||
self.idx=0
|
||||
def addB(self,bv,mlen=None):
|
||||
l=len(bv)
|
||||
if mlen is not None and mlen < l:
|
||||
l=mlen
|
||||
self.buf[self.idx:self.idx+l]=memoryview(bv)[0:l]
|
||||
else:
|
||||
self.buf[self.idx:self.idx+l]=bv
|
||||
self.idx+=l
|
||||
def addVal(self,val,blen=2):
|
||||
hs=hex(val)[2:].encode()
|
||||
if len(hs) != blen:
|
||||
hs=(LB+hs)[-blen:]
|
||||
self.addB(hs)
|
||||
|
||||
def finalize(self):
|
||||
sum=0
|
||||
self.buf[self.idx]=B_STAR
|
||||
self.idx+=1
|
||||
for b in memoryview(self.buf)[1:]:
|
||||
if b == B_STAR:
|
||||
break
|
||||
sum ^= b
|
||||
sum = sum & 0xff
|
||||
self.addVal(sum)
|
||||
self.addB(b'\x0d\x0a')
|
||||
|
||||
seasmartBuffer=SeasmartBuffer()
|
||||
|
||||
def send_act(frame_like:CanFrame,quiet,stream):
|
||||
try:
|
||||
actBuffer.clear()
|
||||
actBuffer.add(ACT_N2K)
|
||||
actBuffer.add(frame_like.len+11)
|
||||
actBuffer.add(frame_like.prio)
|
||||
pgn=frame_like.pgn
|
||||
actBuffer.add(pgn)
|
||||
pgn = pgn >> 8
|
||||
actBuffer.add(pgn)
|
||||
pgn = pgn >> 8;
|
||||
actBuffer.add(pgn)
|
||||
actBuffer.add(frame_like.dst)
|
||||
actBuffer.add(frame_like.src)
|
||||
#Time
|
||||
ts=int(frame_like.ts)
|
||||
actBuffer.add(ts>>24)
|
||||
actBuffer.add(ts>>16)
|
||||
actBuffer.add(ts>>8)
|
||||
actBuffer.add(ts)
|
||||
|
||||
actBuffer.add(frame_like.len)
|
||||
for i in range(0,frame_like.len*2,2):
|
||||
actBuffer.add(int(frame_like.data[i:i+2],16))
|
||||
actBuffer.finalize()
|
||||
written=stream.write(memoryview(actBuffer.buf)[0:actBuffer.idx])
|
||||
if (written != actBuffer.idx):
|
||||
if not quiet:
|
||||
logError(f"actisense not all bytes written {written}/{actBuffer.idx} for pgn={frame_like.pgn} ts={frame_like.ts}",keep=True)
|
||||
stream.flush()
|
||||
return True
|
||||
except Exception as e:
|
||||
if not quiet:
|
||||
print(f"Error writing actisense for pgn {frame_like.pgn}, idx={actBuffer.idx}: {e}",file=sys.stderr)
|
||||
return False
|
||||
|
||||
BK=b','
|
||||
def send_seasmart(frame_like:CanFrame,quiet,stream):
|
||||
try:
|
||||
seasmartBuffer.clear()
|
||||
seasmartBuffer.addB(b'$PCDIN,')
|
||||
seasmartBuffer.addVal(frame_like.pgn,6)
|
||||
seasmartBuffer.addB(BK)
|
||||
seasmartBuffer.addVal(int(frame_like.ts),8)
|
||||
seasmartBuffer.addB(BK)
|
||||
seasmartBuffer.addVal(frame_like.src)
|
||||
seasmartBuffer.addB(BK)
|
||||
seasmartBuffer.addB(frame_like.data.encode(),mlen=frame_like.len*2)
|
||||
seasmartBuffer.finalize()
|
||||
written=stream.write(memoryview(seasmartBuffer.buf)[0:seasmartBuffer.idx])
|
||||
if (written != seasmartBuffer.idx):
|
||||
if not quiet:
|
||||
raise Exception(f"seasmart not all bytes written {written}/{seasmartBuffer.idx} for pgn={frame_like.pgn} ts={frame_like.ts}")
|
||||
stream.flush()
|
||||
return True
|
||||
except Exception as e:
|
||||
if not quiet:
|
||||
logError(f"writing seasmart for pgn {frame_like.pgn}, idx={seasmartBuffer.idx}: {e}",keep=True)
|
||||
return False
|
||||
|
||||
class Counters:
|
||||
C_OK=1
|
||||
C_FAIL=2
|
||||
C_FRAME=3
|
||||
TITLES={
|
||||
C_OK:'OK',
|
||||
C_FAIL:'FAIL',
|
||||
C_FRAME:'FRAMES'
|
||||
}
|
||||
def __init__(self):
|
||||
self.counters={}
|
||||
for i in self.TITLES.keys():
|
||||
self.counters[i]=0
|
||||
def add(self,idx:int):
|
||||
if idx not in self.TITLES.keys():
|
||||
return
|
||||
self.counters[idx]+=1
|
||||
def __str__(self):
|
||||
rt=None
|
||||
for i in self.TITLES.keys():
|
||||
v=f"{self.TITLES[i]}:{self.counters[i]}"
|
||||
if rt is None:
|
||||
rt=v
|
||||
else:
|
||||
rt+=","+v
|
||||
return rt
|
||||
|
||||
def writeOut(frame:CanFrame,format:Format,quiet:bool,counters:Counters):
|
||||
rt=False
|
||||
if format.key == Format.F_ACT:
|
||||
rt= send_act(frame,quiet,sys.stdout.buffer)
|
||||
elif format.key == Format.F_SEASMART:
|
||||
rt= send_seasmart(frame,quiet,sys.stdout.buffer)
|
||||
else:
|
||||
print(frame.printOut(format))
|
||||
rt=True
|
||||
counters.add(Counters.C_OK if rt else Counters.C_FAIL)
|
||||
return rt
|
||||
|
||||
def findFormat(name:str)->Format:
|
||||
for f in FORMATS:
|
||||
if f.name == name:
|
||||
return f
|
||||
return None
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
opts,args=getopt.getopt(sys.argv[1:],"hp:qw:f:")
|
||||
except getopt.GetoptError as e:
|
||||
logError(e)
|
||||
pgnlist=[]
|
||||
quiet=False
|
||||
delay=0.0
|
||||
format=findFormat(Format.N_PLAIN)
|
||||
for o,a in opts:
|
||||
if o == '-h':
|
||||
usage()
|
||||
elif o == '-q':
|
||||
quiet=True
|
||||
elif o == '-p':
|
||||
pgns=(int(x) for x in a.split(","))
|
||||
pgnlist.extend(pgns)
|
||||
elif o == '-w':
|
||||
delay=float(a)
|
||||
elif o == '-f':
|
||||
format=findFormat(a)
|
||||
if format is None:
|
||||
logError(f"invalid format {a}, allowed {','.join(x.name for x in FORMATS)}")
|
||||
if len(args) < 1:
|
||||
usage()
|
||||
hasFilter=len(pgnlist) > 0
|
||||
if not quiet and hasFilter:
|
||||
print(f"PGNs: {','.join(str(x) for x in pgnlist)}",file=sys.stderr)
|
||||
counters=Counters()
|
||||
with open (args[0],"r") as fh:
|
||||
buffer={}
|
||||
lnr=0
|
||||
for line in fh:
|
||||
lnr+=1
|
||||
frame=CanFrame.fromDump(line)
|
||||
if frame is None:
|
||||
continue
|
||||
if hasFilter and not frame.pgn in pgnlist:
|
||||
continue
|
||||
counters.add(Counters.C_FRAME)
|
||||
if frame.sequence is None or not format.merge:
|
||||
writeOut(frame,format,quiet,counters=counters)
|
||||
if delay > 0:
|
||||
time.sleep(delay)
|
||||
else:
|
||||
key=frame.key()
|
||||
mf=buffer.get(key)
|
||||
mustDelete=False
|
||||
if mf is None:
|
||||
if frame.frame != 0:
|
||||
if not quiet:
|
||||
print(f"floating multi frame in line {lnr}: {frame}",file=sys.stderr)
|
||||
continue
|
||||
mf=MultiFrame(frame)
|
||||
if not mf.finished:
|
||||
buffer[key]=mf
|
||||
else:
|
||||
mf.addFrame(frame)
|
||||
mustDelete=True
|
||||
if mf.finished:
|
||||
writeOut(mf,format,quiet,counters=counters)
|
||||
if mustDelete:
|
||||
del buffer[key]
|
||||
if delay > 0:
|
||||
time.sleep(delay)
|
||||
if not quiet:
|
||||
print(f"STATISTICS: {counters}",file=sys.stderr)
|
||||
|
||||
Reference in New Issue
Block a user