Add DSLogic capture, signal conversion and DSView decoders
This commit is contained in:
@@ -42,6 +42,7 @@ class DigitalCapture:
|
||||
start: float
|
||||
end: float
|
||||
sample_rate: float = 0
|
||||
trigger_time: float = None
|
||||
|
||||
|
||||
def _check(cancel):
|
||||
@@ -54,12 +55,14 @@ def read_capture(path, progress=lambda value: None, cancel=lambda: False):
|
||||
try:
|
||||
if path.suffix.lower() == '.csv':
|
||||
result = _csv(path, progress, cancel)
|
||||
elif path.suffix.lower() == '.json':
|
||||
result = _json(path, progress, cancel)
|
||||
elif path.suffix.lower() in ('.sal', '.dsl'):
|
||||
with zipfile.ZipFile(path) as archive:
|
||||
result = (_sal if path.suffix.lower() == '.sal' else _dsl)(
|
||||
path, archive, progress, cancel)
|
||||
else:
|
||||
raise ValueError('Выберите цифровую запись CSV, SAL или DSL.')
|
||||
raise ValueError('Выберите цифровую запись CSV, JSON, SAL или DSL.')
|
||||
except (KeyError, struct.error, zipfile.BadZipFile, EOFError,
|
||||
configparser.Error, UnicodeError) as exc:
|
||||
raise ValueError('Повреждённый или неподдерживаемый файл: %s' % exc) from exc
|
||||
@@ -70,6 +73,58 @@ def read_capture(path, progress=lambda value: None, cancel=lambda: False):
|
||||
return result
|
||||
|
||||
|
||||
def _json(path, progress, cancel):
|
||||
def unique_object(pairs):
|
||||
result = {}
|
||||
for key, value in pairs:
|
||||
if key in result:
|
||||
raise ValueError('Повторяющееся поле JSON: %s.' % key)
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
_check(cancel)
|
||||
with path.open(encoding='utf-8-sig') as stream:
|
||||
rows = json.load(stream, object_pairs_hook=unique_object)
|
||||
_check(cancel)
|
||||
if not isinstance(rows, list) or not rows or not isinstance(rows[0], dict):
|
||||
raise ValueError('Ожидается JSON: непустой массив объектов с Time [s] и каналами 0/1.')
|
||||
names = list(rows[0])
|
||||
if ('Time [s]' not in names or not 2 <= len(names) <= 65
|
||||
or any(not name.strip() for name in names)):
|
||||
raise ValueError('JSON должен содержать Time [s] и от 1 до 64 цифровых каналов.')
|
||||
channels = [DigitalChannel(name, 0, array('d')) for name in names if name != 'Time [s]']
|
||||
fields = set(names)
|
||||
start = end = None
|
||||
previous = []
|
||||
for index, row in enumerate(rows):
|
||||
if index % 4096 == 0:
|
||||
_check(cancel)
|
||||
progress(index * 99 // len(rows))
|
||||
try:
|
||||
if not isinstance(row, dict) or set(row) != fields:
|
||||
raise ValueError('набор полей должен быть одинаковым во всех строках')
|
||||
time = row['Time [s]']
|
||||
if (type(time) not in (int, float) or not math.isfinite(time)
|
||||
or (end is not None and time <= end)):
|
||||
raise ValueError('время должно быть числом, строго возрастать и быть конечным')
|
||||
values = [row[c.name] for c in channels]
|
||||
if any(type(v) not in (int, float, bool) or v not in (0, 1) for v in values):
|
||||
raise ValueError('цифровые уровни должны быть 0 или 1')
|
||||
if start is None:
|
||||
start = time
|
||||
for channel, value in zip(channels, values):
|
||||
channel.initial = int(value)
|
||||
else:
|
||||
for channel, old, value in zip(channels, previous, values):
|
||||
if old != value:
|
||||
channel.edges.append(time)
|
||||
previous = values
|
||||
end = time
|
||||
except (ValueError, OverflowError) as exc:
|
||||
raise ValueError('JSON, строка %d: %s' % (index + 1, exc)) from exc
|
||||
return DigitalCapture(str(path), 'JSON', channels, start, end)
|
||||
|
||||
|
||||
def _csv(path, progress, cancel):
|
||||
size = max(1, path.stat().st_size)
|
||||
consumed = 0
|
||||
|
||||
Reference in New Issue
Block a user