Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add old version with MY18 support #12

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,105 changes: 4,105 additions & 0 deletions examples/133202.TSV

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions examples/ParseCAN_old/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import data
from . import spec

__all__ = ['data', 'spec']
61 changes: 61 additions & 0 deletions examples/ParseCAN_old/data/Frame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from .. import parse, data, meta, helper

__all__ = ['Frame', 'FrameTimed']


class Frame(meta.message):

attributes = ('can_id', 'data', 'dlc')

def __init__(self, can_id, data):
# Store our attributes in the format we want them.
self.can_id = int(can_id)
self.data = data

@property
def data(self):
return self._data

@data.setter
def data(self, x):
if not isinstance(x, (bytes, bytearray)):
raise TypeError('data must be of type bytes or bytearray')

self._data = x
self._raw_data = int.from_bytes(x, byteorder='big', signed=False) << 8 * (8 - len(x))
# self._raw_data = int.from_bytes(x, byteorder='big', signed=False)

def __getitem__(self, index):
'''
Returns the bit or range of bits within data.
'''
if isinstance(index, int):
position = index
length = 1
elif isinstance(index, tuple):
position, length = index
else:
raise TypeError('frame indices must be integers or tuples, not {}'
.format(type(index).__name__))

return data.evil_macros.EXTRACT(self._raw_data, position, length)

__str__ = helper.csv_by_attrs(attributes, mapdict={
'time': parse.number_in('ms'),
'can_id': hex,
'data': hex
})

def __iter__(self):
return (getattr(self, x) for x in self.attributes)

def unpack(self, spec, **kwargs):
return spec.unpack(self, **kwargs)


class FrameTimed(Frame):
attributes = ('time', 'can_id', 'data')

def __init__(self, *args, time, **kwargs):
super().__init__(*args, **kwargs)
self.time = time
38 changes: 38 additions & 0 deletions examples/ParseCAN_old/data/Log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import csv
from pathlib import Path
from .. import data, parse


class Log:
def __init__(self, source, parser):
self.src = Path(source)
self.parser = parser
self.length = 0
self.parsed = 0
with self.src.open('r') as f:
for l in f:
self.length += 1

def __iter__(self):
'''
Returns an iterator of the valid outputs of self.parser.
'''
def parse_progress(line):
self.parsed += 1
return self.parser(line)
return filter(bool, map(parse_progress, self.src.open('r')))

def unpack(self, spec, **kwargs):
return ((msg, spec.unpack(msg, **kwargs)) for msg in self)

def csv(self, outpath):
'''
Outputs a csv representation of the log in outpath.
'''
outpath = Path(outpath)
csvfile = outpath.open('w', newline='')
writer = csv.writer(csvfile)

writer.writerow(data.FrameTimed.attributes)
for message in self:
writer.writerow(message)
56 changes: 56 additions & 0 deletions examples/ParseCAN_old/data/Race.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from itertools import chain
from pathlib import Path
from .. import data


class Race:
def __init__(self, srcpath, outdir=None, srcsuff='.log'):
'''
Expects a path to the files in the form of a string or a pathlib.Path.
Expects an outdir in the same format.
srcsuff: suffix of log files
'''
self.srcdir = Path(srcpath)
self.srcsuff = srcsuff
self.outdir = Path(outdir) if outdir else self.srcdir.joinpath('out')

def logfiles(self):
return self.srcdir.glob('*' + self.srcsuff)

def __iter__(self):
'''
Returns a generator of Log objects within self.srcdir.
'''
return (data.log(logfile) for logfile in self.logfiles())

@property
def messages(self):
'''
An iterator of all the messages in each of the logs in this race.
'''
return chain.from_iterable(self)

def unpack(self, spec):
'''
An iterator of the unpackation of all the messages
in each of the logs in this race.
'''
return (spec.unpack(msg) for msg in self.messages)

# def __getattr__(self, index):
# '''
# Returns the log file with index i.
# '''
# raise NotImplementedError

def csv(self, outdir=None):
'''
Outputs csv files of each log into outdir/csv.
'''
outdir = Path(outdir) if outdir else self.outdir.joinpath('csv')

if not outdir.exists():
outdir.mkdir()

for log in self:
log.csv(outdir.joinpath(log.src.stem + '.csv'))
6 changes: 6 additions & 0 deletions examples/ParseCAN_old/data/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import evil_macros
from .Race import Race as race
from .Log import Log as log
from .Frame import *

__all__ = ['race', 'log']
60 changes: 60 additions & 0 deletions examples/ParseCAN_old/data/evil_macros.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import struct

fmttolen = {
'b': 1,
'B': 1,
'h': 2,
'H': 2,
'i': 4,
'I': 4,
'q': 8,
'Q': 8
}


def CAST(type, num, endianness='big', reverse=False):
if endianness == 'big':
d = '<' if reverse else '>'
else:
d = '>' if reverse else '<'

inbytes = num.to_bytes(fmttolen[type], endianness)
return struct.unpack(d + type, inbytes)[0]


def cast_gen(type, **kwargs):
def closure(x):
return CAST(type, num=x, **kwargs)

return closure


def RPAD(n, l):
'''
Pads an integer `n` with zeros until it's `l` bits long.
'''
return n << (l - n.bit_length())


def ONES(leng):
return ((1 << (leng)) - 1)


def START_IDX(start, leng):
return (64 - (start) - (leng))


def ZEROES_MASK(start, leng):
return (~(ONES(leng) << START_IDX(start, leng)))


def INPUT_MASK(inp, start, leng):
return (((inp) & ONES(leng)) << START_IDX(start, leng))


def INSERT(inp, out, start, leng):
return (((out) & (ZEROES_MASK(start, leng))) | INPUT_MASK(inp, start, leng))


def EXTRACT(inp, start, leng):
return (((inp) >> START_IDX(start, leng)) & ONES(leng))
37 changes: 37 additions & 0 deletions examples/ParseCAN_old/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
def dict_key_populated(D: dict, key, value) -> bool:
'''
Returns True if `D[key]` is populated by a value that is not `value`,
False otherwise.
'''
return key in D and D[key] != value


def tuples_to_dict_list(L):
'''
Converts an iterable of tuples of key, value pairs into a dictionary
with each key mapping to a list values.

tuples_to_dict_list([('A', 1), ('A', 1), ('A', 3), ('B', 1)]) == {'A': [1, 1, 3], 'B': [1]}
'''
D = {}
for k, v in L:
if k in D:
D[k].append(v)
else:
D[k] = [v]

return D


def attr_extract(obj, attrs, mapdict=None):
if mapdict:
return (mapdict[attr](getattr(obj, attr, None)) for attr in attrs)
else:
return (getattr(obj, attr, None) for attr in attrs)


def csv_by_attrs(attrs, mapdict=None):
def csv(obj):
return ','.join(map(str, attr_extract(obj, attrs, mapdict)))

return csv
5 changes: 5 additions & 0 deletions examples/ParseCAN_old/meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class message:
'''
A meta-message may or may not contain properties other than a can_id.
'''
attributes = ('can_id')
59 changes: 59 additions & 0 deletions examples/ParseCAN_old/parse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'''
A module containing all the specifics about each file format.
'''
import pint

ureg = pint.UnitRegistry()
ureg.define('dmrad = 0.1 * mrad')
ureg.define('fivemm = 0.5 * mm')
ureg.define('dudeg = 1e-7 * deg')
# TODO: Multiple specs could define units differently.
# Make unit definitions spec specific.


def number(num, unit=False):
'''
Parses a number into an `int`, `float`, or `Quantity` whenever appropriate.

- If `unit` is a string or Quantity, a number with the unit described will
be returned. In that case `num` must be dimensionless.

- If `unit` is truthy the number will be forced to have a unit.
'''
# print(num, unit)
if isinstance(num, ureg.Quantity):
if isinstance(unit, (str, ureg.Quantity)):
return num.to(unit)

return num

if isinstance(unit, (str, ureg.Quantity)):
# print(num, unit, type(unit))
return ureg.Quantity(float(num), unit)

if unit:
return ureg.Quantity(num)
elif isinstance(num, (int, float)):
return num

return ureg.parse_expression(num)


def number_in(unit):
def in_unit(num):
return number(num, unit).magnitude

return in_unit


def hexstr_to_bytes(s):
'''
Returns a bytes object corresponding to to the hex string `s` given.
len(s) must be a multiple of 2.
'''

if len(s) % 2 == 1:
raise ValueError('given hex string is of odd length')

gen = (int(twostr, 16) for twostr in map(''.join, zip(s[::2], s[1::2])))
return bytes(gen)
Loading