From 3b84c8361d403e7816741a716ce4165f7160e8ab Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 8 Oct 2023 11:19:17 +0200 Subject: [PATCH 01/99] today() --- pyxcp/cpp_ext/__init__.py | 1 + pyxcp/cpp_ext/bin.hpp | 81 ++++ pyxcp/cpp_ext/daqlist.hpp | 101 +++++ pyxcp/cpp_ext/extension_wrapper.cpp | 69 +++ pyxcp/cpp_ext/mcobject.hpp | 139 ++++++ pyxcp/daq_stim/__init__.py | 157 +++++++ pyxcp/daq_stim/optimize/__init__.py | 68 +++ pyxcp/daq_stim/optimize/binpacking.py | 43 ++ pyxcp/examples/xcp_policy.py | 6 +- pyxcp/master/errorhandler.py | 24 +- pyxcp/master/master.py | 16 +- pyxcp/recorder/__init__.py | 6 +- pyxcp/recorder/reader.hpp | 121 +++++ pyxcp/recorder/rekorder.hpp | 625 ++++++-------------------- pyxcp/recorder/setup.py | 2 +- pyxcp/recorder/unfolder.hpp | 362 +++++++++++++++ pyxcp/recorder/wrap.cpp | 25 +- pyxcp/recorder/writer.hpp | 218 +++++++++ pyxcp/tests/test_binpacking.py | 186 ++++++++ pyxcp/tests/test_daq.py | 195 ++++++++ 20 files changed, 1924 insertions(+), 521 deletions(-) create mode 100644 pyxcp/cpp_ext/__init__.py create mode 100644 pyxcp/cpp_ext/bin.hpp create mode 100644 pyxcp/cpp_ext/daqlist.hpp create mode 100644 pyxcp/cpp_ext/extension_wrapper.cpp create mode 100644 pyxcp/cpp_ext/mcobject.hpp create mode 100644 pyxcp/daq_stim/__init__.py create mode 100644 pyxcp/daq_stim/optimize/__init__.py create mode 100644 pyxcp/daq_stim/optimize/binpacking.py create mode 100644 pyxcp/recorder/reader.hpp create mode 100644 pyxcp/recorder/unfolder.hpp create mode 100644 pyxcp/recorder/writer.hpp create mode 100644 pyxcp/tests/test_binpacking.py create mode 100644 pyxcp/tests/test_daq.py diff --git a/pyxcp/cpp_ext/__init__.py b/pyxcp/cpp_ext/__init__.py new file mode 100644 index 0000000..e80c591 --- /dev/null +++ b/pyxcp/cpp_ext/__init__.py @@ -0,0 +1 @@ +from .cpp_ext import (Bin, DaqList, McObject) \ No newline at end of file diff --git a/pyxcp/cpp_ext/bin.hpp b/pyxcp/cpp_ext/bin.hpp new file mode 100644 index 0000000..36052d5 --- /dev/null +++ b/pyxcp/cpp_ext/bin.hpp @@ -0,0 +1,81 @@ + +#if !defined(__BIN_HPP) + #define __BIN_HPP + + #include + #include + #include + #include + #include + #include + + #include "mcobject.hpp" + +class Bin { + public: + + Bin(std::uint16_t size) : m_size(size), m_residual_capacity(size) { + } + + void append(const McObject& bin) { + m_entries.emplace_back(bin); + } + + std::uint16_t get_size() const { + return m_size; + } + + void set_size(const std::uint16_t size) { + m_size = size; + } + + std::uint16_t get_residual_capacity() const { + return m_residual_capacity; + } + + void set_residual_capacity(const std::uint16_t residual_capacity) { + m_residual_capacity = residual_capacity; + } + + const std::vector& get_entries() const { + return m_entries; + } + + bool operator==(const Bin& other) const { + return (m_size == other.m_size) && (m_residual_capacity == other.m_residual_capacity) && (m_entries == other.m_entries); + } + + private: + + std::uint16_t m_size; + std::uint16_t m_residual_capacity; + std::vector m_entries{}; +}; + +std::string bin_entries_to_string(const std::vector& entries); + +std::string to_string(const Bin& obj) { + std::stringstream ss; + + ss << "Bin(residual_capacity=" << obj.get_residual_capacity() << ", entries=[" << bin_entries_to_string(obj.get_entries()) + << "])"; + return ss.str(); +} + +std::string bin_entries_to_string(const std::vector& entries) { + std::stringstream ss; + + for (const auto& entry : entries) { + ss << to_string(entry) << ", "; + } + return ss.str(); +} + + #if 0 + + @property + def __len__(self) -> int: + return len(self.entries) + #endif + +#endif // __BIN_HPP diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp new file mode 100644 index 0000000..4f6d7bc --- /dev/null +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -0,0 +1,101 @@ + + +#if !defined(__DAQ_LIST_HPP) + #define __DAQ_LIST_HPP + + #include "bin.hpp" + #include "mcobject.hpp" + +class DaqList { + public: + + using daq_list_initialzer_t = std::tuple; + using flatten_odts_t = + std::vector>>; + + DaqList(std::uint16_t event_num, bool enable_timestamps, const std::vector& measurements) : + m_event_num(event_num), m_enable_timestamps(enable_timestamps) { + for (const auto& measurement : measurements) { + auto const& [name, address, ext, length, dt_name] = measurement; + // std::cout << "DL-obj: " << name << ", " << address << ", " << ext << ", " << length << ", " << dt_name << std::endl; + m_measurements.emplace_back(McObject(name, address, ext, length, dt_name)); + } + } + + bool get_enable_timestamps() const { + return m_enable_timestamps; + } + + bool get_event_num() const { + return m_event_num; + } + + const std::vector& get_measurements() const { + return m_measurements; + } + + const std::vector& get_measurements_opt() const { + return m_measurements_opt; + } + + const std::vector& get_header_names() const { + return m_header_names; + } + + std::uint16_t get_odt_count() const { + return m_odt_count; + } + + std::uint16_t get_total_entries() const { + return m_total_entries; + } + + std::uint16_t get_total_length() const { + return m_total_length; + } + + const flatten_odts_t& get_flatten_odts() const { + return m_flatten_odts; + } + + void set_measurements_opt(const std::vector& measurements_opt) { + m_measurements_opt = measurements_opt; + + auto odt_count = 0u; + auto total_entries = 0u; + auto total_length = 0u; + for (const auto& bin : measurements_opt) { + odt_count++; + std::vector> flatten_odt{}; + for (const auto& mc_obj : bin.get_entries()) { + for (const auto& component : mc_obj.get_components()) { + m_header_names.emplace_back(component.get_name()); + flatten_odt.emplace_back( + component.get_name(), component.get_address(), component.get_ext(), component.get_length(), + component.get_type_index() + ); + total_entries++; + total_length += component.get_length(); + } + } + m_flatten_odts.emplace_back(flatten_odt); + } + m_odt_count = odt_count; + m_total_entries = total_entries; + m_total_length = total_length; + } + + private: + + std::uint16_t m_event_num; + bool m_enable_timestamps; + std::vector m_measurements; + std::vector m_measurements_opt; + std::vector m_header_names; + std::uint16_t m_odt_count; + std::uint16_t m_total_entries; + std::uint16_t m_total_length; + flatten_odts_t m_flatten_odts; +}; + +#endif // __DAQ_LIST_HPP diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp new file mode 100644 index 0000000..5fb4939 --- /dev/null +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -0,0 +1,69 @@ + +#include +#include +#include +#include +#include + +#include + +#include "mcobject.hpp" +#include "bin.hpp" +#include "daqlist.hpp" + +namespace py = pybind11; +using namespace pybind11::literals; + +PYBIND11_MODULE(cpp_ext, m) { + m.doc() = "C++ extensions for pyXCP."; + + py::class_(m, "McObject") + .def(py::init&>() + ,"name"_a, "address"_a, "ext"_a, "length"_a, "data_type"_a="", "components"_a=std::vector() + ) + .def_property("name", &McObject::get_name, &McObject::set_name) + .def_property("address", &McObject::get_address, &McObject::set_address) + .def_property("ext", &McObject::get_ext, &McObject::set_ext) + .def_property("length", &McObject::get_length, &McObject::set_length) + .def_property("data_type", &McObject::get_data_type, &McObject::set_data_type) + + .def("add_component", &McObject::add_component, "component"_a) + .def("__repr__", [](const McObject& self) { + return to_string(self); + }) + ; + + py::class_(m, "Bin") + .def(py::init(), "size"_a) + .def_property("size", &Bin::get_size, &Bin::set_size) + .def_property("residual_capacity", &Bin::get_residual_capacity, &Bin::set_residual_capacity) + .def_property("entries", &Bin::get_entries, nullptr) + .def("append", &Bin::append) + + .def("__repr__", [](const Bin& self) { + return to_string(self); + }) + + .def("__eq__", [](const Bin& self, const Bin& other) { + return self == other; + }) + + .def("__len__", [](const Bin& self) { + return std::size(self.get_entries()); + }) + ; + + py::class_(m, "DaqList") + .def(py::init&>(), + "event_num"_a, "enable_timestamps"_a, "measurements"_a) + .def_property("event_num", &DaqList::get_event_num, nullptr) + .def_property("enable_timestamps", &DaqList::get_enable_timestamps, nullptr) + .def_property("measurements", &DaqList::get_measurements, nullptr) + .def_property("measurements_opt", &DaqList::get_measurements_opt, &DaqList::set_measurements_opt) + .def_property("header_names", &DaqList::get_header_names, nullptr) + .def_property("odt_count", &DaqList::get_odt_count, nullptr) + .def_property("total_entries", &DaqList::get_total_entries, nullptr) + .def_property("total_length", &DaqList::get_total_length, nullptr) + ; + +} diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp new file mode 100644 index 0000000..620bf1e --- /dev/null +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -0,0 +1,139 @@ + +#if !defined(__MC_OBJECT_HPP) + #define __MC_OBJECT_HPP + + #include + #include + #include + #include + #include + +const std::map> TYPE_MAP = { + {"U8", { 0, 1 }}, + { "I8", { 1, 1 }}, + { "U16", { 2, 2 }}, + { "I16", { 3, 2 }}, + { "U32", { 4, 4 }}, + { "I32", { 5, 4 }}, + { "U64", { 6, 8 }}, + { "I64", { 7, 8 }}, + { "F32", { 8, 4 }}, + { "F64", { 9, 8 }}, +}; + +class McObject { + public: + + explicit McObject( + std::string_view name, std::uint32_t address, std::uint8_t ext, std::uint16_t length, const std::string& data_type, + const std::vector& components = std::vector() + ) : + m_name(name), + m_address(address), + m_ext(ext), + m_length(length), + m_data_type(data_type), + m_components(components), + m_type_index(-1) { + if (data_type != "") { + const auto [ti, len] = TYPE_MAP.at(data_type); + m_type_index = ti; + m_length = len; + } + } + + McObject(const McObject& obj) = default; + McObject(McObject&& obj) = default; + McObject& operator=(const McObject&) = default; + McObject& operator=(McObject&&) = default; + + const std::string& get_name() const { + return m_name; + } + + void set_name(std::string_view name) { + m_name = name; + } + + std::uint32_t get_address() const { + return m_address; + } + + void set_address(std::uint32_t address) { + m_address = address; + } + + std::uint8_t get_ext() const { + return m_ext; + } + + void set_ext(std::uint8_t ext) { + m_ext = ext; + } + + const std::string& get_data_type() const { + return m_data_type; + } + + void set_data_type(const std::string& value) { + m_data_type = value; + } + + std::uint16_t get_length() const { + return m_length; + } + + void set_length(std::uint16_t length) { + m_length = length; + } + + std::int32_t get_type_index() const { + return m_type_index; + } + + const std::vector& get_components() const { + return m_components; + } + + void add_component(const McObject& obj) { + m_components.emplace_back(obj); + } + + bool operator==(const McObject& other) const { + return (m_name == other.m_name) && (m_address == other.m_address) && (m_ext == other.m_ext) && + (m_length == other.m_length) && (m_data_type == other.m_data_type) && + (std::equal(m_components.begin(), m_components.end(), other.m_components.begin(), other.m_components.end())); + } + + private: + + std::string m_name; + std::uint32_t m_address; + std::uint8_t m_ext; + std::uint16_t m_length; + std::string m_data_type; + std::int16_t m_type_index; + std::vector m_components{}; +}; + +std::string mc_components_to_string(const std::vector& components); + +std::string to_string(const McObject& obj) { + std::stringstream ss; + + ss << "McObject(name='" << obj.get_name() << "', address=" << obj.get_address() + << ", ext=" << static_cast(obj.get_ext()) << ", data_type='" << obj.get_data_type() + << "', length=" << obj.get_length() << ", components=[" << mc_components_to_string(obj.get_components()) << "])"; + return ss.str(); +} + +std::string mc_components_to_string(const std::vector& components) { + std::stringstream ss; + + for (const auto& obj : components) { + ss << to_string(obj) << ", "; + } + return ss.str(); +} + +#endif // __MC_OBJECT_HPP diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py new file mode 100644 index 0000000..86855a8 --- /dev/null +++ b/pyxcp/daq_stim/__init__.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import struct +from collections import defaultdict +from dataclasses import dataclass +from dataclasses import field +from pprint import pprint +from typing import Any +from typing import Callable +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple + +from pyxcp import types +from pyxcp.cpp_ext import DaqList +from pyxcp.daq_stim.optimize import make_continuous_blocks +from pyxcp.daq_stim.optimize import McObject +from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing +from pyxcp.recorder import UnfoldingParameters +from pyxcp.recorder import XcpLogFileReader +from pyxcp.recorder import XcpLogFileUnfolder +from pyxcp.types import FrameCategory + + +DAQ_ID_FIELD_SIZE = { + "IDF_ABS_ODT_NUMBER": 1, + "IDF_REL_ODT_NUMBER_ABS_DAQ_LIST_NUMBER_BYTE": 2, + "IDF_REL_ODT_NUMBER_ABS_DAQ_LIST_NUMBER_WORD": 3, + "IDF_REL_ODT_NUMBER_ABS_DAQ_LIST_NUMBER_WORD_ALIGNED": 4, +} + +DAQ_TIMESTAMP_SIZE = { + "S1": 1, + "S2": 2, + "S4": 4, +} + + +class Daq: + def __init__(self, file_name: str, callback: Optional[Callable[[int, Tuple], None]] = None): + self.callback = callback + self.file_name = file_name + + def set_master(self, xcp_master): + self.xcp_master = xcp_master + + def add_daq_lists(self, daq_lists: List[DaqList]): + self.daq_lists = daq_lists + + def setup(self, write_multiple: bool = True): + self.daq_info = self.xcp_master.getDaqInfo() + pprint(self.daq_info) + try: + processor = self.daq_info.get("processor") + properties = processor.get("properties") + resolution = self.daq_info.get("resolution") + if properties["configType"] == "STATIC": + raise TypeError("DAQ configuration is static, cannot proceed.") + self.supports_timestampes = properties["timestampSupported"] + self.supports_prescaler = properties["prescalerSupported"] + if self.supports_timestampes: + mode = resolution.get("timestampMode") + self.ts_fixed = mode.get("fixed") + self.ts_size = DAQ_TIMESTAMP_SIZE[mode.get("size")] + ts_unit_exp = types.DAQ_TIMESTAMP_UNIT_TO_EXP[mode.get("unit")] + ts_ticks = resolution.get("timestampTicks") + self.ts_scale_factor = (10**ts_unit_exp) * ts_ticks + else: + self.ts_size = 0 + self.ts_fixed = False + key_byte = processor.get("keyByte") + header_len = DAQ_ID_FIELD_SIZE[key_byte["identificationField"]] + max_dto = self.xcp_master.slaveProperties.maxDto + max_odt_entry_size = resolution.get("maxOdtEntrySizeDaq") + max_payload_size = min(max_odt_entry_size, max_dto - header_len) + self.min_daq = processor.get("minDaq") + except Exception as e: + raise TypeError(f"DAQ_INFO corrupted: {e}") from e + + # DAQ optimization. + for daq_list in self.daq_lists: + ttt = make_continuous_blocks(daq_list.measurements, max_payload_size) + daq_list.measurements_opt = first_fit_decreasing(ttt, max_payload_size) + + byte_order = 0 if self.xcp_master.slaveProperties.byteOrder == "INTEL" else 1 + self.uf = UnfoldingParameters(byte_order, header_len, self.ts_scale_factor, False, self.ts_size, self.daq_lists) + + self.first_pids = [] + daq_count = len(self.daq_lists) + self.xcp_master.freeDaq() + # Allocate + self.xcp_master.allocDaq(daq_count) + for i, daq_list in enumerate(self.daq_lists, self.min_daq): + measurements = daq_list.measurements_opt + odt_count = len(measurements) + self.xcp_master.allocOdt(i, odt_count) + for j, measurement in enumerate(measurements): + entry_count = len(measurement.entries) + self.xcp_master.allocOdtEntry(i, j, entry_count) + # Write DAQs + for i, daq_list in enumerate(self.daq_lists, self.min_daq): + # self.xcp_master.setDaqListMode(daqListNumber=i, mode=0x10, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xff) + measurements = daq_list.measurements_opt + for j, measurement in enumerate(measurements): + self.xcp_master.setDaqPtr(i, j, 0) + for entry in measurement.entries: + self.xcp_master.writeDaq(0xFF, entry.length, entry.ext, entry.address) + + def start(self): + for i, daq_list in enumerate(self.daq_lists, self.min_daq): + mode = 0x10 if daq_list.enable_timestamps else 0x00 + self.xcp_master.setDaqListMode( + daqListNumber=i, mode=mode, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xFF # TODO: + MIN_DAQ + ) + res = self.xcp_master.startStopDaqList(0x02, i) + self.first_pids.append(res.firstPid) + self.xcp_master.startStopSynch(0x01) + + def stop(self): + self.xcp_master.startStopSynch(0x00) + + def reader(self): + unfolder = XcpLogFileUnfolder(self.file_name, self.uf) + unfolder.start(self.first_pids) + + for block in unfolder.next_block(): + print(block) + + +class Collector: + def __init__(self, daq_num: int, num_odts: int, unfolder, callback): + self.daq_num = daq_num + self.num_odts = num_odts + self.current_odt_num = 0 + self.frames = [None] * num_odts + self.unfolder = unfolder + self.callback = callback + + def add(self, odt_num, frame): + if odt_num != self.current_odt_num: + print(f"WRONG SEQ-NO {odt_num} expected {self.current_odt_num} [LIST: {self.daq_num}]") + self.current_odt_num = odt_num + self.frames[self.current_odt_num] = frame + self.current_odt_num += 1 + if self.current_odt_num == self.num_odts: + result = {} + for idx, frame in enumerate(self.frames): + offset = 0 + for name, length in self.unfolder[idx]: + data = frame[offset : offset + length] + result[name] = bytes(data) + offset += length + if self.callback is not None: + self.callback(0, result) + # print("DAQ", self.daq_num, result) + self.current_odt_num %= self.num_odts diff --git a/pyxcp/daq_stim/optimize/__init__.py b/pyxcp/daq_stim/optimize/__init__.py new file mode 100644 index 0000000..e5d8b96 --- /dev/null +++ b/pyxcp/daq_stim/optimize/__init__.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Optimize data-structures like memory sections.""" +from collections import defaultdict +from dataclasses import dataclass +from dataclasses import field +from itertools import groupby +from operator import attrgetter +from typing import Any +from typing import Dict +from typing import List +from typing import Tuple +from typing import Union + +from pyxcp.cpp_ext import McObject + + +def make_continuous_blocks(chunks: List[McObject], upper_bound=None, upper_bound_initial=None) -> List[McObject]: + """Try to make continous blocks from a list of small, unordered `chunks`. + + Parameters + ---------- + chunks: list of `McObject` + + Returns + ------- + sorted list of `McObject` + """ + + def key_func(x): + return (x.ext, x.address) + + values = [] + # 1. Groupy by address. + for _key, value in groupby(sorted(chunks, key=key_func), key=key_func): + # 2. Pick the largest one. + values.append(max(value, key=attrgetter("length"))) + result_sections = [] + last_section = None + last_ext = None + while values: + section = values.pop(0) + if (last_section and section.address <= last_section.address + last_section.length) and not (section.ext != last_ext): + last_end = last_section.address + last_section.length - 1 + current_end = section.address + section.length - 1 + if last_end > section.address: + pass + else: + offset = current_end - last_end + if upper_bound: + if last_section.length + offset <= upper_bound: + last_section.length += offset + last_section.add_component(section) + else: + result_sections.append( + McObject(name="", address=section.address, ext=section.ext, length=section.length, components=[section]) + ) + else: + last_section.length += offset + last_section.add_component(section) + else: + # Create a new section. + result_sections.append( + McObject(name="", address=section.address, ext=section.ext, length=section.length, components=[section]) + ) + last_section = result_sections[-1] + last_ext = last_section.ext + return result_sections diff --git a/pyxcp/daq_stim/optimize/binpacking.py b/pyxcp/daq_stim/optimize/binpacking.py new file mode 100644 index 0000000..9bed51c --- /dev/null +++ b/pyxcp/daq_stim/optimize/binpacking.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Bin-packing algorithms. +""" +from typing import List +from typing import Optional + +from pyxcp.cpp_ext import Bin + + +def first_fit_decreasing(items, bin_size: int, initial_bin_size: Optional[int] = None) -> List[Bin]: + """bin-packing with first-fit-decreasing algorithm. + + Parameters + ---------- + items: list + items that need to be stored/allocated. + + bin_size: int + + Returns + ------- + list + Resulting bins + """ + if initial_bin_size is None: + initial_bin_size = bin_size + # bin_size = max(bin_size, initial_bin_size) + bins = [Bin(size=initial_bin_size)] # Initial bin + for item in sorted(items, key=lambda x: x.length, reverse=True): + if item.length > bin_size: + raise ValueError(f"Item '{item}' is too large to fit in a {bin_size} byte sized bin.") + for bin in bins: + if bin.residual_capacity >= item.length: + bin.append(item) + bin.residual_capacity -= item.length + break + else: + new_bin = Bin(size=bin_size) + bins.append(new_bin) + new_bin.append(item) + new_bin.residual_capacity -= item.length + return bins diff --git a/pyxcp/examples/xcp_policy.py b/pyxcp/examples/xcp_policy.py index f1ac96e..85d1f04 100644 --- a/pyxcp/examples/xcp_policy.py +++ b/pyxcp/examples/xcp_policy.py @@ -1,18 +1,18 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -"""Demostrates how to use frame recording policies. +"""Demostrates how to use frame recording policies including recorder extension. """ from pprint import pprint from pyxcp.cmdline import ArgumentParser -from pyxcp.transport.base import FrameRecorderAcquisitionPolicy +from pyxcp.transport.base import FrameRecorderPolicy from pyxcp.transport.base import StdoutPolicy ap = ArgumentParser(description="pyXCP frame recording policy example.") LOG_FILE = "pyxcp" -policy = FrameRecorderAcquisitionPolicy(LOG_FILE) +policy = FrameRecorderPolicy(LOG_FILE) use_recorder = True # policy = StdoutPolicy() # You may also try this one. diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index 57475fe..2415251 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -9,6 +9,7 @@ import time import types from collections import namedtuple + from pyxcp.errormatrix import Action from pyxcp.errormatrix import ERROR_MATRIX from pyxcp.errormatrix import PreAction @@ -22,6 +23,7 @@ logger = logging.getLogger("pyxcp.errorhandler") + class SingletonBase(object): _lock = threading.Lock() @@ -144,7 +146,7 @@ class Repeater: def __init__(self, initial_value: int): self._counter = initial_value - #print("\tREPEATER ctor", hex(id(self))) + # print("\tREPEATER ctor", hex(id(self))) def repeat(self): """Check if repetition is required. @@ -153,7 +155,7 @@ def repeat(self): ------- bool """ - #print("\t\tCOUNTER:", hex(id(self)), self._counter) + # print("\t\tCOUNTER:", hex(id(self)), self._counter) if self._counter == Repeater.INFINITE: return True elif self._counter > 0: @@ -198,12 +200,12 @@ def __eq__(self, other): @property def repeater(self): - #print("\tGet repeater", hex(id(self._repeater)), self._repeater is None) + # print("\tGet repeater", hex(id(self._repeater)), self._repeater is None) return self._repeater @repeater.setter def repeater(self, value): - #print("\tSet repeater", hex(id(value))) + # print("\tSet repeater", hex(id(value))) self._repeater = value def execute(self): @@ -334,12 +336,11 @@ def __call__(self, inst, func, arguments): self.arguments = arguments handler = Handler(inst, func, arguments) self.handlerStack.push(handler) - #print("\tENTER handler:", hex(id(handler))) + # print("\tENTER handler:", hex(id(handler))) try: while True: try: handler = self.handlerStack.tos() - #print("\t\tEXEC", hex(id(handler))) res = handler.execute() except XcpResponseError as e: self.logger.error(f"XcpResponseError [{str(e)}]") @@ -351,12 +352,15 @@ def __call__(self, inst, func, arguments): raise UnrecoverableError(f"Don't know how to handle exception '{repr(e)}'") from e else: self.error_code = None - # print("\t\t\t*** SUCCESS ***") self.handlerStack.pop() if self.handlerStack.empty(): - # print("OK, all handlers passed: '{}'.".format(res)) return res + if self.error_code == XcpError.ERR_CMD_SYNCH: + # Don't care about SYNCH for now... + self.inst.logger.info("SYNCH received.") + continue + if self.error_code is not None: preActions, actions, repeater = handler.actions(*getActions(inst.service, self.error_code)) if handler.repeater is None: @@ -368,7 +372,9 @@ def __call__(self, inst, func, arguments): if handler.repeater.repeat(): continue else: - raise UnrecoverableError(f"Max. repetition count reached while trying to execute service '{handler.func.__name__}'.") + raise UnrecoverableError( + f"Max. repetition count reached while trying to execute service '{handler.func.__name__}'." + ) finally: # cleanup of class variables self.previous_error_code = None diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 3ffbf4e..d7061e1 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -1785,7 +1785,7 @@ def cond_unlock(self, resources=None): while remaining > 0: result = self.getSeed(types.XcpGetSeedMode.REMAINING, resource_value) seed.extend(list(result.seed)) - remaining = result.length + remaining -= result.length result, key = getKey( self.logger, self.seedNKeyDLL, @@ -1795,13 +1795,13 @@ def cond_unlock(self, resources=None): ) if result == SeedNKeyResult.ACK: key = list(key) - total_length = len(key) - offset = 0 - while offset < total_length: - data = key[offset : offset + MAX_PAYLOAD] - key_length = len(data) - offset += key_length - self.unlock(key_length, data) + remaining = len(key) + while key: + data = key[:MAX_PAYLOAD] + key_len = len(data) + self.unlock(remaining, data) + key = key[MAX_PAYLOAD:] + remaining -= key_len else: raise SeedNKeyError("SeedAndKey DLL returned: {}".format(SeedNKeyResult(result).name)) diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 81473ba..2766d68 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -15,7 +15,11 @@ else: HAS_PANDAS = True -import rekorder as rec +import pyxcp.recorder.rekorder as rec + + +UnfoldingParameters = rec._UnfoldingParameters +XcpLogFileUnfolder = rec._XcpLogFileUnfolder @dataclass diff --git a/pyxcp/recorder/reader.hpp b/pyxcp/recorder/reader.hpp new file mode 100644 index 0000000..ba6efd8 --- /dev/null +++ b/pyxcp/recorder/reader.hpp @@ -0,0 +1,121 @@ + +#ifndef RECORDER_READER_HPP +#define RECORDER_READER_HPP + +class XcpLogFileReader { + public: + + explicit XcpLogFileReader(const std::string &file_name) { + if (!file_name.ends_with(detail::FILE_EXTENSION)) { + m_file_name = file_name + detail::FILE_EXTENSION; + } else { + m_file_name = file_name; + } + + m_mmap = new mio::mmap_source(m_file_name); + blob_t magic[detail::MAGIC_SIZE + 1]; + + read_bytes(0UL, detail::MAGIC_SIZE, magic); + if (memcmp(detail::MAGIC.c_str(), magic, detail::MAGIC_SIZE) != 0) { + throw std::runtime_error("Invalid file magic."); + } + m_offset = detail::MAGIC_SIZE; + + read_bytes(m_offset, detail::FILE_HEADER_SIZE, reinterpret_cast(&m_header)); + // printf("Sizes: %u %u %.3f\n", m_header.size_uncompressed, + // m_header.size_compressed, + // float(m_header.size_uncompressed) / float(m_header.size_compressed)); + if (m_header.hdr_size != detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE) { + throw std::runtime_error("File header size does not match."); + } + if (detail::VERSION != m_header.version) { + throw std::runtime_error("File version mismatch."); + } + + if (m_header.num_containers < 1) { + throw std::runtime_error("At least one container required."); + } + + m_offset += detail::FILE_HEADER_SIZE; + } + + [[nodiscard]] FileHeaderType get_header() const noexcept { + return m_header; + } + + [[nodiscard]] auto get_header_as_tuple() const noexcept -> HeaderTuple { + auto hdr = get_header(); + + return std::make_tuple( + hdr.num_containers, hdr.record_count, hdr.size_uncompressed, hdr.size_compressed, + (double)((std::uint64_t)(((double)hdr.size_uncompressed / (double)hdr.size_compressed * 100.0) + 0.5)) / 100.0 + ); + } + + void reset() noexcept { + m_current_container = 0; + m_offset = file_header_size(); + } + + std::optional next_block() { + auto container = ContainerHeaderType{}; + auto frame = frame_header_t{}; + std::uint32_t boffs = 0; + auto result = FrameVector{}; + payload_t payload; + + if (m_current_container >= m_header.num_containers) { + return std::nullopt; + } + read_bytes(m_offset, detail::CONTAINER_SIZE, reinterpret_cast(&container)); + __ALIGN auto buffer = new blob_t[container.size_uncompressed]; + m_offset += detail::CONTAINER_SIZE; + result.reserve(container.record_count); + const int uc_size = ::LZ4_decompress_safe( + reinterpret_cast(ptr(m_offset)), reinterpret_cast(buffer), container.size_compressed, + container.size_uncompressed + ); + if (uc_size < 0) { + throw std::runtime_error("LZ4 decompression failed."); + } + boffs = 0; + for (std::uint32_t idx = 0; idx < container.record_count; ++idx) { + _fcopy(reinterpret_cast(&frame), reinterpret_cast(&(buffer[boffs])), sizeof(frame_header_t)); + boffs += sizeof(frame_header_t); + result.emplace_back( + frame.category, frame.counter, frame.timestamp, frame.length, create_payload(frame.length, &buffer[boffs]) + ); + boffs += frame.length; + } + m_offset += container.size_compressed; + m_current_container += 1; + delete[] buffer; + + return std::optional{ result }; + } + + ~XcpLogFileReader() noexcept { + delete m_mmap; + } + + protected: + + [[nodiscard]] blob_t const *ptr(std::uint32_t pos = 0) const { + return reinterpret_cast(m_mmap->data() + pos); + } + + void read_bytes(std::uint32_t pos, std::uint32_t count, blob_t *buf) const { + auto addr = reinterpret_cast(ptr(pos)); + _fcopy(reinterpret_cast(buf), addr, count); + } + + private: + + std::string m_file_name; + std::uint32_t m_offset{ 0 }; + std::uint32_t m_current_container{ 0 }; + mio::mmap_source *m_mmap{ nullptr }; + FileHeaderType m_header; +}; + +#endif // RECORDER_READER_HPP diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 662eb3b..15ba649 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -1,84 +1,76 @@ #if !defined(__REKORDER_HPP) -#define __REKORDER_HPP - -#if !defined(STANDALONE_REKORDER) - #define STANDALONE_REKORDER 0 -#endif /* STANDALONE_REKORDER */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - - -#if defined(_WIN32) - #include - #include - - #include -#endif /* _WIN32 */ - - -#include "lz4.h" -#include "mio.hpp" - -#if STANDALONE_REKORDER == 0 - #include - #include - #include - - namespace py = pybind11; - using namespace pybind11::literals; -#endif /* STANDALONE_REKORDER */ - -#if !defined(__BIGGEST_ALIGNMENT__) - #define __BIGGEST_ALIGNMENT__ (8) -#endif - - -#define __ALIGNMENT_REQUIREMENT __BIGGEST_ALIGNMENT__ -#define __ALIGN alignas(__ALIGNMENT_REQUIREMENT) + #define __REKORDER_HPP + + #if !defined(STANDALONE_REKORDER) + #define STANDALONE_REKORDER 0 + #endif /* STANDALONE_REKORDER */ + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #if defined(_WIN32) + #include + #include + #include + #endif /* _WIN32 */ + + #include "lz4.h" + #include "mio.hpp" + + #if STANDALONE_REKORDER == 0 + #include + #include + #include + +namespace py = pybind11; +using namespace pybind11::literals; + #endif /* STANDALONE_REKORDER */ + + #if !defined(__BIGGEST_ALIGNMENT__) + #define __BIGGEST_ALIGNMENT__ (8) + #endif + #define __ALIGNMENT_REQUIREMENT __BIGGEST_ALIGNMENT__ + #define __ALIGN alignas(__ALIGNMENT_REQUIREMENT) -constexpr auto kilobytes(std::uint32_t value) -> std::uint32_t -{ +constexpr auto kilobytes(std::uint32_t value) -> std::uint32_t { return value * 1024; } -constexpr auto megabytes(std::uint32_t value) -> std::uint32_t -{ +constexpr auto megabytes(std::uint32_t value) -> std::uint32_t { return kilobytes(value) * 1024; } constexpr uint16_t XCP_PAYLOAD_MAX = 0xFFFF; -/* -byte-order is, where applicable little ending (LSB first). -*/ -#pragma pack(push) -#pragma pack(1) -struct FileHeaderType -{ + /* + byte-order is, where applicable little ending (LSB first). + */ + #pragma pack(push) + #pragma pack(1) + +struct FileHeaderType { uint16_t hdr_size; uint16_t version; uint16_t options; @@ -92,8 +84,7 @@ using HeaderTuple = std::tuple; -#else - using payload_t = py::array_t; -#endif /* STANDALONE_REKORDER */ - - -struct frame_header_t -{ - uint8_t category {0}; - uint16_t counter {0}; - double timestamp {0.0}; - uint16_t length {0}; + #if STANDALONE_REKORDER == 1 +using payload_t = std::shared_ptr; + #else +using payload_t = py::array_t; + #endif /* STANDALONE_REKORDER */ + +struct frame_header_t { + uint8_t category{ 0 }; + uint16_t counter{ 0 }; + double timestamp{ 0.0 }; + uint16_t length{ 0 }; }; -#pragma pack(pop) -using FrameTuple = std::tuple; -using FrameVector = std::vector; -using FrameTupleWriter = std::tuple; + #pragma pack(pop) + +using FrameTuple = std::tuple; +using FrameVector = std::vector; +using FrameTupleWriter = std::tuple; enum class FrameCategory : std::uint8_t { META, @@ -132,16 +122,14 @@ enum class FrameCategory : std::uint8_t { STIM, }; -namespace detail -{ - const std::string FILE_EXTENSION(".xmraw"); - const std::string MAGIC{"ASAMINT::XCP_RAW"}; - constexpr auto MAGIC_SIZE = 16; - constexpr auto VERSION = 0x0100; - constexpr auto FILE_HEADER_SIZE = sizeof(FileHeaderType); - constexpr auto CONTAINER_SIZE = sizeof(ContainerHeaderType); -} - +namespace detail { +const std::string FILE_EXTENSION(".xmraw"); +const std::string MAGIC{ "ASAMINT::XCP_RAW" }; +constexpr auto MAGIC_SIZE = 16; +constexpr auto VERSION = 0x0100; +constexpr auto FILE_HEADER_SIZE = sizeof(FileHeaderType); +constexpr auto CONTAINER_SIZE = sizeof(ContainerHeaderType); +} // namespace detail constexpr auto file_header_size() -> std::uint32_t { return (detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE); @@ -149,56 +137,53 @@ constexpr auto file_header_size() -> std::uint32_t { using rounding_func_t = std::function; -inline rounding_func_t create_rounding_func(std::uint32_t multiple) { +inline rounding_func_t create_rounding_func(std::uint32_t multiple) { return [multiple](std::uint32_t value) { - return (value + (multiple - 1)) & ~(multiple -1 ); + return (value + (multiple - 1)) & ~(multiple - 1); }; } const auto round_to_alignment = create_rounding_func(__ALIGNMENT_REQUIREMENT); - -inline void _fcopy(char * dest, char const * src, std::uint32_t n) noexcept -{ +inline void _fcopy(char* dest, char const * src, std::uint32_t n) noexcept { for (std::uint32_t i = 0; i < n; ++i) { dest[i] = src[i]; } } -#if STANDALONE_REKORDER == 1 - inline blob_t * get_payload_ptr(const payload_t& payload) noexcept { - return payload.get(); - } + #if STANDALONE_REKORDER == 1 +inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { + return payload.get(); +} - inline payload_t create_payload(std::uint32_t size, blob_t const * data) noexcept { - auto pl = std::make_shared(size); - _fcopy(reinterpret_cast(pl.get()), reinterpret_cast(data), size); - return pl; - } -#else - inline payload_t create_payload(std::uint32_t size, blob_t const * data) { - return py::array_t(size, data); - } +inline payload_t create_payload(std::uint32_t size, blob_t const * data) noexcept { + auto pl = std::make_shared(size); + _fcopy(reinterpret_cast(pl.get()), reinterpret_cast(data), size); + return pl; +} + #else +inline payload_t create_payload(std::uint32_t size, blob_t const * data) { + return py::array_t(size, data); +} - inline blob_t * get_payload_ptr(const payload_t& payload) noexcept { - py::buffer_info buf = payload.request(); +inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { + py::buffer_info buf = payload.request(); - return static_cast(buf.ptr); - } -#endif /* STANDALONE_REKORDER */ + return static_cast(buf.ptr); +} + #endif /* STANDALONE_REKORDER */ inline void hexdump(blob_t const * buf, std::uint16_t sz) { - for (std::uint16_t idx = 0; idx < sz; ++idx) - { + for (std::uint16_t idx = 0; idx < sz; ++idx) { printf("%02X ", buf[idx]); } printf("\n\r"); } - -template +template class TsQueue { -public: + public: + TsQueue() = default; TsQueue(const TsQueue& other) noexcept { @@ -214,7 +199,7 @@ class TsQueue { std::shared_ptr get() noexcept { std::unique_lock lock(m_mtx); - m_cond.wait(lock, [this]{return !m_queue.empty();}); + m_cond.wait(lock, [this] { return !m_queue.empty(); }); std::shared_ptr result(std::make_shared(m_queue.front())); m_queue.pop(); return result; @@ -225,15 +210,15 @@ class TsQueue { return m_queue.empty(); } -private: - mutable std::mutex m_mtx; - std::queue m_queue; + private: + + mutable std::mutex m_mtx; + std::queue m_queue; std::condition_variable m_cond; }; - class Event { -public: + public: Event(const Event& other) noexcept { std::scoped_lock lock(other.m_mtx); @@ -241,7 +226,7 @@ class Event { } ~Event() = default; - Event() = default; + Event() = default; void signal() noexcept { std::scoped_lock lock(m_mtx); @@ -251,7 +236,7 @@ class Event { void wait() noexcept { std::unique_lock lock(m_mtx); - m_cond.wait(lock, [this]{return m_flag;}); + m_cond.wait(lock, [this] { return m_flag; }); m_flag = false; } @@ -260,26 +245,25 @@ class Event { return m_flag; } -private: - mutable std::mutex m_mtx {}; - bool m_flag {false}; - std::condition_variable m_cond {}; -}; + private: + mutable std::mutex m_mtx{}; + bool m_flag{ false }; + std::condition_variable m_cond{}; +}; /* * * Super simplicistic block memory manager. * */ -template +template class BlockMemory { - -public: + public: using mem_block_t = std::array; - explicit BlockMemory() noexcept : m_memory{nullptr}, m_allocation_count{0} { + explicit BlockMemory() noexcept : m_memory{ nullptr }, m_allocation_count{ 0 } { m_memory = new T[_IS * _NB]; } @@ -288,15 +272,16 @@ class BlockMemory { delete[] m_memory; } } + BlockMemory(const BlockMemory&) = delete; - T * acquire() noexcept { + T* acquire() noexcept { const std::scoped_lock lock(m_mtx); if (m_allocation_count >= _NB) { return nullptr; } - T * ptr = reinterpret_cast(m_memory + (m_allocation_count * _IS)); + T* ptr = reinterpret_cast(m_memory + (m_allocation_count * _IS)); m_allocation_count++; return ptr; } @@ -309,353 +294,15 @@ class BlockMemory { m_allocation_count--; } -private: + private: - T * m_memory; + T* m_memory; std::uint32_t m_allocation_count; - std::mutex m_mtx; - + std::mutex m_mtx; }; + #include "reader.hpp" + #include "unfolder.hpp" + #include "writer.hpp" -/** - */ -class XcpLogFileWriter -{ -public: - explicit XcpLogFileWriter(const std::string& file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1) noexcept - { - if (!file_name.ends_with(detail::FILE_EXTENSION)) { - m_file_name = file_name + detail::FILE_EXTENSION; - } else { - m_file_name = file_name; - } - -#if defined(_WIN32) - m_fd = CreateFileA( - m_file_name.c_str(), - GENERIC_READ | GENERIC_WRITE, - 0, - (LPSECURITY_ATTRIBUTES)nullptr, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, - nullptr - ); -#else - m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666); -#endif - truncate(megabytes(prealloc)); - m_mmap = new mio::mmap_sink(m_fd); - m_chunk_size = megabytes(chunk_size); - m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)]; - m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; - - start_thread(); - } - - ~XcpLogFileWriter() noexcept { - finalize(); - #ifdef __APPLE__ - if (collector_thread.joinable()) { - collector_thread.join(); - } - #endif - } - - void finalize() { - if (!m_finalized) { - m_finalized = true; - stop_thread(); - if (m_container_record_count) { - compress_frames(); - } - write_header(detail::VERSION, 0x0000, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed); - m_mmap->unmap(); - truncate(m_offset); -#if defined(_WIN32) - CloseHandle(m_fd); -#else - close(m_fd); -#endif - delete m_mmap; - delete[] m_intermediate_storage; - } - } - - void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const * data) noexcept { - auto payload= new char[length]; - //auto payload = mem.acquire(); - - _fcopy(payload, data, length); - my_queue.put( - std::make_tuple(category, counter, timestamp, length, payload) - ); - } - -protected: - void truncate(off_t size) const noexcept - { -#if defined(_WIN32) - if (SetFilePointer(m_fd, size, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { - // TODO: Errorhandling. - } - if (SetEndOfFile(m_fd) == 0) { - // TODO: Errorhandling. - } -#else - ftruncate(m_fd, size); -#endif - } - - blob_t * ptr(std::uint32_t pos = 0) const noexcept - { - return (blob_t *)(m_mmap->data() + pos); - } - - template - void store_im(T const * data, std::uint32_t length) noexcept { - _fcopy(reinterpret_cast(m_intermediate_storage + m_intermediate_storage_offset), reinterpret_cast(data), length); - m_intermediate_storage_offset += length; - } - - void compress_frames() { - auto container = ContainerHeaderType{}; - //printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); - const int cp_size = ::LZ4_compress_default( - reinterpret_cast(m_intermediate_storage), reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), - m_intermediate_storage_offset, LZ4_COMPRESSBOUND(m_intermediate_storage_offset) - ); - if (cp_size < 0) { - throw std::runtime_error("LZ4 compression failed."); - } - //printf("comp: %d %d [%f]\n", m_intermediate_storage_offset, cp_size, double(m_intermediate_storage_offset) / double(cp_size)); - container.record_count = m_container_record_count; - container.size_compressed = cp_size; - container.size_uncompressed = m_container_size_uncompressed; - _fcopy(reinterpret_cast(ptr(m_offset)), reinterpret_cast(&container), detail::CONTAINER_SIZE); - m_offset += (detail::CONTAINER_SIZE + cp_size); - m_total_size_uncompressed += m_container_size_uncompressed; - m_total_size_compressed += cp_size; - m_record_count += m_container_record_count; - m_container_size_uncompressed = 0; - m_container_size_compressed = 0; - m_container_record_count = 0; - m_intermediate_storage_offset = 0; - m_num_containers += 1; - } - - void write_bytes(std::uint32_t pos, std::uint32_t count, char const * buf) const noexcept - { - auto addr = reinterpret_cast(ptr(pos)); - - _fcopy(addr, buf, count); - } - - void write_header(uint16_t version, uint16_t options, uint32_t num_containers, - uint32_t record_count, uint32_t size_compressed, uint32_t size_uncompressed) noexcept { - auto header = FileHeaderType{}; - write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); - header.hdr_size = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; - header.version = version; - header.options = options; - header.num_containers = num_containers; - header.record_count = record_count; - header.size_compressed = size_compressed; - header.size_uncompressed = size_uncompressed; - write_bytes(0x00000000UL + detail::MAGIC_SIZE, detail::FILE_HEADER_SIZE, reinterpret_cast(&header)); - } - - bool start_thread() noexcept { - if (collector_thread.joinable()) { - return false; - } - stop_collector_thread_flag = false; - #ifdef __APPLE__ - collector_thread = std::thread([this]() { - #else - collector_thread = std::jthread([this]() { - #endif - while (!stop_collector_thread_flag) { - auto item = my_queue.get(); - const auto content = item.get(); - if (stop_collector_thread_flag == true) - { - break; - } - const auto [category, counter, timestamp, length, payload] = *content; - const frame_header_t frame{ category, counter, timestamp, length }; - store_im(&frame, sizeof(frame)); - store_im(payload, length); - delete[] payload; - m_container_record_count += 1; - m_container_size_uncompressed += (sizeof(frame) + length); - if (m_container_size_uncompressed > m_chunk_size) { - compress_frames(); - } - } - }); - return true; - } - - bool stop_thread() noexcept { - if (!collector_thread.joinable()) { - return false; - } - stop_collector_thread_flag = true; - my_queue.put(FrameTupleWriter{}); // Put something into the queue, otherwise the thread will hang forever. - collector_thread.join(); - return true; - } - -private: - std::string m_file_name; - std::uint32_t m_offset{0}; - std::uint32_t m_chunk_size{0}; - std::uint32_t m_num_containers{0}; - std::uint32_t m_record_count{0UL}; - std::uint32_t m_container_record_count{0UL}; - std::uint32_t m_total_size_uncompressed{0UL}; - std::uint32_t m_total_size_compressed{0UL}; - std::uint32_t m_container_size_uncompressed{0UL}; - std::uint32_t m_container_size_compressed{0UL}; - __ALIGN blob_t * m_intermediate_storage{nullptr}; - std::uint32_t m_intermediate_storage_offset{0}; - mio::file_handle_type m_fd{INVALID_HANDLE_VALUE}; - mio::mmap_sink * m_mmap{nullptr}; - bool m_finalized{false}; - #ifdef __APPLE__ - std::thread collector_thread{}; - #else - std::jthread collector_thread{}; - #endif - std::mutex mtx; - TsQueue my_queue; - BlockMemory mem{}; - std::atomic_bool stop_collector_thread_flag{false}; -}; - - -/** - */ -class XcpLogFileReader -{ -public: - explicit XcpLogFileReader(const std::string& file_name) - { - if (!file_name.ends_with(detail::FILE_EXTENSION)) { - m_file_name = file_name + detail::FILE_EXTENSION; - } else { - m_file_name = file_name; - } - - m_mmap = new mio::mmap_source(m_file_name); - blob_t magic[detail::MAGIC_SIZE + 1]; - - read_bytes(0UL, detail::MAGIC_SIZE, magic); - if (memcmp(detail::MAGIC.c_str(), magic, detail::MAGIC_SIZE) != 0) { - throw std::runtime_error("Invalid file magic."); - } - m_offset = detail::MAGIC_SIZE; - - read_bytes(m_offset, detail::FILE_HEADER_SIZE, reinterpret_cast(&m_header)); - //printf("Sizes: %u %u %.3f\n", m_header.size_uncompressed, - // m_header.size_compressed, - // float(m_header.size_uncompressed) / float(m_header.size_compressed)); - if (m_header.hdr_size != detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE) - { - throw std::runtime_error("File header size does not match."); - } - if (detail::VERSION != m_header.version) - { - throw std::runtime_error("File version mismatch."); - } - - if (m_header.num_containers < 1) { - throw std::runtime_error("At least one container required."); - } - - m_offset += detail::FILE_HEADER_SIZE; - } - - [[nodiscard]] - FileHeaderType get_header() const noexcept { - return m_header; - } - - [[nodiscard]] - auto get_header_as_tuple() const noexcept -> HeaderTuple { - auto hdr = get_header(); - - return std::make_tuple( - hdr.num_containers, - hdr.record_count, - hdr.size_uncompressed, - hdr.size_compressed, - (double)((std::uint64_t)(((double)hdr.size_uncompressed / (double)hdr.size_compressed * 100.0) + 0.5)) / 100.0 - ); - } - - void reset() noexcept { - m_current_container = 0; - m_offset = file_header_size(); - } - - std::optional next_block() { - auto container = ContainerHeaderType{}; - auto frame = frame_header_t{}; - std::uint32_t boffs = 0; - auto result = FrameVector{}; - payload_t payload; - - if (m_current_container >= m_header.num_containers) { - return std::nullopt; - } - read_bytes(m_offset, detail::CONTAINER_SIZE, reinterpret_cast(&container)); - __ALIGN auto buffer = new blob_t[container.size_uncompressed]; - m_offset += detail::CONTAINER_SIZE; - result.reserve(container.record_count); - const int uc_size = ::LZ4_decompress_safe(reinterpret_cast(ptr(m_offset)), reinterpret_cast(buffer), container.size_compressed, container.size_uncompressed); - if (uc_size < 0) { - throw std::runtime_error("LZ4 decompression failed."); - } - boffs = 0; - for (std::uint32_t idx = 0; idx < container.record_count; ++idx) { - _fcopy(reinterpret_cast(&frame), reinterpret_cast(&(buffer[boffs])), sizeof(frame_header_t)); - boffs += sizeof(frame_header_t); - result.emplace_back(frame.category, frame.counter, frame.timestamp, frame.length, create_payload(frame.length, &buffer[boffs])); - boffs += frame.length; - } - m_offset += container.size_compressed; - m_current_container += 1; - delete[] buffer; - - return std::optional{result}; - } - - ~XcpLogFileReader() noexcept - { - delete m_mmap; - } - -protected: - [[nodiscard]] - blob_t const *ptr(std::uint32_t pos = 0) const - { - return reinterpret_cast(m_mmap->data() + pos); - } - - void read_bytes(std::uint32_t pos, std::uint32_t count, blob_t * buf) const - { - auto addr = reinterpret_cast(ptr(pos)); - _fcopy(reinterpret_cast(buf), addr, count); - } - -private: - std::string m_file_name; - std::uint32_t m_offset{0}; - std::uint32_t m_current_container{0}; - mio::mmap_source * m_mmap{nullptr}; - FileHeaderType m_header; -}; - -#endif // __REKORDER_HPP +#endif // __REKORDER_HPP diff --git a/pyxcp/recorder/setup.py b/pyxcp/recorder/setup.py index 8dbc8c5..99b6916 100644 --- a/pyxcp/recorder/setup.py +++ b/pyxcp/recorder/setup.py @@ -23,7 +23,7 @@ Pybind11Extension( EXT_NAMES[0], include_dirs=[INCLUDE_DIRS], - sources=["lz4.cpp", "wrap.cpp"], + sources=["lz4.c", "wrap.cpp"], define_macros=[("EXTENSION_NAME", EXT_NAMES[0])], cxx_std=20, # Extension will use C++20 generators/coroutines. ), diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp new file mode 100644 index 0000000..03cd442 --- /dev/null +++ b/pyxcp/recorder/unfolder.hpp @@ -0,0 +1,362 @@ + +#ifndef RECORDER_UNFOLDER_HPP +#define RECORDER_UNFOLDER_HPP + +#include +#include + +#include "daqlist.hpp" +#include "mcobject.hpp" + +// NOTE: C++23 has std::byteswap() +constexpr auto _bswap(std::uint64_t v) noexcept { + return ((v & UINT64_C(0x0000'0000'0000'00FF)) << 56) | ((v & UINT64_C(0x0000'0000'0000'FF00)) << 40) | + ((v & UINT64_C(0x0000'0000'00FF'0000)) << 24) | ((v & UINT64_C(0x0000'0000'FF00'0000)) << 8) | + ((v & UINT64_C(0x0000'00FF'0000'0000)) >> 8) | ((v & UINT64_C(0x0000'FF00'0000'0000)) >> 24) | + ((v & UINT64_C(0x00FF'0000'0000'0000)) >> 40) | ((v & UINT64_C(0xFF00'0000'0000'0000)) >> 56); +} + +constexpr auto _bswap(std::uint32_t v) noexcept { + return ((v & UINT32_C(0x0000'00FF)) << 24) | ((v & UINT32_C(0x0000'FF00)) << 8) | ((v & UINT32_C(0x00FF'0000)) >> 8) | + ((v & UINT32_C(0xFF00'0000)) >> 24); +} + +constexpr auto _bswap(std::uint16_t v) noexcept { + return ((v & UINT16_C(0x00FF)) << 8) | ((v & UINT16_C(0xFF00)) >> 8); +} + +using measurement_value_t = std::variant; +using measurement_value_vector_t = std::vector; + +template +auto get_value(blob_t const * buf, std::uint32_t offset) -> Ty { + return *reinterpret_cast(&buf[offset]); +} + +template +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> Ty { + return _bswap(get_value(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> float { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> double { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> float { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> double { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> std::int16_t { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::int16_t { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> std::int32_t { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::int32_t { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> std::int64_t { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::int64_t { + return static_cast(get_value_swapped(buf, offset)); +} + +/* +** Get primitive datatypes, consider byte-order. +*/ +struct Getter { + Getter() = default; + + explicit Getter(bool swap, std::uint8_t id_size, std::uint8_t ts_size) : m_id_size(id_size), m_ts_size(ts_size) { + int8 = get_value; + uint8 = get_value; + + if (swap) { + int16 = get_value_swapped; + int32 = get_value_swapped; + int64 = get_value_swapped; + uint16 = get_value_swapped; + uint32 = get_value_swapped; + uint64 = get_value_swapped; + float_ = get_value_swapped; + double_ = get_value_swapped; + } else { + int16 = get_value; + int32 = get_value; + int64 = get_value; + uint16 = get_value; + uint32 = get_value; + uint64 = get_value; + float_ = get_value; + double_ = get_value; + } + // ts_size=0; + std::cout << "TS-SIZE: " << static_cast(ts_size) << " : " << static_cast(m_id_size) << std::endl; + } + + std::uint32_t get_timestamp(blob_t const * buf) { + switch (m_ts_size) { + case 0: + return 0; + case 1: + return uint8(buf, m_id_size); + case 2: + return uint16(buf, m_id_size); + case 4: + return uint32(buf, m_id_size); + default: + throw std::runtime_error("Unsupported timestamp size: " + std::to_string(m_ts_size)); + } + } + + measurement_value_t reader(std::uint16_t tp, blob_t const * buf, std::uint16_t offset) const { + switch (tp) { + case 0: + return static_cast(uint8(buf, offset)); + case 1: + return int8(buf, offset); + case 2: + return static_cast(uint16(buf, offset)); + case 3: + return int16(buf, offset); + case 4: + return static_cast(uint32(buf, offset)); + case 5: + return int32(buf, offset); + case 6: + return uint64(buf, offset); + case 7: + return int64(buf, offset); + case 8: + return float_(buf, offset); + case 9: + return double_(buf, offset); + default: + throw std::runtime_error("Unsupported data type: " + std::to_string(tp)); + } + } + + void set_first_pids(const std::vector& daq_lists, const std::vector& first_pids) { + m_first_pids = first_pids; + + if (m_id_size == 1) { + // In case of 1-byte ID field (absolute ODT number) we need a mapping. + + std::uint16_t daq_list_num = 0; + for (const auto& daq_list : daq_lists) { + auto first_pid = m_first_pids[daq_list_num]; + + for (std::uint16_t idx = first_pid; idx < daq_list.get_odt_count() + first_pid; ++idx) { + m_odt_to_daq_map[idx] = { daq_list_num, (idx - first_pid) }; + } + daq_list_num++; + } + } + } + + std::tuple get_id(blob_t const * buf) { + std::uint16_t odt_num = 0; + + switch (m_id_size) { + case 1: + odt_num = uint8(buf, 0); // Get 1-byte ODT number... + return m_odt_to_daq_map[odt_num]; // ...and return mapped values. + case 2: + return { uint8(buf, 1), uint8(buf, 0) }; + case 3: + return { uint16(buf, 1), uint8(buf, 0) }; + case 4: + return { uint16(buf, 2), uint8(buf, 0) }; + default: + throw std::runtime_error("Unsupported ID size: " + std::to_string(m_id_size)); + } + } + + std::uint8_t m_id_size; + std::uint8_t m_ts_size; + std::function int8; + std::function uint8; + std::function int16; + std::function int32; + std::function int64; + std::function uint16; + std::function uint32; + std::function uint64; + std::function float_; + std::function double_; + std::vector m_first_pids; + std::map> m_odt_to_daq_map; +}; + +struct UnfoldingParameters { + UnfoldingParameters() = delete; + + explicit UnfoldingParameters( + std::uint8_t byte_order, std::uint8_t id_field_size, double scale_factor, bool enable_timestamps, std::uint8_t ts_size, + const std::vector& daq_lists + ) : + m_byte_order(byte_order), + m_id_field_size(id_field_size), + m_scale_factor(scale_factor), + m_enable_timestamps(enable_timestamps), + m_ts_size(ts_size), + m_daq_lists(daq_lists) { + } + + std::uint8_t m_byte_order; // INTEL(LITTLE)=0, MOTOROLA(BIG)=1 + std::uint8_t m_id_field_size; + double m_scale_factor; + std::uint8_t m_ts_size; + bool m_enable_timestamps; + std::vector m_daq_lists; + // timestampSupported + // prescalerSupported + // timestampMode.fixed + // minDAQ +}; + +class MeasurementBuffer { + public: + + MeasurementBuffer(std::size_t num_elements) : m_buffer(num_elements) { + } + + private: + + std::vector m_buffer; + std::uint16_t m_current_odt = 0; +}; + +class XcpLogFileUnfolder { + public: + + explicit XcpLogFileUnfolder(const std::string& file_name, const UnfoldingParameters& params) : + m_reader(file_name), m_byte_order(std::endian::native), m_params(params) { + std::endian target_byte_order; + bool requires_swap; + + // std::vector<; + + if (m_params.m_byte_order == 0) { + target_byte_order = std::endian::little; + } else if (m_params.m_byte_order == 1) { + target_byte_order = std::endian::big; + } + if (target_byte_order != m_byte_order) { + requires_swap = true; + } else { + requires_swap = false; + } + + auto ts_size = m_params.m_ts_size; + std::cout << "ENA-TS: " << m_params.m_enable_timestamps << std::endl; + if (params.m_enable_timestamps) { + ts_size = 0; + } + + std::cout << "ID-size: " << static_cast(params.m_id_field_size) << std::endl; + m_getter = Getter(requires_swap, params.m_id_field_size, ts_size); + } + + void start(const std::vector& first_pids) { + m_getter.set_first_pids(m_params.m_daq_lists, first_pids); + } + + std::optional next_block() { + std::uint16_t offset = 0; + + while (true) { + const auto& block = m_reader.next_block(); + + if (!block) { + break; + } + + for (const auto& frame : block.value()) { + const auto& [category, counter, timestamp, frame_length, payload] = frame; + auto payload_data = payload.data(); + + if (category != static_cast(FrameCategory::DAQ)) { + continue; + } + //////////////////////////////// + offset = 0; + auto [daq_num, odt_num] = m_getter.get_id(payload_data); + offset += m_params.m_id_field_size; + std::cout << "CTR: " << counter << " ID: " << daq_num << ": " << odt_num << std::endl; + + if (odt_num == 0) { + auto ts = m_getter.get_timestamp(payload_data); + // auto ts = 0; + std::cout << "\tSTART DAQ-LIST: " << ts << std::endl; + if (m_params.m_enable_timestamps) { + offset += m_params.m_ts_size; + } + } + for (const auto& param : m_params.m_daq_lists[daq_num].get_flatten_odts()[odt_num]) { + const auto& [name, address, ext, size, type_index] = param; + + std::cout << "\t" << name << " " << offset << " : " << size << " ==> "; + + auto length = payload.size(); + if (offset >= length) { + throw std::runtime_error( + "Offset is out of range! " + std::to_string(offset) + " >= " + std::to_string(length) + ); + } + auto data = m_getter.reader(type_index, payload_data, offset); + + if (std::holds_alternative(data)) { + std::cout << std::get(data) << " " << std::endl; + } else if (std::holds_alternative(data)) { + std::cout << std::get(data) << "(+/-) " << std::endl; + } else if (std::holds_alternative(data)) { + std::cout << std::get(data) << " (double) " << std::endl; + } + offset += size; + } + //////////////////////////////// + } + return std::nullopt; + } + return std::nullopt; + } + + private: + + XcpLogFileReader m_reader; + std::endian m_byte_order; + UnfoldingParameters m_params; + Getter m_getter; + std::map m_first_pids; + // std::vector> m_measurement_buffers; +}; + +#endif // RECORDER_UNFOLDER_HPP diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 80999ea..44714d4 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -1,27 +1,32 @@ -#include - -#include #include +#include #include +#include + #include "rekorder.hpp" namespace py = pybind11; using namespace pybind11::literals; - PYBIND11_MODULE(rekorder, m) { m.doc() = "XCP raw frame recorder."; py::class_(m, "_PyXcpLogFileReader") .def(py::init()) .def("next_block", &XcpLogFileReader::next_block) .def("reset", &XcpLogFileReader::reset) - .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple) - ; + .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple); py::class_(m, "_PyXcpLogFileWriter") - .def(py::init()) - .def("finalize", &XcpLogFileWriter::finalize) - .def("add_frame", &XcpLogFileWriter::add_frame) - ; + .def(py::init()) + .def("finalize", &XcpLogFileWriter::finalize) + .def("add_frame", &XcpLogFileWriter::add_frame); + + py::class_(m, "_UnfoldingParameters") + .def(py::init&>()); + + py::class_(m, "_XcpLogFileUnfolder") + .def(py::init()) + .def("next_block", &XcpLogFileUnfolder::next_block) + .def("start", &XcpLogFileUnfolder::start); } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp new file mode 100644 index 0000000..d5080f5 --- /dev/null +++ b/pyxcp/recorder/writer.hpp @@ -0,0 +1,218 @@ + +#ifndef RECORDER_WRITER_HPP +#define RECORDER_WRITER_HPP + +class XcpLogFileWriter { + public: + + explicit XcpLogFileWriter(const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1) noexcept { + if (!file_name.ends_with(detail::FILE_EXTENSION)) { + m_file_name = file_name + detail::FILE_EXTENSION; + } else { + m_file_name = file_name; + } + +#if defined(_WIN32) + m_fd = CreateFileA( + m_file_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr + ); +#else + m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666); +#endif + truncate(megabytes(prealloc)); + m_mmap = new mio::mmap_sink(m_fd); + m_chunk_size = megabytes(chunk_size); + m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)]; + m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; + + start_thread(); + } + + ~XcpLogFileWriter() noexcept { + finalize(); +#ifdef __APPLE__ + if (collector_thread.joinable()) { + collector_thread.join(); + } +#endif + } + + void finalize() { + if (!m_finalized) { + m_finalized = true; + stop_thread(); + if (m_container_record_count) { + compress_frames(); + } + write_header( + detail::VERSION, 0x0000, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed + ); + m_mmap->unmap(); + truncate(m_offset); +#if defined(_WIN32) + CloseHandle(m_fd); +#else + close(m_fd); +#endif + delete m_mmap; + delete[] m_intermediate_storage; + } + } + + void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const *data) noexcept { + auto payload = new char[length]; + // auto payload = mem.acquire(); + + _fcopy(payload, data, length); + my_queue.put(std::make_tuple(category, counter, timestamp, length, payload)); + } + + protected: + + void truncate(off_t size) const noexcept { +#if defined(_WIN32) + if (SetFilePointer(m_fd, size, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + // TODO: Errorhandling. + } + if (SetEndOfFile(m_fd) == 0) { + // TODO: Errorhandling. + } +#else + ftruncate(m_fd, size); +#endif + } + + blob_t *ptr(std::uint32_t pos = 0) const noexcept { + return (blob_t *)(m_mmap->data() + pos); + } + + template + void store_im(T const *data, std::uint32_t length) noexcept { + _fcopy( + reinterpret_cast(m_intermediate_storage + m_intermediate_storage_offset), reinterpret_cast(data), + length + ); + m_intermediate_storage_offset += length; + } + + void compress_frames() { + auto container = ContainerHeaderType{}; + // printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); + const int cp_size = ::LZ4_compress_default( + reinterpret_cast(m_intermediate_storage), + reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), m_intermediate_storage_offset, + LZ4_COMPRESSBOUND(m_intermediate_storage_offset) + ); + if (cp_size < 0) { + throw std::runtime_error("LZ4 compression failed."); + } + // printf("comp: %d %d [%f]\n", m_intermediate_storage_offset, cp_size, double(m_intermediate_storage_offset) / + // double(cp_size)); + container.record_count = m_container_record_count; + container.size_compressed = cp_size; + container.size_uncompressed = m_container_size_uncompressed; + _fcopy(reinterpret_cast(ptr(m_offset)), reinterpret_cast(&container), detail::CONTAINER_SIZE); + m_offset += (detail::CONTAINER_SIZE + cp_size); + m_total_size_uncompressed += m_container_size_uncompressed; + m_total_size_compressed += cp_size; + m_record_count += m_container_record_count; + m_container_size_uncompressed = 0; + m_container_size_compressed = 0; + m_container_record_count = 0; + m_intermediate_storage_offset = 0; + m_num_containers += 1; + } + + void write_bytes(std::uint32_t pos, std::uint32_t count, char const *buf) const noexcept { + auto addr = reinterpret_cast(ptr(pos)); + + _fcopy(addr, buf, count); + } + + void write_header( + uint16_t version, uint16_t options, uint32_t num_containers, uint32_t record_count, uint32_t size_compressed, + uint32_t size_uncompressed + ) noexcept { + auto header = FileHeaderType{}; + write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); + header.hdr_size = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; + header.version = version; + header.options = options; + header.num_containers = num_containers; + header.record_count = record_count; + header.size_compressed = size_compressed; + header.size_uncompressed = size_uncompressed; + write_bytes(0x00000000UL + detail::MAGIC_SIZE, detail::FILE_HEADER_SIZE, reinterpret_cast(&header)); + } + + bool start_thread() noexcept { + if (collector_thread.joinable()) { + return false; + } + stop_collector_thread_flag = false; +#ifdef __APPLE__ + collector_thread = std::thread([this]() { +#else + collector_thread = std::jthread([this]() { +#endif + while (!stop_collector_thread_flag) { + auto item = my_queue.get(); + const auto content = item.get(); + if (stop_collector_thread_flag == true) { + break; + } + const auto [category, counter, timestamp, length, payload] = *content; + const frame_header_t frame{ category, counter, timestamp, length }; + store_im(&frame, sizeof(frame)); + store_im(payload, length); + delete[] payload; + m_container_record_count += 1; + m_container_size_uncompressed += (sizeof(frame) + length); + if (m_container_size_uncompressed > m_chunk_size) { + compress_frames(); + } + } + }); + return true; + } + + bool stop_thread() noexcept { + if (!collector_thread.joinable()) { + return false; + } + stop_collector_thread_flag = true; + my_queue.put(FrameTupleWriter{}); // Put something into the queue, otherwise the thread will hang forever. + collector_thread.join(); + return true; + } + + private: + + std::string m_file_name; + std::uint32_t m_offset{ 0 }; + std::uint32_t m_chunk_size{ 0 }; + std::uint32_t m_num_containers{ 0 }; + std::uint32_t m_record_count{ 0UL }; + std::uint32_t m_container_record_count{ 0UL }; + std::uint32_t m_total_size_uncompressed{ 0UL }; + std::uint32_t m_total_size_compressed{ 0UL }; + std::uint32_t m_container_size_uncompressed{ 0UL }; + std::uint32_t m_container_size_compressed{ 0UL }; + __ALIGN blob_t *m_intermediate_storage{ nullptr }; + std::uint32_t m_intermediate_storage_offset{ 0 }; + mio::file_handle_type m_fd{ INVALID_HANDLE_VALUE }; + mio::mmap_sink *m_mmap{ nullptr }; + bool m_finalized{ false }; +#ifdef __APPLE__ + std::thread collector_thread{}; +#else + std::jthread collector_thread{}; +#endif + std::mutex mtx; + TsQueue my_queue; + BlockMemory mem{}; + std::atomic_bool stop_collector_thread_flag{ false }; +}; + +#endif // RECORDER_WRITER_HPP diff --git a/pyxcp/tests/test_binpacking.py b/pyxcp/tests/test_binpacking.py new file mode 100644 index 0000000..abd669a --- /dev/null +++ b/pyxcp/tests/test_binpacking.py @@ -0,0 +1,186 @@ +import pytest + +from pyxcp.daq_stim.optimize import make_continuous_blocks +from pyxcp.daq_stim.optimize import McObject +from pyxcp.daq_stim.optimize.binpacking import Bin +from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing + + +@pytest.fixture +def blocks(): + return [ + McObject(name="", address=0x000E10BA, length=2), + McObject(name="", address=0x000E10BE, length=2), + McObject(name="", address=0x000E41F4, length=4), + McObject(name="", address=0x000E51FC, length=4), + McObject(name="", address=0x00125288, length=4), + McObject(name="", address=0x00125294, length=4), + McObject(name="", address=0x001252A1, length=1), + McObject(name="", address=0x001252A4, length=4), + McObject(name="", address=0x00125438, length=3), + McObject(name="", address=0x0012543C, length=1), + ] + + +def test_pack_to_single_bin(blocks): + BIN_SIZE = 253 + bins = first_fit_decreasing(items=blocks, bin_size=BIN_SIZE) + + assert len(bins) == 1 + bin0 = bins[0] + assert bin0.residual_capacity == BIN_SIZE - 29 + assert bin0.entries == [ + McObject(name="", address=0x000E41F4, length=4), + McObject(name="", address=0x000E51FC, length=4), + McObject(name="", address=0x00125288, length=4), + McObject(name="", address=0x00125294, length=4), + McObject(name="", address=0x001252A4, length=4), + McObject(name="", address=0x00125438, length=3), + McObject(name="", address=0x000E10BA, length=2), + McObject(name="", address=0x000E10BE, length=2), + McObject(name="", address=0x001252A1, length=1), + McObject(name="", address=0x0012543C, length=1), + ] + + +def test_pack_empty_block_set(): + BIN_SIZE = 253 + bins = first_fit_decreasing(items=[], bin_size=BIN_SIZE) + assert bins == [Bin(size=BIN_SIZE)] + + +def test_pack_to_multiple_bins1(blocks): + BIN_SIZE = 6 + bins = first_fit_decreasing(items=blocks, bin_size=BIN_SIZE) + assert len(bins) == 6 + bin0, bin1, bin2, bin3, bin4, bin5 = bins + assert bin0.residual_capacity == 0 + assert bin0.entries == [ + McObject(name="", address=0x000E41F4, length=4), + McObject(name="", address=0x000E10BA, length=2), + ] + assert bin1.residual_capacity == 0 + assert bin1.entries == [ + McObject(name="", address=0x000E51FC, length=4), + McObject(name="", address=0x000E10BE, length=2), + ] + assert bin2.residual_capacity == 0 + assert bin2.entries == [ + McObject(name="", address=0x00125288, length=4), + McObject(name="", address=0x001252A1, length=1), + McObject(name="", address=0x0012543C, length=1), + ] + assert bin3.residual_capacity == 2 + assert bin3.entries == [McObject(name="", address=0x00125294, length=4)] + assert bin4.residual_capacity == 2 + assert bin4.entries == [McObject(name="", address=0x001252A4, length=4)] + assert bin5.residual_capacity == 3 + assert bin5.entries == [McObject(name="", address=0x00125438, length=3)] + + +def test_binpacking_raises(blocks): + BIN_SIZE = 7 + with pytest.raises(ValueError): + first_fit_decreasing(items=[McObject(name="", address=0x1000, length=32)], bin_size=BIN_SIZE) + + +def test_binpacking_works(blocks): + BIN_SIZE = 7 + first_fit_decreasing(items=[McObject(name="", address=0x1000, length=7)], bin_size=BIN_SIZE) + + +def test_make_continuous_blocks1(): + BLOCKS = [ + McObject(name="", address=0x000E0002, length=2), + McObject(name="", address=0x000E0008, ext=23, length=4), + McObject(name="", address=0x000E0004, length=4), + McObject(name="", address=0x000E000C, ext=23, length=4), + McObject(name="", address=0x000E0000, length=2), + ] + bins = make_continuous_blocks(chunks=BLOCKS) + assert bins == [ + McObject( + name="", + address=917504, + ext=0, + length=8, + components=[ + McObject(name="", address=917504, ext=0, length=2, components=[]), + McObject(name="", address=917506, ext=0, length=2, components=[]), + McObject(name="", address=917508, ext=0, length=4, components=[]), + ], + ), + McObject( + name="", + address=917512, + ext=23, + length=8, + components=[ + McObject(name="", address=917512, ext=23, length=4, components=[]), + McObject(name="", address=917516, ext=23, length=4, components=[]), + ], + ), + ] + + +def test_make_continuous_blocks2(): + BLOCKS = [ + McObject(name="", address=0x000E0002, length=2), + McObject(name="", address=0x000E0008, length=4), + McObject(name="", address=0x000E0004, length=4), + McObject(name="", address=0x000E000C, length=4), + McObject(name="", address=0x000E0000, length=2), + ] + bins = make_continuous_blocks(chunks=BLOCKS) + assert bins == [ + McObject( + name="", + address=917504, + ext=0, + length=16, + components=[ + McObject(name="", address=917504, ext=0, length=2, components=[]), + McObject(name="", address=917506, ext=0, length=2, components=[]), + McObject(name="", address=917508, ext=0, length=4, components=[]), + McObject(name="", address=917512, ext=0, length=4, components=[]), + McObject(name="", address=917516, ext=0, length=4, components=[]), + ], + ) + ] + + +def test_make_continuous_blocks3(): + BLOCKS = [ + McObject(name="", address=0x000E0002, ext=0x01, length=2), + McObject(name="", address=0x000E0008, ext=0x03, length=4), + McObject(name="", address=0x000E0004, ext=0x02, length=4), + McObject(name="", address=0x000E000C, ext=0x04, length=4), + McObject(name="", address=0x000E0000, ext=0x00, length=2), + ] + bins = make_continuous_blocks(chunks=BLOCKS) + assert bins == [ + McObject( + name="", address=917504, ext=0, length=2, components=[McObject(name="", address=917504, ext=0, length=2, components=[])] + ), + McObject( + name="", address=917506, ext=1, length=2, components=[McObject(name="", address=917506, ext=1, length=2, components=[])] + ), + McObject( + name="", address=917508, ext=2, length=4, components=[McObject(name="", address=917508, ext=2, length=4, components=[])] + ), + McObject( + name="", address=917512, ext=3, length=4, components=[McObject(name="", address=917512, ext=3, length=4, components=[])] + ), + McObject( + name="", address=917516, ext=4, length=4, components=[McObject(name="", address=917516, ext=4, length=4, components=[])] + ), + ] + + +def test_mc_object_len_zero(): + with pytest.raises(ValueError): + McObject(name="", address=0, ext=0, length=0) + + +def test_mc_object_ok(): + McObject(name="", address=0, ext=0, length=1) diff --git a/pyxcp/tests/test_daq.py b/pyxcp/tests/test_daq.py new file mode 100644 index 0000000..faf1609 --- /dev/null +++ b/pyxcp/tests/test_daq.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from unittest.mock import Mock +from unittest.mock import patch + +from pyxcp.daq_stim import Daq +from pyxcp.daq_stim import DaqList + +# import pytest + + +DAQ_INFO = { + "channels": [ + { + "cycle": 0, + "maxDaqList": 1, + "name": "Key T", + "priority": 0, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": False}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 10, + "maxDaqList": 1, + "name": "10 ms", + "priority": 1, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 100, + "maxDaqList": 1, + "name": "100ms", + "priority": 2, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 1, + "maxDaqList": 1, + "name": "1ms", + "priority": 3, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 10, + "maxDaqList": 1, + "name": "FilterBypassDaq", + "priority": 4, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 10, + "maxDaqList": 1, + "name": "FilterBypassStim", + "priority": 5, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": False, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + ], + "processor": { + "keyByte": { + "addressExtension": "AE_DIFFERENT_WITHIN_ODT", + "identificationField": "IDF_REL_ODT_NUMBER_ABS_DAQ_LIST_NUMBER_BYTE", + "optimisationType": "OM_DEFAULT", + }, + "maxDaq": 0, + "minDaq": 0, + "properties": { + "bitStimSupported": False, + "configType": "DYNAMIC", + "overloadEvent": False, + "overloadMsb": True, + "pidOffSupported": False, + "prescalerSupported": True, + "resumeSupported": True, + "timestampSupported": True, + }, + }, + "resolution": { + "granularityOdtEntrySizeDaq": 1, + "granularityOdtEntrySizeStim": 1, + "maxOdtEntrySizeDaq": 218, + "maxOdtEntrySizeStim": 218, + "timestampMode": {"fixed": False, "size": "S4", "unit": "DAQ_TIMESTAMP_UNIT_10US"}, + "timestampTicks": 10, + }, +} + +SLAVE_INFO = { + "addressGranularity": 0, + "byteOrder": 0, + "interleavedMode": False, + "masterBlockMode": True, + "maxBs": 2, + "maxCto": 255, + "maxDto": 1500, + "maxWriteDaqMultipleElements": 31, + "minSt": 0, + "optionalCommMode": True, + "pgmProcessor": {}, + "protocolLayerVersion": 1, + "queueSize": 0, + "slaveBlockMode": True, + "supportsCalpag": True, + "supportsDaq": True, + "supportsPgm": True, + "supportsStim": True, + "transportLayerVersion": 1, + "xcpDriverVersionNumber": 25, +} + + +class AttrDict(dict): + def __getattr__(self, name): + return self[name] + + +class MockMaster: + def __init__(self): + self.slaveProperties = AttrDict( + { + "maxDto": 1500, + "supportsDaq": True, + } + ) + + def getDaqInfo(self): + return DAQ_INFO + + def freeDaq(self): + pass + + def allocDaq(self, daq_count): + self.daq_count = daq_count + + def allocOdt(self, daq_num, odt_count): + pass + + def allocOdtEntry(self, daq_num, odt_num, entry_count): + pass + + def setDaqPtr(self, daqListNumber, odtNumber, odtEntryNumber): + pass + + def writeDaq(self, bitOffset, entrySize, addressExt, address): + pass + + def setDaqListMode(self, mode, daqListNumber, eventChannelNumber, prescaler, priority): + pass + + def startStopDaqList(self, mode, daqListNumber): + pass + + def startStopSynch(self, mode): + pass + + +DAQ_LISTS = [ + DaqList( + 1, + [ + ("channel1", 0x1BD004, 0, 4, "U32"), + ("channel2", 0x1BD008, 0, 4, "U32"), + ("PWMFiltered", 0x1BDDE2, 0, 1, "U8"), + ("PWM", 0x1BDDDF, 0, 1, "U8"), + ("Triangle", 0x1BDDDE, 0, 1, "U8"), + ], + ), + DaqList( + 3, + [ + ("TestWord_001", 0x1BE120, 0, 2, "U16"), + ("TestWord_003", 0x1BE128, 0, 2, "U16"), + ("TestWord_004", 0x1BE12C, 0, 2, "U16"), + ("TestWord_005", 0x1BE134, 0, 2, "U16"), + ("TestWord_006", 0x1BE134, 0, 2, "U16"), + ("TestWord_007", 0x1BE138, 0, 2, "U16"), + ("TestWord_008", 0x1BE13C, 0, 2, "U16"), + ("TestWord_009", 0x1BE140, 0, 2, "U16"), + ("TestWord_011", 0x1BE148, 0, 2, "U16"), + # ("", ), + ], + ), +] + +daq = Daq() +daq.set_master(MockMaster()) + +daq.add_daq_lists(DAQ_LISTS) +daq.setup() +daq.start() + From 1531c8edcb6abf30256ea69e21b9205a6893a049 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 12 Oct 2023 10:37:31 +0200 Subject: [PATCH 02/99] today() --- pyxcp/cpp_ext/__init__.py | 4 +- pyxcp/cpp_ext/daqlist.hpp | 7 +- pyxcp/daq_stim/__init__.py | 73 ++++---- pyxcp/daq_stim/optimize/__init__.py | 6 + pyxcp/recorder/rekorder.hpp | 1 + pyxcp/recorder/unfolder.hpp | 248 ++++++++++++++++++++-------- pyxcp/recorder/wrap.cpp | 2 +- requirements.txt | 23 ++- setup.py | 12 +- 9 files changed, 254 insertions(+), 122 deletions(-) diff --git a/pyxcp/cpp_ext/__init__.py b/pyxcp/cpp_ext/__init__.py index e80c591..b49713c 100644 --- a/pyxcp/cpp_ext/__init__.py +++ b/pyxcp/cpp_ext/__init__.py @@ -1 +1,3 @@ -from .cpp_ext import (Bin, DaqList, McObject) \ No newline at end of file +from .cpp_ext import Bin +from .cpp_ext import DaqList +from .cpp_ext import McObject diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index 4f6d7bc..ae4e7c3 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -9,16 +9,15 @@ class DaqList { public: - using daq_list_initialzer_t = std::tuple; + using daq_list_initialzer_t = std::tuple; using flatten_odts_t = std::vector>>; DaqList(std::uint16_t event_num, bool enable_timestamps, const std::vector& measurements) : m_event_num(event_num), m_enable_timestamps(enable_timestamps) { for (const auto& measurement : measurements) { - auto const& [name, address, ext, length, dt_name] = measurement; - // std::cout << "DL-obj: " << name << ", " << address << ", " << ext << ", " << length << ", " << dt_name << std::endl; - m_measurements.emplace_back(McObject(name, address, ext, length, dt_name)); + auto const& [name, address, ext, dt_name] = measurement; + m_measurements.emplace_back(McObject(name, address, ext, 0, dt_name)); } } diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 86855a8..b95b9e9 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -69,22 +69,51 @@ def setup(self, write_multiple: bool = True): else: self.ts_size = 0 self.ts_fixed = False + self.ts_scale_factor = 0.0 key_byte = processor.get("keyByte") header_len = DAQ_ID_FIELD_SIZE[key_byte["identificationField"]] max_dto = self.xcp_master.slaveProperties.maxDto + self.min_daq = processor.get("minDaq") max_odt_entry_size = resolution.get("maxOdtEntrySizeDaq") max_payload_size = min(max_odt_entry_size, max_dto - header_len) - self.min_daq = processor.get("minDaq") + # First ODT may contain timestamp. + self.selectable_timestamps = False + if not self.supports_timestampes: + max_payload_size_first = max_payload_size + print("NO TIMESTAMP SUPPORT") + else: + if self.ts_fixed: + print("Fixed timestamp") + max_payload_size_first = max_payload_size - self.ts_size + else: + print("timestamp variable.") + self.selectable_timestamps = True + except Exception as e: raise TypeError(f"DAQ_INFO corrupted: {e}") from e # DAQ optimization. for daq_list in self.daq_lists: - ttt = make_continuous_blocks(daq_list.measurements, max_payload_size) - daq_list.measurements_opt = first_fit_decreasing(ttt, max_payload_size) - + if self.selectable_timestamps: + if daq_list.enable_timestamps: + max_payload_size_first = max_payload_size - self.ts_size + else: + max_payload_size_first = max_payload_size + ttt = make_continuous_blocks(daq_list.measurements, max_payload_size, max_payload_size_first) + daq_list.measurements_opt = first_fit_decreasing(ttt, max_payload_size, max_payload_size_first) byte_order = 0 if self.xcp_master.slaveProperties.byteOrder == "INTEL" else 1 - self.uf = UnfoldingParameters(byte_order, header_len, self.ts_scale_factor, False, self.ts_size, self.daq_lists) + self.uf = UnfoldingParameters( + byte_order, + header_len, + self.supports_timestampes, + self.ts_fixed, + self.supports_prescaler, + self.selectable_timestamps, + self.ts_scale_factor, + self.ts_size, + self.min_daq, + self.daq_lists, + ) self.first_pids = [] daq_count = len(self.daq_lists) @@ -100,7 +129,6 @@ def setup(self, write_multiple: bool = True): self.xcp_master.allocOdtEntry(i, j, entry_count) # Write DAQs for i, daq_list in enumerate(self.daq_lists, self.min_daq): - # self.xcp_master.setDaqListMode(daqListNumber=i, mode=0x10, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xff) measurements = daq_list.measurements_opt for j, measurement in enumerate(measurements): self.xcp_master.setDaqPtr(i, j, 0) @@ -109,7 +137,9 @@ def setup(self, write_multiple: bool = True): def start(self): for i, daq_list in enumerate(self.daq_lists, self.min_daq): - mode = 0x10 if daq_list.enable_timestamps else 0x00 + mode = 0x00 + if self.supports_timestampes and (self.ts_fixed or (self.selectable_timestamps and daq_list.enable_timestamps)): + mode = 0x10 self.xcp_master.setDaqListMode( daqListNumber=i, mode=mode, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xFF # TODO: + MIN_DAQ ) @@ -126,32 +156,3 @@ def reader(self): for block in unfolder.next_block(): print(block) - - -class Collector: - def __init__(self, daq_num: int, num_odts: int, unfolder, callback): - self.daq_num = daq_num - self.num_odts = num_odts - self.current_odt_num = 0 - self.frames = [None] * num_odts - self.unfolder = unfolder - self.callback = callback - - def add(self, odt_num, frame): - if odt_num != self.current_odt_num: - print(f"WRONG SEQ-NO {odt_num} expected {self.current_odt_num} [LIST: {self.daq_num}]") - self.current_odt_num = odt_num - self.frames[self.current_odt_num] = frame - self.current_odt_num += 1 - if self.current_odt_num == self.num_odts: - result = {} - for idx, frame in enumerate(self.frames): - offset = 0 - for name, length in self.unfolder[idx]: - data = frame[offset : offset + length] - result[name] = bytes(data) - offset += length - if self.callback is not None: - self.callback(0, result) - # print("DAQ", self.daq_num, result) - self.current_odt_num %= self.num_odts diff --git a/pyxcp/daq_stim/optimize/__init__.py b/pyxcp/daq_stim/optimize/__init__.py index e5d8b96..ddee368 100644 --- a/pyxcp/daq_stim/optimize/__init__.py +++ b/pyxcp/daq_stim/optimize/__init__.py @@ -38,6 +38,9 @@ def key_func(x): result_sections = [] last_section = None last_ext = None + first_section = True + if upper_bound_initial is None: + upper_bound_initial = upper_bound while values: section = values.pop(0) if (last_section and section.address <= last_section.address + last_section.length) and not (section.ext != last_ext): @@ -48,6 +51,9 @@ def key_func(x): else: offset = current_end - last_end if upper_bound: + if first_section: + upper_bound = upper_bound_initial + first_section = False if last_section.length + offset <= upper_bound: last_section.length += offset last_section.add_component(section) diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 15ba649..ab62276 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -9,6 +9,7 @@ #include #include + #include #include #include #include diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 03cd442..2607868 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -2,8 +2,10 @@ #ifndef RECORDER_UNFOLDER_HPP #define RECORDER_UNFOLDER_HPP +#include #include #include +#include #include "daqlist.hpp" #include "mcobject.hpp" @@ -117,8 +119,6 @@ struct Getter { float_ = get_value; double_ = get_value; } - // ts_size=0; - std::cout << "TS-SIZE: " << static_cast(ts_size) << " : " << static_cast(m_id_size) << std::endl; } std::uint32_t get_timestamp(blob_t const * buf) { @@ -219,39 +219,174 @@ struct UnfoldingParameters { UnfoldingParameters() = delete; explicit UnfoldingParameters( - std::uint8_t byte_order, std::uint8_t id_field_size, double scale_factor, bool enable_timestamps, std::uint8_t ts_size, + std::uint8_t byte_order, std::uint8_t id_field_size, bool timestamps_supported, bool ts_fixed, bool prescaler_supported, + bool selectable_timestamps, double ts_scale_factor, std::uint8_t ts_size, std::uint16_t min_daq, const std::vector& daq_lists ) : m_byte_order(byte_order), m_id_field_size(id_field_size), - m_scale_factor(scale_factor), - m_enable_timestamps(enable_timestamps), + m_timestamps_supported(timestamps_supported), + m_ts_fixed(ts_fixed), + m_prescaler_supported(prescaler_supported), + m_selectable_timestamps(selectable_timestamps), + m_ts_scale_factor(ts_scale_factor), m_ts_size(ts_size), + m_min_daq(min_daq), m_daq_lists(daq_lists) { } std::uint8_t m_byte_order; // INTEL(LITTLE)=0, MOTOROLA(BIG)=1 std::uint8_t m_id_field_size; - double m_scale_factor; + bool m_timestamps_supported; + bool m_ts_fixed; + bool m_prescaler_supported; + double m_ts_scale_factor; std::uint8_t m_ts_size; - bool m_enable_timestamps; + std::uint16_t m_min_daq; + bool m_selectable_timestamps; std::vector m_daq_lists; - // timestampSupported - // prescalerSupported - // timestampMode.fixed - // minDAQ }; -class MeasurementBuffer { +class DaqListState { public: - MeasurementBuffer(std::size_t num_elements) : m_buffer(num_elements) { + enum class state_t : std::uint8_t { + IDLE = 0, + COLLECTING = 1, + FINISHED = 2, + _IGNORE = 3, // Duplicate frame. + _ERROR = 4, // Out-of-order/missing sequence/ODT number. + }; + + DaqListState( + std::uint16_t num_odts, std::uint16_t total_entries, bool enable_timestamps, std::uint16_t initial_offset, + const DaqList::flatten_odts_t& flatten_odts, const Getter& getter, UnfoldingParameters params + ) : + m_num_odts(num_odts), + m_total_entries(total_entries), + m_enable_timestamps(enable_timestamps), + m_initial_offset(initial_offset), + m_flatten_odts(flatten_odts), + m_getter(getter), + m_params(params), + m_state(state_t::IDLE) { + m_buffer.resize(m_total_entries); + } + + state_t check_state(uint16_t odt_num) { + // std::cout << "\t\tDAQListState::check: " << odt_num << "/" << m_next_odt << std::endl; + if ((m_state == state_t::IDLE) && (odt_num == 0x00)) { + // "synch pulse". + // std::cout << "\t\tDAQListState::check: synch pulse" << ": " << m_num_odts << std::endl; + if (m_num_odts == 0x01) { + // std::cout << "\t\t\tDAQListState::check: single fin" << std::endl; + resetSM(); + return state_t::FINISHED; + } else { + m_state = state_t::COLLECTING; + m_next_odt = 1; + } + } else if (m_state == state_t::COLLECTING) { + if (odt_num == m_next_odt) { + // std::cout << "\t\tDAQListState::check: next odt" << std::endl; + m_next_odt++; + if (m_next_odt == m_num_odts) { + // std::cout << "\t\t\tDAQListState::check: final odt" << std::endl; + resetSM(); + return state_t::FINISHED; + } + } else { + std::cout << "\t\tDAQListState::check: out of order" << odt_num << ": " << m_next_odt << std::endl; + resetSM(); + return state_t::_ERROR; + } + } + return m_state; + } + + void feed(uint16_t odt_num, double timestamp, const payload_t& payload) { + auto state = check_state(odt_num); + // std::cout << "\t\tDAQListState::feed: " << static_cast(state) << std::endl; + if (state == state_t::COLLECTING) { + m_timestamp0 = timestamp; + parse_Odt(odt_num, payload); + } else if (state == state_t::FINISHED) { + m_timestamp0 = timestamp; + parse_Odt(odt_num, payload); + // std::cout << "\t\tDAQListState::feed: FINISHED" << std::endl; + } + } + + protected: + + void resetSM() { + m_state = state_t::IDLE; + m_next_odt = 0; + m_current_idx = 0; + m_timestamp0 = 0.0; + m_timestamp1 = 0.0; + } + + void parse_Odt(uint16_t odt_num, const payload_t& payload) { + auto offset = m_initial_offset; // consider ID field size. + // const auto& [category, counter, frame_timestamp, frame_length, payload] = frame; + auto payload_data = payload.data(); + auto payload_size = payload.size(); + + // auto [daq_num, odt_num] = m_getter.get_id(payload_data); + // m_offset += m_params.m_id_field_size; + // std::cout << "CTR: " << counter << " ID: " << daq_num << ": " << odt_num << std::endl; + + if (odt_num == 0) { + if (m_params.m_timestamps_supported && + (m_params.m_ts_fixed || (m_params.m_selectable_timestamps && m_enable_timestamps == true))) { + m_timestamp1 = static_cast(m_getter.get_timestamp(payload_data)) * m_params.m_ts_scale_factor; + offset += m_params.m_ts_size; + } else { + m_timestamp1 = 0.0; + } + std::cout << "\tSTART DAQ-LIST [" << m_timestamp0 << ", " << m_timestamp1 << std::endl; + } + + for (const auto& param : m_flatten_odts[odt_num]) { + const auto& [name, address, ext, size, type_index] = param; + + std::cout << "\t" << name << " " << offset << " : " << size << " ==> "; + + if (offset >= payload_size) { + throw std::runtime_error( + "Offset is out of range! " + std::to_string(offset) + " >= " + std::to_string(payload_size) + ); + } + auto data = m_getter.reader(type_index, payload_data, offset); + + if (std::holds_alternative(data)) { + std::cout << std::get(data) << " " << std::endl; + } else if (std::holds_alternative(data)) { + std::cout << std::get(data) << "(+/-) " << std::endl; + } else if (std::holds_alternative(data)) { + std::cout << std::get(data) << " (double) " << std::endl; + } + m_buffer[m_current_idx++] = std::move(data); + offset += size; + } } private: + std::uint16_t m_num_odts = 0; + std::uint16_t m_total_entries = 0; + bool m_enable_timestamps = false; + std::uint16_t m_initial_offset; + std::uint16_t m_next_odt = 0; + std::uint16_t m_current_idx = 0; + double m_timestamp0 = 0.0; + double m_timestamp1 = 0.0; + state_t m_state = state_t::IDLE; std::vector m_buffer; - std::uint16_t m_current_odt = 0; + DaqList::flatten_odts_t m_flatten_odts; + Getter m_getter; + UnfoldingParameters m_params; }; class XcpLogFileUnfolder { @@ -262,8 +397,6 @@ class XcpLogFileUnfolder { std::endian target_byte_order; bool requires_swap; - // std::vector<; - if (m_params.m_byte_order == 0) { target_byte_order = std::endian::little; } else if (m_params.m_byte_order == 1) { @@ -274,15 +407,18 @@ class XcpLogFileUnfolder { } else { requires_swap = false; } - - auto ts_size = m_params.m_ts_size; - std::cout << "ENA-TS: " << m_params.m_enable_timestamps << std::endl; - if (params.m_enable_timestamps) { - ts_size = 0; + m_getter = Getter(requires_swap, params.m_id_field_size, params.m_ts_size); + + auto num_daq_lists = params.m_daq_lists.size(); + m_buffers.resize(num_daq_lists); + for (auto idx = 0; idx < num_daq_lists; ++idx) { + m_buffers[idx].resize(params.m_daq_lists[idx].get_total_entries()); + m_state.emplace_back(DaqListState( + params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), + m_params.m_daq_lists[idx].get_enable_timestamps(), m_params.m_id_field_size, + m_params.m_daq_lists[idx].get_flatten_odts(), m_getter, m_params + )); } - - std::cout << "ID-size: " << static_cast(params.m_id_field_size) << std::endl; - m_getter = Getter(requires_swap, params.m_id_field_size, ts_size); } void start(const std::vector& first_pids) { @@ -291,6 +427,7 @@ class XcpLogFileUnfolder { std::optional next_block() { std::uint16_t offset = 0; + double ts = 0.0; while (true) { const auto& block = m_reader.next_block(); @@ -300,63 +437,32 @@ class XcpLogFileUnfolder { } for (const auto& frame : block.value()) { - const auto& [category, counter, timestamp, frame_length, payload] = frame; - auto payload_data = payload.data(); - - if (category != static_cast(FrameCategory::DAQ)) { + if (std::get<0>(frame) != static_cast(FrameCategory::DAQ)) { continue; } - //////////////////////////////// - offset = 0; - auto [daq_num, odt_num] = m_getter.get_id(payload_data); - offset += m_params.m_id_field_size; - std::cout << "CTR: " << counter << " ID: " << daq_num << ": " << odt_num << std::endl; - - if (odt_num == 0) { - auto ts = m_getter.get_timestamp(payload_data); - // auto ts = 0; - std::cout << "\tSTART DAQ-LIST: " << ts << std::endl; - if (m_params.m_enable_timestamps) { - offset += m_params.m_ts_size; - } - } - for (const auto& param : m_params.m_daq_lists[daq_num].get_flatten_odts()[odt_num]) { - const auto& [name, address, ext, size, type_index] = param; - - std::cout << "\t" << name << " " << offset << " : " << size << " ==> "; - - auto length = payload.size(); - if (offset >= length) { - throw std::runtime_error( - "Offset is out of range! " + std::to_string(offset) + " >= " + std::to_string(length) - ); - } - auto data = m_getter.reader(type_index, payload_data, offset); - - if (std::holds_alternative(data)) { - std::cout << std::get(data) << " " << std::endl; - } else if (std::holds_alternative(data)) { - std::cout << std::get(data) << "(+/-) " << std::endl; - } else if (std::holds_alternative(data)) { - std::cout << std::get(data) << " (double) " << std::endl; - } - offset += size; - } - //////////////////////////////// + unfold(frame); } return std::nullopt; } return std::nullopt; } + void unfold(const FrameTuple& frame) { + const auto& [category, counter, frame_timestamp, frame_length, payload] = frame; + auto [daq_num, odt_num] = m_getter.get_id(payload.data()); + + m_state[daq_num].feed(odt_num, frame_timestamp, payload); + } + private: - XcpLogFileReader m_reader; - std::endian m_byte_order; - UnfoldingParameters m_params; - Getter m_getter; - std::map m_first_pids; - // std::vector> m_measurement_buffers; + XcpLogFileReader m_reader; + std::endian m_byte_order; + UnfoldingParameters m_params; + Getter m_getter; + std::map m_first_pids; + std::vector> m_buffers; + std::vector m_state; }; #endif // RECORDER_UNFOLDER_HPP diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 44714d4..d270827 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -23,7 +23,7 @@ PYBIND11_MODULE(rekorder, m) { .def("add_frame", &XcpLogFileWriter::add_frame); py::class_(m, "_UnfoldingParameters") - .def(py::init&>()); + .def(py::init&>()); py::class_(m, "_XcpLogFileUnfolder") .def(py::init()) diff --git a/requirements.txt b/requirements.txt index 5c17290..1ce4e85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,22 @@ -pybind11 -pytest +pybind11~=2.10.4 +pytest~=7.4.2 pytest-runner construct>=2.9 -mako -traitlets -chardet -pyserial +mako~=1.2.4 +traitlets~=5.10.0 +chardet~=5.1.0 +pyserial~=3.5 numpydoc sphinxcontrib-napoleon -toml +toml~=0.10.2 pyusb win-precise-time; sys_platform == 'win32' + +numpy~=1.25.0 +setuptools~=65.5.0 +packaging~=23.1 +Babel~=2.12.1 +Pygments~=2.16.1 +MarkupSafe~=2.1.3 +Jinja2~=3.1.2 +docutils~=0.20.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 9b1d063..865093c 100644 --- a/setup.py +++ b/setup.py @@ -42,18 +42,26 @@ long_description = fh.read() -EXT_NAMES = ["rekorder"] +EXT_NAMES = ["pyxcp.recorder.rekorder", "pyxcp.cpp_ext.cpp_ext"] if has_pybind11: ext_modules = [ Pybind11Extension( EXT_NAMES[0], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/recorder"], + include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/recorder", "pyxcp/cpp_ext"], sources=["pyxcp/recorder/lz4.c", "pyxcp/recorder/wrap.cpp"], define_macros=[("EXTENSION_NAME", EXT_NAMES[0]), ("NDEBUG", 1)], optional=False, cxx_std=20, ), + Pybind11Extension( + EXT_NAMES[1], + include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/cpp_ext"], + sources=["pyxcp/cpp_ext/extension_wrapper.cpp"], + define_macros=[("EXTENSION_NAME", EXT_NAMES[1]), ("NDEBUG", 1)], + optional=False, + cxx_std=20, + ), ] else: ext_modules = [] From 1ee994d1d302019aa905de188c6fb7c29a1316a0 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 15 Oct 2023 17:46:22 +0200 Subject: [PATCH 03/99] today() --- pyxcp/cpp_ext/daqlist.hpp | 12 ++- pyxcp/cpp_ext/extension_wrapper.cpp | 5 +- pyxcp/daq_stim/__init__.py | 23 +++++- pyxcp/recorder/__init__.py | 21 ++++- pyxcp/recorder/unfolder.hpp | 124 +++++++++++++--------------- pyxcp/recorder/wrap.cpp | 7 +- 6 files changed, 110 insertions(+), 82 deletions(-) diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index ae4e7c3..49f80bc 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -13,8 +13,11 @@ class DaqList { using flatten_odts_t = std::vector>>; - DaqList(std::uint16_t event_num, bool enable_timestamps, const std::vector& measurements) : - m_event_num(event_num), m_enable_timestamps(enable_timestamps) { + DaqList( + std::string_view meas_name, std::uint16_t event_num, bool enable_timestamps, + const std::vector& measurements + ) : + m_name(meas_name), m_event_num(event_num), m_enable_timestamps(enable_timestamps) { for (const auto& measurement : measurements) { auto const& [name, address, ext, dt_name] = measurement; m_measurements.emplace_back(McObject(name, address, ext, 0, dt_name)); @@ -25,6 +28,10 @@ class DaqList { return m_enable_timestamps; } + const std::string& get_name() const { + return m_name; + } + bool get_event_num() const { return m_event_num; } @@ -86,6 +93,7 @@ class DaqList { private: + std::string m_name; std::uint16_t m_event_num; bool m_enable_timestamps; std::vector m_measurements; diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index 5fb4939..2b34b8b 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -54,8 +54,9 @@ PYBIND11_MODULE(cpp_ext, m) { ; py::class_(m, "DaqList") - .def(py::init&>(), - "event_num"_a, "enable_timestamps"_a, "measurements"_a) + .def(py::init&>(), + "name"_a, "event_num"_a, "enable_timestamps"_a, "measurements"_a) + .def_property("name", &DaqList::get_name, nullptr) .def_property("event_num", &DaqList::get_event_num, nullptr) .def_property("enable_timestamps", &DaqList::get_enable_timestamps, nullptr) .def_property("measurements", &DaqList::get_measurements, nullptr) diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index b95b9e9..f89be6f 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- import struct +import time from collections import defaultdict from dataclasses import dataclass from dataclasses import field @@ -17,7 +18,7 @@ from pyxcp.daq_stim.optimize import make_continuous_blocks from pyxcp.daq_stim.optimize import McObject from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing -from pyxcp.recorder import UnfoldingParameters +from pyxcp.recorder import MeasurementParameters from pyxcp.recorder import XcpLogFileReader from pyxcp.recorder import XcpLogFileUnfolder from pyxcp.types import FrameCategory @@ -102,7 +103,7 @@ def setup(self, write_multiple: bool = True): ttt = make_continuous_blocks(daq_list.measurements, max_payload_size, max_payload_size_first) daq_list.measurements_opt = first_fit_decreasing(ttt, max_payload_size, max_payload_size_first) byte_order = 0 if self.xcp_master.slaveProperties.byteOrder == "INTEL" else 1 - self.uf = UnfoldingParameters( + self.uf = MeasurementParameters( byte_order, header_len, self.supports_timestampes, @@ -150,9 +151,23 @@ def start(self): def stop(self): self.xcp_master.startStopSynch(0x00) + import time + def reader(self): unfolder = XcpLogFileUnfolder(self.file_name, self.uf) unfolder.start(self.first_pids) - for block in unfolder.next_block(): - print(block) + print("HEADERS") + philez = [] + for idx, d in enumerate(self.daq_lists): + philez.append(open(f"{d.name}.csv", "wt")) + print(d.name, d.header_names) + hdr = ",".join(["timestamp0", "timestamp1"] + d.header_names) + philez[idx].write(f"{hdr}\n") + print("DATA") + start_time = time.perf_counter() + for daq_list, ts0, ts1, payload in unfolder: + philez[daq_list].write(f'{ts0},{ts1},{", ".join([str(x) for x in payload])}\n') + for ph in philez: + ph.close() + print("ETA: ", time.perf_counter() - start_time, "seconds") diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 2766d68..288cfb5 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -17,9 +17,7 @@ import pyxcp.recorder.rekorder as rec - -UnfoldingParameters = rec._UnfoldingParameters -XcpLogFileUnfolder = rec._XcpLogFileUnfolder +MeasurementParameters = rec._MeasurementParameters @dataclass @@ -67,6 +65,23 @@ def as_dataframe(self): raise NotImplementedError("method as_dataframe() requires 'pandas' package") +class XcpLogFileUnfolder: + def __init__(self, file_name: str, params: MeasurementParameters): + self._unfolder = rec._XcpLogFileUnfolder(file_name, params) + + def start(self, first_pids: list): + self._unfolder.start(first_pids) + + def __iter__(self): + while True: + block = self._unfolder.next_block() + if block is None: + break + for frame in block: + daq_list, ts0, ts1, payload = frame + yield (daq_list, ts0, ts1, payload) + + class XcpLogFileWriter: """ """ diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 2607868..e9bca9a 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -27,8 +27,8 @@ constexpr auto _bswap(std::uint16_t v) noexcept { return ((v & UINT16_C(0x00FF)) << 8) | ((v & UINT16_C(0xFF00)) >> 8); } -using measurement_value_t = std::variant; -using measurement_value_vector_t = std::vector; +using measurement_value_t = std::variant; +using measurement_tuple_t = std::tuple>; template auto get_value(blob_t const * buf, std::uint32_t offset) -> Ty { @@ -168,7 +168,6 @@ struct Getter { if (m_id_size == 1) { // In case of 1-byte ID field (absolute ODT number) we need a mapping. - std::uint16_t daq_list_num = 0; for (const auto& daq_list : daq_lists) { auto first_pid = m_first_pids[daq_list_num]; @@ -215,10 +214,10 @@ struct Getter { std::map> m_odt_to_daq_map; }; -struct UnfoldingParameters { - UnfoldingParameters() = delete; +struct MeasurementParameters { + MeasurementParameters() = delete; - explicit UnfoldingParameters( + explicit MeasurementParameters( std::uint8_t byte_order, std::uint8_t id_field_size, bool timestamps_supported, bool ts_fixed, bool prescaler_supported, bool selectable_timestamps, double ts_scale_factor, std::uint8_t ts_size, std::uint16_t min_daq, const std::vector& daq_lists @@ -259,9 +258,11 @@ class DaqListState { }; DaqListState( - std::uint16_t num_odts, std::uint16_t total_entries, bool enable_timestamps, std::uint16_t initial_offset, - const DaqList::flatten_odts_t& flatten_odts, const Getter& getter, UnfoldingParameters params + std::uint16_t daq_list_num, std::uint16_t num_odts, std::uint16_t total_entries, bool enable_timestamps, + std::uint16_t initial_offset, const DaqList::flatten_odts_t& flatten_odts, const Getter& getter, + MeasurementParameters params ) : + m_daq_list_num(daq_list_num), m_num_odts(num_odts), m_total_entries(total_entries), m_enable_timestamps(enable_timestamps), @@ -274,12 +275,9 @@ class DaqListState { } state_t check_state(uint16_t odt_num) { - // std::cout << "\t\tDAQListState::check: " << odt_num << "/" << m_next_odt << std::endl; if ((m_state == state_t::IDLE) && (odt_num == 0x00)) { // "synch pulse". - // std::cout << "\t\tDAQListState::check: synch pulse" << ": " << m_num_odts << std::endl; if (m_num_odts == 0x01) { - // std::cout << "\t\t\tDAQListState::check: single fin" << std::endl; resetSM(); return state_t::FINISHED; } else { @@ -288,15 +286,13 @@ class DaqListState { } } else if (m_state == state_t::COLLECTING) { if (odt_num == m_next_odt) { - // std::cout << "\t\tDAQListState::check: next odt" << std::endl; m_next_odt++; if (m_next_odt == m_num_odts) { - // std::cout << "\t\t\tDAQListState::check: final odt" << std::endl; resetSM(); return state_t::FINISHED; } } else { - std::cout << "\t\tDAQListState::check: out of order" << odt_num << ": " << m_next_odt << std::endl; + std::cout << "\t\tODT num out of order: " << odt_num << " -- expected: " << m_next_odt << std::endl; resetSM(); return state_t::_ERROR; } @@ -304,40 +300,39 @@ class DaqListState { return m_state; } - void feed(uint16_t odt_num, double timestamp, const payload_t& payload) { - auto state = check_state(odt_num); - // std::cout << "\t\tDAQListState::feed: " << static_cast(state) << std::endl; + bool feed(uint16_t odt_num, double timestamp, const payload_t& payload) { + auto state = check_state(odt_num); + auto finished = false; if (state == state_t::COLLECTING) { m_timestamp0 = timestamp; parse_Odt(odt_num, payload); } else if (state == state_t::FINISHED) { m_timestamp0 = timestamp; parse_Odt(odt_num, payload); - // std::cout << "\t\tDAQListState::feed: FINISHED" << std::endl; + finished = true; } + return finished; + } + + void add_result(std::vector& result_buffer) { + result_buffer.emplace_back(m_daq_list_num, m_timestamp0, m_timestamp1, m_buffer); } protected: void resetSM() { - m_state = state_t::IDLE; - m_next_odt = 0; - m_current_idx = 0; - m_timestamp0 = 0.0; - m_timestamp1 = 0.0; + m_state = state_t::IDLE; + m_next_odt = 0; + m_timestamp0 = 0.0; } void parse_Odt(uint16_t odt_num, const payload_t& payload) { - auto offset = m_initial_offset; // consider ID field size. - // const auto& [category, counter, frame_timestamp, frame_length, payload] = frame; + auto offset = m_initial_offset; // consider ID field size. auto payload_data = payload.data(); auto payload_size = payload.size(); - // auto [daq_num, odt_num] = m_getter.get_id(payload_data); - // m_offset += m_params.m_id_field_size; - // std::cout << "CTR: " << counter << " ID: " << daq_num << ": " << odt_num << std::endl; - if (odt_num == 0) { + m_current_idx = 0; if (m_params.m_timestamps_supported && (m_params.m_ts_fixed || (m_params.m_selectable_timestamps && m_enable_timestamps == true))) { m_timestamp1 = static_cast(m_getter.get_timestamp(payload_data)) * m_params.m_ts_scale_factor; @@ -345,21 +340,18 @@ class DaqListState { } else { m_timestamp1 = 0.0; } - std::cout << "\tSTART DAQ-LIST [" << m_timestamp0 << ", " << m_timestamp1 << std::endl; } for (const auto& param : m_flatten_odts[odt_num]) { const auto& [name, address, ext, size, type_index] = param; - std::cout << "\t" << name << " " << offset << " : " << size << " ==> "; - if (offset >= payload_size) { throw std::runtime_error( "Offset is out of range! " + std::to_string(offset) + " >= " + std::to_string(payload_size) ); } auto data = m_getter.reader(type_index, payload_data, offset); - +#if 0 if (std::holds_alternative(data)) { std::cout << std::get(data) << " " << std::endl; } else if (std::holds_alternative(data)) { @@ -367,6 +359,7 @@ class DaqListState { } else if (std::holds_alternative(data)) { std::cout << std::get(data) << " (double) " << std::endl; } +#endif m_buffer[m_current_idx++] = std::move(data); offset += size; } @@ -374,6 +367,7 @@ class DaqListState { private: + std::uint16_t m_daq_list_num = 0; std::uint16_t m_num_odts = 0; std::uint16_t m_total_entries = 0; bool m_enable_timestamps = false; @@ -386,13 +380,13 @@ class DaqListState { std::vector m_buffer; DaqList::flatten_odts_t m_flatten_odts; Getter m_getter; - UnfoldingParameters m_params; + MeasurementParameters m_params; }; class XcpLogFileUnfolder { public: - explicit XcpLogFileUnfolder(const std::string& file_name, const UnfoldingParameters& params) : + explicit XcpLogFileUnfolder(const std::string& file_name, const MeasurementParameters& params) : m_reader(file_name), m_byte_order(std::endian::native), m_params(params) { std::endian target_byte_order; bool requires_swap; @@ -408,13 +402,9 @@ class XcpLogFileUnfolder { requires_swap = false; } m_getter = Getter(requires_swap, params.m_id_field_size, params.m_ts_size); - - auto num_daq_lists = params.m_daq_lists.size(); - m_buffers.resize(num_daq_lists); - for (auto idx = 0; idx < num_daq_lists; ++idx) { - m_buffers[idx].resize(params.m_daq_lists[idx].get_total_entries()); + for (auto idx = 0; idx < params.m_daq_lists.size(); ++idx) { m_state.emplace_back(DaqListState( - params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), + idx, params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), m_params.m_daq_lists[idx].get_enable_timestamps(), m_params.m_id_field_size, m_params.m_daq_lists[idx].get_flatten_odts(), m_getter, m_params )); @@ -425,44 +415,42 @@ class XcpLogFileUnfolder { m_getter.set_first_pids(m_params.m_daq_lists, first_pids); } - std::optional next_block() { - std::uint16_t offset = 0; - double ts = 0.0; + std::optional> next_block() { + std::vector result{}; - while (true) { - const auto& block = m_reader.next_block(); + const auto& block = m_reader.next_block(); - if (!block) { - break; - } + std::cout << "Block number: " << xxx_blk_no++ << std::endl; - for (const auto& frame : block.value()) { - if (std::get<0>(frame) != static_cast(FrameCategory::DAQ)) { - continue; - } - unfold(frame); - } + if (!block) { return std::nullopt; } - return std::nullopt; - } - void unfold(const FrameTuple& frame) { - const auto& [category, counter, frame_timestamp, frame_length, payload] = frame; - auto [daq_num, odt_num] = m_getter.get_id(payload.data()); + for (const auto& frame : block.value()) { + if (std::get<0>(frame) != static_cast(FrameCategory::DAQ)) { + continue; + } + const auto& [category, counter, frame_timestamp, frame_length, payload] = frame; + auto [daq_num, odt_num] = m_getter.get_id(payload.data()); - m_state[daq_num].feed(odt_num, frame_timestamp, payload); + if (m_state[daq_num].feed(odt_num, frame_timestamp, payload)) { + m_state[daq_num].add_result(result); + } + } + return result; } private: - XcpLogFileReader m_reader; - std::endian m_byte_order; - UnfoldingParameters m_params; - Getter m_getter; - std::map m_first_pids; - std::vector> m_buffers; - std::vector m_state; + XcpLogFileReader m_reader; + std::endian m_byte_order; + MeasurementParameters m_params; + Getter m_getter; + std::map m_first_pids; + // std::vector> m_buffers; + std::vector m_state; + + std::uint16_t xxx_blk_no{ 0 }; }; #endif // RECORDER_UNFOLDER_HPP diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index d270827..284a42c 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -22,11 +22,12 @@ PYBIND11_MODULE(rekorder, m) { .def("finalize", &XcpLogFileWriter::finalize) .def("add_frame", &XcpLogFileWriter::add_frame); - py::class_(m, "_UnfoldingParameters") + py::class_(m, "_MeasurementParameters") .def(py::init&>()); py::class_(m, "_XcpLogFileUnfolder") - .def(py::init()) + .def(py::init()) .def("next_block", &XcpLogFileUnfolder::next_block) - .def("start", &XcpLogFileUnfolder::start); + .def("start", &XcpLogFileUnfolder::start) + ; } From 7cb92ca83e02f23b0153062100867d0d8206d8eb Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 17 Aug 2023 14:10:39 +0200 Subject: [PATCH 04/99] STIM initial --- pyxcp/master/master.py | 87 +++++-- pyxcp/stim/stim.cpp | 20 ++ pyxcp/stim/stim.hpp | 456 ++++++++++++++++++++++++++++++++++++ pyxcp/stim/stim_wrapper.cpp | 57 +++++ pyxcp/types.py | 15 ++ 5 files changed, 613 insertions(+), 22 deletions(-) create mode 100644 pyxcp/stim/stim.cpp create mode 100644 pyxcp/stim/stim.hpp create mode 100644 pyxcp/stim/stim_wrapper.cpp diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 3ffbf4e..854e280 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -20,6 +20,8 @@ from typing import Optional from typing import Union +from .stim import DaqEventInfo +from .stim import Stim from pyxcp import checksum from pyxcp import types from pyxcp.config import Configuration @@ -124,6 +126,8 @@ def __init__(self, transportName, config=None, policy=None): self.disconnect_response_optional = self.config.get("DISCONNECT_RESPONSE_OPTIONAL") self.slaveProperties = SlaveProperties() self.slaveProperties.pgmProcessor = SlaveProperties() + self.stim = Stim() + self.stim.clear() def __enter__(self): """Context manager entry part.""" @@ -967,14 +971,15 @@ def copyCalPage(self, srcSegment, srcPage, dstSegment, dstPage): # DAQ @wrapped - def setDaqPtr(self, daqListNumber, odtNumber, odtEntryNumber): + def setDaqPtr(self, daqListNumber: int, odtNumber: int, odtEntryNumber: int): self.currentDaqPtr = types.DaqPtr(daqListNumber, odtNumber, odtEntryNumber) # Needed for errorhandling. daqList = self.WORD_pack(daqListNumber) response = self.transport.request(types.Command.SET_DAQ_PTR, 0, *daqList, odtNumber, odtEntryNumber) + self.stim.setDaqPtr(daqListNumber, odtNumber, odtEntryNumber) return response @wrapped - def clearDaqList(self, daqListNumber): + def clearDaqList(self, daqListNumber: int): """Clear DAQ list configuration. Parameters @@ -982,10 +987,12 @@ def clearDaqList(self, daqListNumber): daqListNumber : int """ daqList = self.WORD_pack(daqListNumber) - return self.transport.request(types.Command.CLEAR_DAQ_LIST, 0, *daqList) + result = self.transport.request(types.Command.CLEAR_DAQ_LIST, 0, *daqList) + self.stim.clearDaqList(daqListNumber) + return result @wrapped - def writeDaq(self, bitOffset, entrySize, addressExt, address): + def writeDaq(self, bitOffset: int, entrySize: int, addressExt: int, address: int): """Write element in ODT entry. Parameters @@ -998,12 +1005,15 @@ def writeDaq(self, bitOffset, entrySize, addressExt, address): address : int """ addr = self.DWORD_pack(address) - return self.transport.request(types.Command.WRITE_DAQ, bitOffset, entrySize, addressExt, *addr) + result = self.transport.request(types.Command.WRITE_DAQ, bitOffset, entrySize, addressExt, *addr) + self.stim.writeDaq(bitOffset, entrySize, addressExt, address) + return result @wrapped def setDaqListMode(self, mode, daqListNumber, eventChannelNumber, prescaler, priority): dln = self.WORD_pack(daqListNumber) ecn = self.WORD_pack(eventChannelNumber) + self.stim.setDaqListMode(mode, daqListNumber, eventChannelNumber, prescaler, priority) return self.transport.request(types.Command.SET_DAQ_LIST_MODE, mode, *dln, *ecn, prescaler, priority) @wrapped @@ -1023,7 +1033,7 @@ def getDaqListMode(self, daqListNumber): return types.GetDaqListModeResponse.parse(response, byteOrder=self.slaveProperties.byteOrder) @wrapped - def startStopDaqList(self, mode, daqListNumber): + def startStopDaqList(self, mode: int, daqListNumber: int): """Start /stop/select DAQ list. Parameters @@ -1036,6 +1046,7 @@ def startStopDaqList(self, mode, daqListNumber): """ dln = self.WORD_pack(daqListNumber) response = self.transport.request(types.Command.START_STOP_DAQ_LIST, mode, *dln) + self.stim.startStopDaqList(mode, daqListNumber) return types.StartStopDaqListResponse.parse(response, byteOrder=self.slaveProperties.byteOrder) @wrapped @@ -1207,10 +1218,12 @@ def getDaqPackedMode(self, daqListNumber): @wrapped def freeDaq(self): """Clear dynamic DAQ configuration.""" - return self.transport.request(types.Command.FREE_DAQ) + result = self.transport.request(types.Command.FREE_DAQ) + self.stim.freeDaq() + return result @wrapped - def allocDaq(self, daqCount): + def allocDaq(self, daqCount: int): """Allocate DAQ lists. Parameters @@ -1219,17 +1232,23 @@ def allocDaq(self, daqCount): number of DAQ lists to be allocated """ dq = self.WORD_pack(daqCount) - return self.transport.request(types.Command.ALLOC_DAQ, 0, *dq) + result = self.transport.request(types.Command.ALLOC_DAQ, 0, *dq) + self.stim.allocDaq(daqCount) + return result @wrapped - def allocOdt(self, daqListNumber, odtCount): + def allocOdt(self, daqListNumber: int, odtCount: int): dln = self.WORD_pack(daqListNumber) - return self.transport.request(types.Command.ALLOC_ODT, 0, *dln, odtCount) + result = self.transport.request(types.Command.ALLOC_ODT, 0, *dln, odtCount) + self.stim.allocOdt(daqListNumber, odtCount) + return result @wrapped - def allocOdtEntry(self, daqListNumber, odtNumber, odtEntriesCount): + def allocOdtEntry(self, daqListNumber: int, odtNumber: int, odtEntriesCount: int): dln = self.WORD_pack(daqListNumber) - return self.transport.request(types.Command.ALLOC_ODT_ENTRY, 0, *dln, odtNumber, odtEntriesCount) + result = self.transport.request(types.Command.ALLOC_ODT_ENTRY, 0, *dln, odtNumber, odtEntriesCount) + self.stim.allocOdtEntry(daqListNumber, odtNumber, odtEntriesCount) + return result # PGM @wrapped @@ -1686,28 +1705,52 @@ def getDaqInfo(self): }, } result["resolution"] = resolutionInfo - channels = [] + daq_events = [] for ecn in range(dpi.maxEventChannel): eci = self.getDaqEventInfo(ecn) + cycle = eci["eventChannelTimeCycle"] + maxDaqList = eci["maxDaqList"] + priority = eci["eventChannelPriority"] + time_unit = eci["eventChannelTimeUnit"] + consistency = eci["daqEventProperties"]["consistency"] + daq_supported = eci["daqEventProperties"]["daq"] + stim_supported = eci["daqEventProperties"]["stim"] + packed_supported = eci["daqEventProperties"]["packed"] name = self.fetch(eci.eventChannelNameLength) if name: name = decode_bytes(name) channel = { "name": name, - "priority": eci["eventChannelPriority"], - "unit": eci["eventChannelTimeUnit"], - "cycle": eci["eventChannelTimeCycle"], - "maxDaqList": eci["maxDaqList"], + "priority": priority, + "unit": time_unit, + "cycle": cycle, + "maxDaqList": maxDaqList, "properties": { - "consistency": eci["daqEventProperties"]["consistency"], - "daq": eci["daqEventProperties"]["daq"], - "stim": eci["daqEventProperties"]["stim"], - "packed": eci["daqEventProperties"]["packed"], + "consistency": consistency, + "daq": daq_supported, + "stim": stim_supported, + "packed": packed_supported, }, } + print( + "EC-U", "unit", time_unit, types.EVENT_CHANNEL_TIME_UNIT_TO_EXP[time_unit], eci["daqEventProperties"]["consistency"] + ) + daq_event_info = DaqEventInfo( + name, + types.EVENT_CHANNEL_TIME_UNIT_TO_EXP[time_unit], + cycle, + maxDaqList, + priority, + consistency, + daq_supported, + stim_supported, + packed_supported, + ) + daq_events.append(daq_event_info) channels.append(channel) result["channels"] = channels + self.stim.setDaqEventInfo(daq_events) return result def getCurrentProtectionStatus(self): diff --git a/pyxcp/stim/stim.cpp b/pyxcp/stim/stim.cpp new file mode 100644 index 0000000..8a0638d --- /dev/null +++ b/pyxcp/stim/stim.cpp @@ -0,0 +1,20 @@ + +#pragma comment(lib, "Winmm.lib") +#pragma comment(lib, "Avrt.lib") + +#include "stim.hpp" + +void make_dto() { + +} + +void init() { + + +} + +Mutex _writer_lock{}; + +const Mutex& get_writer_lock() { + return _writer_lock; +} diff --git a/pyxcp/stim/stim.hpp b/pyxcp/stim/stim.hpp new file mode 100644 index 0000000..6edbe5e --- /dev/null +++ b/pyxcp/stim/stim.hpp @@ -0,0 +1,456 @@ + +#if !defined(__STIM_HPP) +#define __STIM_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class Timer { +public: + + Timer() { + QueryPerformanceFrequency(&m_ticks_per_sec); + QueryPerformanceCounter(&m_starting_time); + } + + /* + * Returns the number of µ-seconds since the timer was started. + */ + std::uint64_t elapsed() const { + LARGE_INTEGER now; + LARGE_INTEGER ela; + + QueryPerformanceCounter(&now); + ela.QuadPart = now.QuadPart - m_starting_time.QuadPart; + ela.QuadPart /= (m_ticks_per_sec.QuadPart / 1000000UL); + return ela.QuadPart; + } + +private: + LARGE_INTEGER m_ticks_per_sec; + LARGE_INTEGER m_starting_time; +}; + +template +class TsQueue { +public: + TsQueue() = default; + + TsQueue(const TsQueue& other) noexcept { + std::scoped_lock lock(other.m_mtx); + m_queue = other.m_queue; + } + + void put(T value) noexcept { + std::scoped_lock lock(m_mtx); + m_queue.push(value); + m_cond.notify_one(); + } + + std::shared_ptr get() noexcept { + std::unique_lock lock(m_mtx); + m_cond.wait(lock, [this]{return !m_queue.empty();}); + std::shared_ptr result(std::make_shared(m_queue.front())); + m_queue.pop(); + return result; + } + + bool empty() const noexcept { + std::scoped_lock lock(m_mtx); + return m_queue.empty(); + } + +private: + mutable std::mutex m_mtx; + std::queue m_queue; + std::condition_variable m_cond; +}; + +struct Mutex { + void lock() { + m_mtx.lock(); + } + void unlock() { + m_mtx.unlock(); + } + + std::mutex m_mtx{}; +}; + +extern Mutex _writer_lock; +const Mutex& get_writer_lock(); + +constexpr std::uint8_t MIN_STIM_PID=0x00; +constexpr std::uint8_t MAX_STIM_PID=0xBF; + +struct DAQListType { + +}; + +///// From BlueParrot. + +using XcpDaq_ODTEntryIntegerType = std::uint16_t; +using XcpDaq_ODTIntegerType = std::uint16_t; + +typedef enum tagXcpDaq_DirectionType { + XCP_DIRECTION_NONE, + XCP_DIRECTION_DAQ, + XCP_DIRECTION_STIM, + XCP_DIRECTION_DAQ_STIM +} XcpDaq_DirectionType; + +//////////////// C++ style //////////////// + +struct OdtEntryType { + + void clear() { + address=0; + address_extension=0; + bitOffset=0; + entry_size=0; + } + + std::uint32_t address; + std::uint16_t address_extension; + std::uint16_t bitOffset; + std::uint32_t entry_size; +}; + +struct OdtType { + XcpDaq_ODTEntryIntegerType numOdtEntries; + std::uint16_t firstOdtEntry; + + void clear() { + m_entries.resize(0); + } + + void resize(std::uint16_t n) { + m_entries.resize(n); + } + + std::vector m_entries; +}; + +struct DynamicListType { + + void clear() { + numOdts=0; + firstOdt=0; + mode=0; + prescaler=0; + counter=0; + m_odts.resize(0); + } + + void resize(std::size_t n) { + m_odts.resize(n); + } + + XcpDaq_ODTIntegerType numOdts; + std::uint16_t firstOdt; + std::uint16_t mode; +//#if XCP_DAQ_ENABLE_PRESCALER == XCP_ON + std::uint16_t prescaler; + std::uint16_t counter; + + std::vector m_odts{}; + +// #endif /* XCP_DAQ_ENABLE_PRESCALER */ +}; + +//////////////// C++ style //////////////// + +typedef struct tagXcpDaq_ListConfigurationType { + XcpDaq_ODTIntegerType numOdts; + std::uint16_t firstOdt; +} XcpDaq_ListConfigurationType; + +typedef struct tagXcpDaq_ListStateType { + std::uint16_t mode; +#if XCP_DAQ_ENABLE_PRESCALER == XCP_ON + std::uint16_t prescaler; + std::uint16_t counter; +#endif /* XCP_DAQ_ENABLE_PRESCALER */ +} XcpDaq_ListStateType; + +typedef enum tagXcpDaq_EntityKindType { + XCP_ENTITY_UNUSED, + XCP_ENTITY_DAQ_LIST, + XCP_ENTITY_ODT, + XCP_ENTITY_ODT_ENTRY +} XcpDaq_EntityKindType; + +typedef struct tagXcpDaq_EventType { + std::uint8_t const *const name; + std::uint8_t nameLen; + std::uint8_t properties; + std::uint8_t timeunit; + std::uint8_t cycle; + /* unit8_t priority; */ +} XcpDaq_EventType; + +typedef struct tagXcpDaq_MessageType { + std::uint8_t dlc; + std::uint8_t const *data; +} XcpDaq_MessageType; + +///// + +struct StimParameters { + std::byte max_dto; +}; + +struct DaqEventInfo { + + explicit DaqEventInfo(std::string_view name, std::int8_t unit_exp, std::size_t cycle, + std::size_t maxDaqList, std::size_t priority, std::string_view consistency, bool daq, bool stim, bool packed) : + m_name(name), m_unit_exp(unit_exp), m_cycle(cycle), m_maxDaqList(maxDaqList), + m_priority(priority), m_consistency(consistency), m_daq(daq), m_stim(stim), m_packed(packed) { + if (cycle == 0) { + m_periodic = false; + m_cycle_time = 0.0; + } else { + m_periodic = true; + m_cycle_time = cycle * std::pow(10, unit_exp); + } + + std::cout << "Event: " << name << " Zaikel: " << m_cycle_time << " periodic? " << m_periodic << std::endl; + } + +public: + std::string m_name{}; + std::int8_t m_unit_exp; + std::size_t m_cycle; + std::size_t m_maxDaqList; + std::size_t m_priority; + std::string m_consistency{}; + bool m_daq; + bool m_stim; + bool m_packed; + bool m_periodic; + double m_cycle_time; +}; + +enum class EventChannelTimeUnit : std::uint8_t { + EVENT_CHANNEL_TIME_UNIT_1NS=0, + EVENT_CHANNEL_TIME_UNIT_10NS=1, + EVENT_CHANNEL_TIME_UNIT_100NS=2, + EVENT_CHANNEL_TIME_UNIT_1US=3, + EVENT_CHANNEL_TIME_UNIT_10US=4, + EVENT_CHANNEL_TIME_UNIT_100US=5, + EVENT_CHANNEL_TIME_UNIT_1MS=6, + EVENT_CHANNEL_TIME_UNIT_10MS=7, + EVENT_CHANNEL_TIME_UNIT_100MS=8, + EVENT_CHANNEL_TIME_UNIT_1S=9, + EVENT_CHANNEL_TIME_UNIT_1PS=10, + EVENT_CHANNEL_TIME_UNIT_10PS=11, + EVENT_CHANNEL_TIME_UNIT_100PS=12, +}; + +enum class DAQ_TIMESTAMP_UNIT_TO_EXP : std::int8_t { + DAQ_TIMESTAMP_UNIT_1PS= -12, + DAQ_TIMESTAMP_UNIT_10PS= -11, + DAQ_TIMESTAMP_UNIT_100PS= -10, + DAQ_TIMESTAMP_UNIT_1NS= -9, + DAQ_TIMESTAMP_UNIT_10NS= -8, + DAQ_TIMESTAMP_UNIT_100NS= -7, + DAQ_TIMESTAMP_UNIT_1US= -6, + DAQ_TIMESTAMP_UNIT_10US= -5, + DAQ_TIMESTAMP_UNIT_100US= -4, + DAQ_TIMESTAMP_UNIT_1MS= -3, + DAQ_TIMESTAMP_UNIT_10MS=-2, + DAQ_TIMESTAMP_UNIT_100MS= -1, + DAQ_TIMESTAMP_UNIT_1S = 0, +}; + +#if 0 + DAQ_TIMESTAMP_UNIT_1NS=0b0000, + DAQ_TIMESTAMP_UNIT_10NS=0b0001, + DAQ_TIMESTAMP_UNIT_100NS=0b0010, + DAQ_TIMESTAMP_UNIT_1US=0b0011, + DAQ_TIMESTAMP_UNIT_10US=0b0100, + DAQ_TIMESTAMP_UNIT_100US=0b0101, + DAQ_TIMESTAMP_UNIT_1MS=0b0110, + DAQ_TIMESTAMP_UNIT_10MS=0b0111, + DAQ_TIMESTAMP_UNIT_100MS=0b1000, + DAQ_TIMESTAMP_UNIT_1S=0b1001, + DAQ_TIMESTAMP_UNIT_1PS=0b1010, + DAQ_TIMESTAMP_UNIT_10PS=0b1011, + DAQ_TIMESTAMP_UNIT_100PS=0b1100, +////////////// +////////////// +////////////// + EVENT_CHANNEL_TIME_UNIT_1NS=0, + EVENT_CHANNEL_TIME_UNIT_10NS=1, + EVENT_CHANNEL_TIME_UNIT_100NS=2, + EVENT_CHANNEL_TIME_UNIT_1US=3, + EVENT_CHANNEL_TIME_UNIT_10US=4, + EVENT_CHANNEL_TIME_UNIT_100US=5, + EVENT_CHANNEL_TIME_UNIT_1MS=6, + EVENT_CHANNEL_TIME_UNIT_10MS=7, + EVENT_CHANNEL_TIME_UNIT_100MS=8, + EVENT_CHANNEL_TIME_UNIT_1S=9, + EVENT_CHANNEL_TIME_UNIT_1PS=10, + EVENT_CHANNEL_TIME_UNIT_10PS=11, + EVENT_CHANNEL_TIME_UNIT_100PS=12, +#endif + +class Stim { +public: + const std::uint8_t DIRECTION_STIM = 0x02; + using event_info_t = std::vector; + + explicit Stim() { + if (timeBeginPeriod(100) == TIMERR_NOERROR) { + std::cout << "timeBeginPeriod() OK!!!" << std::endl; + } else { + std::cout << "timeBeginPeriod() failed!!!" << std::endl; + } + + DWORD task_index=0UL; + + auto xxx = AvSetMmThreadCharacteristics("Pro Audio", &task_index); + std::cout << "AvSetMmThreadCharacteristics() " << xxx << ":" << task_index << std::endl; + + auto start = timeGetTime(); + //Sleep(1650); + //std::cout << "Elapsed: " << timeGetTime() - start; + } + + void setParameters(const StimParameters& params) { + m_params = params; + } + + void setDaqEventInfo(const event_info_t& daq_event_info) { + m_daq_event_info = daq_event_info; + std::size_t idx=0; + + for (const auto& event: daq_event_info) { + if (event.m_stim) { + m_stim_events.emplace(idx); + std::cout << "\tSTIM: " << event.m_name << ":" << idx << std::endl; + } + idx++; + } + } + + void setDaqPtr(std::uint16_t daqListNumber, std::uint16_t odtNumber, std::uint16_t odtEntryNumber) { + m_daq_ptr = {daqListNumber, odtNumber, odtEntryNumber}; + std::cout << "SET_DAQ_PTR " << daqListNumber << ":" << odtNumber << ":" << odtEntryNumber << std::endl; + } + + void clearDaqList(std::uint16_t daqListNumber) { + auto entry = m_daq_lists[daqListNumber]; + std::cout << "CLEAR_DAQ_LIST " << daqListNumber << std::endl; + entry.clear(); + } + + void writeDaq(std::uint16_t bitOffset, std::uint16_t entrySize, std::uint16_t addressExt, std::uint32_t address) { + auto [d, o, e] = m_daq_ptr; + auto entry = m_daq_lists[d].m_odts[o].m_entries[e]; + + std::cout << "WRITE_DAQ " << bitOffset << ":" << entrySize << ":" << addressExt << ":" << address << std::endl; + + entry.bitOffset = bitOffset; + entry.address=address; + entry.address_extension=addressExt; + entry.entry_size=entrySize; + + std::cout << "\tBO: " << entry.bitOffset << std::endl; + std::cout << "\tES: " << entry.entry_size << std::endl; + std::cout << "\tAD: " << entry.address << std::endl; + std::cout << "\tAE: " << entry.address_extension << std::endl; + } + + void setDaqListMode(std::uint16_t mode,std::uint16_t daqListNumber,std::uint16_t eventChannelNumber,std::uint16_t prescaler,std::uint16_t priority) { + + std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" << priority << std::endl; + + if ((mode & DIRECTION_STIM) == DIRECTION_STIM) { + std::cout << "\tSTIM-MODE!!!\n"; + // TODO: Calculate timebase on the fly. + auto res = std::gcd(47, 11); + calculate_scheduler_period(4177); + } + } + + void startStopDaqList(std::uint16_t mode,std::uint16_t daqListNumber) { + std::cout << "START_STOP_DAQ_LIST " << mode << ":" << daqListNumber << std::endl; + } + + void startStopSynch(std::uint16_t mode) { + + } + + void writeDaqMultiple(/* const std::vector&*/std::uint16_t daqElements) { + + } + + void dtoCtrProperties(std::uint16_t modifier,std::uint16_t eventChannel,std::uint16_t relatedEventChannel,std::uint16_t mode) { + + } + + void setDaqPackedMode(std::uint16_t, std::uint16_t daqListNumber, std::uint16_t daqPackedMode, std::uint16_t dpmTimestampMode=0, std::uint16_t dpmSampleCount=0) { + // PACKED_MODE + } + + void clear() { + m_daq_lists.clear(); + m_daq_event_info.clear(); + m_stim_events.clear(); + } + + void freeDaq() { + std::cout << "FREE_DAQ\n"; + clear(); + } + + void allocDaq(std::uint16_t daqCount) { + m_daq_lists.resize(daqCount); + std::cout << "ALLOC_DAQ " << daqCount << std::endl; + std::for_each(m_daq_lists.cbegin(), m_daq_lists.cend(), [](auto elem) { + elem.clear(); + }); + } + + void allocOdt(std::uint16_t daqListNumber, std::uint16_t odtCount) { + std::cout << "ALLOC_ODT " << daqListNumber << ":" << odtCount << std::endl; + m_daq_lists[daqListNumber].resize(odtCount); + } + + void allocOdtEntry(std::uint16_t daqListNumber,std::uint16_t odtNumber,std::uint16_t odtEntriesCount) { + std::cout << "ALLOC_ODT_ENTRY " << daqListNumber << ":" << odtNumber << ":" << odtEntriesCount << std::endl; + m_daq_lists[daqListNumber].m_odts[odtNumber].resize(odtEntriesCount); + } +protected: + + void calculate_scheduler_period(std::size_t value) { + if (!m_scheduler_period) { + *m_scheduler_period = value; + } + *m_scheduler_period = std::gcd(*m_scheduler_period, value); + } + +private: + StimParameters m_params{}; + std::vector m_daq_lists{}; + std::tuple m_daq_ptr; + std::optional m_scheduler_period{std::nullopt}; + event_info_t m_daq_event_info; + std::set m_stim_events {}; +}; + +#endif // __STIM_HPP diff --git a/pyxcp/stim/stim_wrapper.cpp b/pyxcp/stim/stim_wrapper.cpp new file mode 100644 index 0000000..19ac1da --- /dev/null +++ b/pyxcp/stim/stim_wrapper.cpp @@ -0,0 +1,57 @@ +#include +#include +#include + +#include + +namespace py = pybind11; +using namespace py::literals; + +#pragma warning(disable: 4251 4273) + +#include "stim.hpp" + +void print_dict(const py::dict& dict) { +/* Easily interact with Python types */ + for (auto item : dict) { + std::cout << "key=" << std::string(py::str(item.first)) << ", " << "value=" << std::string(py::str(item.second)) << std::endl; + } +} + +PYBIND11_MODULE(stim, m) { + //m.def("parse", &parse, "A2LParser"); + + m.def("print_dict", &print_dict); + + m.attr("writer_lock") = &_writer_lock; + + py::class_(m, "DaqEventInfo") + .def(py::init()) + ; + + py::class_(m, "Stim") + .def(py::init<>()) + .def("setDaqEventInfo", &Stim::setDaqEventInfo) + .def("clear", &Stim::clear) + .def("freeDaq", &Stim::freeDaq) + .def("allocDaq", &Stim::allocDaq) + .def("allocOdt", &Stim::allocOdt) + .def("allocOdtEntry", &Stim::allocOdtEntry) + .def("setDaqPtr", &Stim::setDaqPtr) + .def("clearDaqList", &Stim::clearDaqList) + .def("writeDaq", &Stim::writeDaq) + .def("setDaqListMode", &Stim::setDaqListMode) + .def("startStopDaqList", &Stim::startStopDaqList) + + ; + + py::class_ (m, "Mutex") + .def("__enter__", [&] (Mutex& self) { + self.lock(); + }) + .def("__exit__", + [&] (Mutex& self, const std::optional& exc_type, const std::optional& exc_value, const std::optional& traceback) { + self.unlock(); + }) + ; +} diff --git a/pyxcp/types.py b/pyxcp/types.py index 26e3678..60b1e80 100644 --- a/pyxcp/types.py +++ b/pyxcp/types.py @@ -925,6 +925,21 @@ class Event(enum.IntEnum): "DAQ_TIMESTAMP_UNIT_1S": 0, } +EVENT_CHANNEL_TIME_UNIT_TO_EXP = { + "EVENT_CHANNEL_TIME_UNIT_1PS": -12, + "EVENT_CHANNEL_TIME_UNIT_10PS": -11, + "EVENT_CHANNEL_TIME_UNIT_100PS": -10, + "EVENT_CHANNEL_TIME_UNIT_1NS": -9, + "EVENT_CHANNEL_TIME_UNIT_10NS": -8, + "EVENT_CHANNEL_TIME_UNIT_100NS": -7, + "EVENT_CHANNEL_TIME_UNIT_1US": -6, + "EVENT_CHANNEL_TIME_UNIT_10US": -5, + "EVENT_CHANNEL_TIME_UNIT_100US": -4, + "EVENT_CHANNEL_TIME_UNIT_1MS": -3, + "EVENT_CHANNEL_TIME_UNIT_10MS": -2, + "EVENT_CHANNEL_TIME_UNIT_100MS": -1, + "EVENT_CHANNEL_TIME_UNIT_1S": 0, +} class XcpGetSeedMode(enum.IntEnum): FIRST_PART = 0 From 308b647a4333ba69688d0fbea8316b29b09fabd1 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 22 Aug 2023 07:46:17 +0200 Subject: [PATCH 05/99] Today() --- pyxcp/master/master.py | 19 +++-- pyxcp/stim/scheduler.cpp | 34 ++++++++ pyxcp/stim/scheduler.hpp | 66 +++++++++++++++ pyxcp/stim/stim.cpp | 11 ++- pyxcp/stim/stim.hpp | 161 +++++++++++++++++++++++++----------- pyxcp/stim/stim_wrapper.cpp | 22 ++--- pyxcp/transport/base.py | 33 +++++--- 7 files changed, 270 insertions(+), 76 deletions(-) create mode 100644 pyxcp/stim/scheduler.cpp create mode 100644 pyxcp/stim/scheduler.hpp diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 854e280..96e0fcd 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -41,7 +41,13 @@ from pyxcp.utils import decode_bytes from pyxcp.utils import delay from pyxcp.utils import SHORT_SLEEP +<<<<<<< HEAD +||||||| parent of cb36baf (Today()) +from .stim import Stim, DaqEventInfo +======= +from .stim import Stim, DaqEventInfo, get_writer_lock, get_policy_lock +>>>>>>> cb36baf (Today()) def broadcasted(func: Callable): """""" @@ -100,6 +106,12 @@ def __init__(self, transportName, config=None, policy=None): self.transport = createTransport(transportName, config, policy) self.transport_name = transportName + self.transport.set_writer_lock(get_writer_lock()) + self.transport.set_policy_lock(get_policy_lock()) + self.stim = Stim() + self.stim.clear() + self.stim.set_policy_feeder(self.transport.policy.feed) + self.stim.set_frame_sender(self.transport.send) # In some cases the transport-layer needs to communicate with us. self.transport.parent = self @@ -126,8 +138,6 @@ def __init__(self, transportName, config=None, policy=None): self.disconnect_response_optional = self.config.get("DISCONNECT_RESPONSE_OPTIONAL") self.slaveProperties = SlaveProperties() self.slaveProperties.pgmProcessor = SlaveProperties() - self.stim = Stim() - self.stim.clear() def __enter__(self): """Context manager entry part.""" @@ -1732,10 +1742,7 @@ def getDaqInfo(self): "stim": stim_supported, "packed": packed_supported, }, - } - print( - "EC-U", "unit", time_unit, types.EVENT_CHANNEL_TIME_UNIT_TO_EXP[time_unit], eci["daqEventProperties"]["consistency"] - ) + }git ns daq_event_info = DaqEventInfo( name, types.EVENT_CHANNEL_TIME_UNIT_TO_EXP[time_unit], diff --git a/pyxcp/stim/scheduler.cpp b/pyxcp/stim/scheduler.cpp new file mode 100644 index 0000000..5e5f6a2 --- /dev/null +++ b/pyxcp/stim/scheduler.cpp @@ -0,0 +1,34 @@ + +#include "scheduler.hpp" + +VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) +{ +if (lpParam == NULL) +{ +printf("TimerRoutine lpParam is NULL\n"); +} +else +{ +// lpParam points to the argument; in this case it is an int + +printf("Timer routine called. Parameter is %d.\n", +*(int*)lpParam); +if(TimerOrWaitFired) +{ +printf("The wait timed out.\n"); +} +else +{ +printf("The wait event was signaled.\n"); +} +} +} + +#include + +void mul4_vectorized( float* ptr ) +{ + __m128 f = _mm_loadu_ps( ptr ); + f = _mm_mul_ps( f, f ); + _mm_storeu_ps( ptr, f ); +} \ No newline at end of file diff --git a/pyxcp/stim/scheduler.hpp b/pyxcp/stim/scheduler.hpp new file mode 100644 index 0000000..2cdb6cd --- /dev/null +++ b/pyxcp/stim/scheduler.hpp @@ -0,0 +1,66 @@ + + +#ifndef STIM_SCHEDULER_HPP +#define STIM_SCHEDULER_HPP + +#define _CRT_SECURE_NO_WARNINGS (1) + +#include + +#include +#include + +VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired); + +struct Scheduler { + + Scheduler() = default; + ~Scheduler() = default; + + bool start_thread() noexcept { + if (timer_thread.joinable()) { + return false; + } + + m_TimerQueue = CreateTimerQueue(); + if (NULL == m_TimerQueue) { + printf("CreateTimerQueue failed (%d)\n", GetLastError()); + return false; + } + + // Set a timer to call the timer routine in 10 seconds. + if (!CreateTimerQueueTimer( &m_timer, m_TimerQueue, + (WAITORTIMERCALLBACK)TimerRoutine, nullptr , 1, 500, 0)) + { + printf("CreateTimerQueueTimer failed (%d)\n", GetLastError()); + return false; + } + + stop_timer_thread_flag = false; + timer_thread = std::jthread([this]() { + while (!stop_timer_thread_flag) { + printf("ENTER SLEEP loop!!!\n"); + SleepEx(INFINITE,TRUE ); + stop_timer_thread_flag=TRUE; + } + }); + return true; + } + + bool stop_thread() noexcept { + if (!timer_thread.joinable()) { + return false; + } + stop_timer_thread_flag = true; + //my_queue.put(std::nullopt); + timer_thread.join(); + return true; + } + + std::jthread timer_thread{}; + bool stop_timer_thread_flag{}; + HANDLE m_timer{}; + HANDLE m_TimerQueue; +}; + +#endif //STIM_SCHEDULER_HPP diff --git a/pyxcp/stim/stim.cpp b/pyxcp/stim/stim.cpp index 8a0638d..0f7db6d 100644 --- a/pyxcp/stim/stim.cpp +++ b/pyxcp/stim/stim.cpp @@ -13,8 +13,13 @@ void init() { } -Mutex _writer_lock{}; +static Mutex writer_lock{}; +static Mutex policy_lock{}; -const Mutex& get_writer_lock() { - return _writer_lock; +Mutex& get_writer_lock() { + return writer_lock; +} + +Mutex& get_policy_lock() { + return policy_lock; } diff --git a/pyxcp/stim/stim.hpp b/pyxcp/stim/stim.hpp index 6edbe5e..d656ea4 100644 --- a/pyxcp/stim/stim.hpp +++ b/pyxcp/stim/stim.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -18,6 +19,10 @@ #include #include +#include "scheduler.hpp" + +constexpr double TMR_RESOLUTION = 1.0 / 1000.0; // Timer resolution is one millisecond. + class Timer { public: @@ -80,6 +85,8 @@ class TsQueue { }; struct Mutex { + ~Mutex() {} + void lock() { m_mtx.lock(); } @@ -90,8 +97,8 @@ struct Mutex { std::mutex m_mtx{}; }; -extern Mutex _writer_lock; -const Mutex& get_writer_lock(); +Mutex& get_writer_lock(); +Mutex& get_policy_lock(); constexpr std::uint8_t MIN_STIM_PID=0x00; constexpr std::uint8_t MAX_STIM_PID=0xBF; @@ -152,6 +159,8 @@ struct DynamicListType { mode=0; prescaler=0; counter=0; + event_channel_number=0; + priority=0; m_odts.resize(0); } @@ -162,13 +171,13 @@ struct DynamicListType { XcpDaq_ODTIntegerType numOdts; std::uint16_t firstOdt; std::uint16_t mode; -//#if XCP_DAQ_ENABLE_PRESCALER == XCP_ON + std::uint16_t prescaler; + std::uint16_t event_channel_number; std::uint16_t counter; + std::uint16_t priority; std::vector m_odts{}; - -// #endif /* XCP_DAQ_ENABLE_PRESCALER */ }; //////////////// C++ style //////////////// @@ -215,23 +224,28 @@ struct StimParameters { struct DaqEventInfo { - explicit DaqEventInfo(std::string_view name, std::int8_t unit_exp, std::size_t cycle, + explicit DaqEventInfo(/*std::string_view*/const std::string& name, std::int8_t unit_exp, std::size_t cycle, std::size_t maxDaqList, std::size_t priority, std::string_view consistency, bool daq, bool stim, bool packed) : m_name(name), m_unit_exp(unit_exp), m_cycle(cycle), m_maxDaqList(maxDaqList), m_priority(priority), m_consistency(consistency), m_daq(daq), m_stim(stim), m_packed(packed) { if (cycle == 0) { m_periodic = false; - m_cycle_time = 0.0; + m_cycle_time = 0; } else { m_periodic = true; - m_cycle_time = cycle * std::pow(10, unit_exp); + auto cycle_time = cycle * std::pow(10, unit_exp); + if (cycle_time < TMR_RESOLUTION) { + cycle_time = TMR_RESOLUTION; + } + m_cycle_time = static_cast(cycle_time * 1000.0); + std::cout << "\tTMR_TMP: " << m_cycle_time << std::endl; } - std::cout << "Event: " << name << " Zaikel: " << m_cycle_time << " periodic? " << m_periodic << std::endl; + std::cout << "Event: " << m_name << " Zaikel: " << m_cycle_time << "ms - periodic? " << m_periodic << std::endl; } public: - std::string m_name{}; + std::string m_name; std::int8_t m_unit_exp; std::size_t m_cycle; std::size_t m_maxDaqList; @@ -241,7 +255,8 @@ struct DaqEventInfo { bool m_stim; bool m_packed; bool m_periodic; - double m_cycle_time; + std::size_t m_cycle_time; + std::set m_daq_lists{}; }; enum class EventChannelTimeUnit : std::uint8_t { @@ -276,42 +291,14 @@ enum class DAQ_TIMESTAMP_UNIT_TO_EXP : std::int8_t { DAQ_TIMESTAMP_UNIT_1S = 0, }; -#if 0 - DAQ_TIMESTAMP_UNIT_1NS=0b0000, - DAQ_TIMESTAMP_UNIT_10NS=0b0001, - DAQ_TIMESTAMP_UNIT_100NS=0b0010, - DAQ_TIMESTAMP_UNIT_1US=0b0011, - DAQ_TIMESTAMP_UNIT_10US=0b0100, - DAQ_TIMESTAMP_UNIT_100US=0b0101, - DAQ_TIMESTAMP_UNIT_1MS=0b0110, - DAQ_TIMESTAMP_UNIT_10MS=0b0111, - DAQ_TIMESTAMP_UNIT_100MS=0b1000, - DAQ_TIMESTAMP_UNIT_1S=0b1001, - DAQ_TIMESTAMP_UNIT_1PS=0b1010, - DAQ_TIMESTAMP_UNIT_10PS=0b1011, - DAQ_TIMESTAMP_UNIT_100PS=0b1100, -////////////// -////////////// -////////////// - EVENT_CHANNEL_TIME_UNIT_1NS=0, - EVENT_CHANNEL_TIME_UNIT_10NS=1, - EVENT_CHANNEL_TIME_UNIT_100NS=2, - EVENT_CHANNEL_TIME_UNIT_1US=3, - EVENT_CHANNEL_TIME_UNIT_10US=4, - EVENT_CHANNEL_TIME_UNIT_100US=5, - EVENT_CHANNEL_TIME_UNIT_1MS=6, - EVENT_CHANNEL_TIME_UNIT_10MS=7, - EVENT_CHANNEL_TIME_UNIT_100MS=8, - EVENT_CHANNEL_TIME_UNIT_1S=9, - EVENT_CHANNEL_TIME_UNIT_1PS=10, - EVENT_CHANNEL_TIME_UNIT_10PS=11, - EVENT_CHANNEL_TIME_UNIT_100PS=12, -#endif +void sched_init(); // TODO: Incl. class Stim { public: const std::uint8_t DIRECTION_STIM = 0x02; using event_info_t = std::vector; + using feed_function_t = std::function)>; + using send_function_t = std::function)>; explicit Stim() { if (timeBeginPeriod(100) == TIMERR_NOERROR) { @@ -326,6 +313,7 @@ class Stim { std::cout << "AvSetMmThreadCharacteristics() " << xxx << ":" << task_index << std::endl; auto start = timeGetTime(); + m_scheduler.start_thread(); //Sleep(1650); //std::cout << "Elapsed: " << timeGetTime() - start; } @@ -348,11 +336,17 @@ class Stim { } void setDaqPtr(std::uint16_t daqListNumber, std::uint16_t odtNumber, std::uint16_t odtEntryNumber) { + if (!validateEntryNumber(daqListNumber, odtNumber, odtEntryNumber)) { + return; + } m_daq_ptr = {daqListNumber, odtNumber, odtEntryNumber}; std::cout << "SET_DAQ_PTR " << daqListNumber << ":" << odtNumber << ":" << odtEntryNumber << std::endl; } void clearDaqList(std::uint16_t daqListNumber) { + if (!validateEntryNumber(daqListNumber)) { + return; + } auto entry = m_daq_lists[daqListNumber]; std::cout << "CLEAR_DAQ_LIST " << daqListNumber << std::endl; entry.clear(); @@ -375,19 +369,36 @@ class Stim { std::cout << "\tAE: " << entry.address_extension << std::endl; } - void setDaqListMode(std::uint16_t mode,std::uint16_t daqListNumber,std::uint16_t eventChannelNumber,std::uint16_t prescaler,std::uint16_t priority) { + void setDaqListMode(std::uint16_t mode,std::uint16_t daqListNumber, std::uint16_t eventChannelNumber, + std::uint16_t prescaler, std::uint16_t priority) { + if (!validateEntryNumber(daqListNumber)) { + return; + } + auto entry = m_daq_lists[daqListNumber]; + entry.mode = mode; + entry.prescaler = prescaler; + // The use of a prescaler is only used for DAQ lists with DIRECTION = DAQ. + + entry.priority=priority; + entry.event_channel_number=eventChannelNumber; - std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" << priority << std::endl; + auto& event = m_daq_event_info[eventChannelNumber]; + event.m_daq_lists.emplace(daqListNumber); + std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" << priority << std::endl; if ((mode & DIRECTION_STIM) == DIRECTION_STIM) { std::cout << "\tSTIM-MODE!!!\n"; // TODO: Calculate timebase on the fly. - auto res = std::gcd(47, 11); - calculate_scheduler_period(4177); + + std::cout << "\t\tEvent: " << event.m_name << " ==> " << event.m_cycle_time << " - " << event.m_periodic << std::endl; + calculate_scheduler_period(event.m_cycle_time); } } void startStopDaqList(std::uint16_t mode,std::uint16_t daqListNumber) { + if (!validateEntryNumber(daqListNumber)) { + return; + } std::cout << "START_STOP_DAQ_LIST " << mode << ":" << daqListNumber << std::endl; } @@ -404,6 +415,9 @@ class Stim { } void setDaqPackedMode(std::uint16_t, std::uint16_t daqListNumber, std::uint16_t daqPackedMode, std::uint16_t dpmTimestampMode=0, std::uint16_t dpmSampleCount=0) { + if (!validateEntryNumber(daqListNumber)) { + return; + } // PACKED_MODE } @@ -427,21 +441,71 @@ class Stim { } void allocOdt(std::uint16_t daqListNumber, std::uint16_t odtCount) { + if (!validateEntryNumber(daqListNumber)) { + return; + } std::cout << "ALLOC_ODT " << daqListNumber << ":" << odtCount << std::endl; m_daq_lists[daqListNumber].resize(odtCount); } void allocOdtEntry(std::uint16_t daqListNumber,std::uint16_t odtNumber,std::uint16_t odtEntriesCount) { + if (!validateEntryNumber(daqListNumber, odtNumber)) { + return; + } std::cout << "ALLOC_ODT_ENTRY " << daqListNumber << ":" << odtNumber << ":" << odtEntriesCount << std::endl; m_daq_lists[daqListNumber].m_odts[odtNumber].resize(odtEntriesCount); } + + void set_policy_feeder(const feed_function_t& fun) { + m_feed_function = fun; + } + + void set_frame_sender(const send_function_t& fun) { + m_send_function = fun; + } + + void send(const std::vector frame) { + if (m_send_function) { + std::scoped_lock _ {get_writer_lock()}; + m_send_function.value()(frame); + } + } + protected: void calculate_scheduler_period(std::size_t value) { if (!m_scheduler_period) { *m_scheduler_period = value; } + if (!m_scheduler_max_value) { + *m_scheduler_max_value = value; + } + std::cout << "SCHED_Value: " << value << std::endl; *m_scheduler_period = std::gcd(*m_scheduler_period, value); + *m_scheduler_max_value = std::lcm(*m_scheduler_max_value, value); + std::cout << "SCHED_Period: " << *m_scheduler_period << " max: " << *m_scheduler_max_value << std::endl; + } + + bool validateEntryNumber(std::uint16_t daqListNumber, std::optional odtNumber=std::nullopt, + std::optional odtEntryNumber=std::nullopt) const { + + if (daqListNumber >= std::size(m_daq_lists)) { + return false; + } + if (odtNumber) { + auto entry = m_daq_lists[daqListNumber]; + if (*odtNumber >= std::size(entry.m_odts)) { + return false; + } + } + if (odtEntryNumber) { + auto entry = m_daq_lists[daqListNumber].m_odts[*odtNumber]; + if (*odtEntryNumber >= std::size(entry.m_entries)) { + return false; + } + } + std::cout << "\tOK, number is valid!!!\n"; + return true; } private: @@ -449,8 +513,13 @@ class Stim { std::vector m_daq_lists{}; std::tuple m_daq_ptr; std::optional m_scheduler_period{std::nullopt}; + std::optional m_scheduler_max_value{std::nullopt}; event_info_t m_daq_event_info; + //std::map> m_event_mapping{}; std::set m_stim_events {}; + std::optional m_feed_function {std::nullopt}; + std::optional m_send_function {std::nullopt}; + Scheduler m_scheduler{}; }; #endif // __STIM_HPP diff --git a/pyxcp/stim/stim_wrapper.cpp b/pyxcp/stim/stim_wrapper.cpp index 19ac1da..5bcbbea 100644 --- a/pyxcp/stim/stim_wrapper.cpp +++ b/pyxcp/stim/stim_wrapper.cpp @@ -11,22 +11,14 @@ using namespace py::literals; #include "stim.hpp" -void print_dict(const py::dict& dict) { -/* Easily interact with Python types */ - for (auto item : dict) { - std::cout << "key=" << std::string(py::str(item.first)) << ", " << "value=" << std::string(py::str(item.second)) << std::endl; - } -} PYBIND11_MODULE(stim, m) { - //m.def("parse", &parse, "A2LParser"); - - m.def("print_dict", &print_dict); - m.attr("writer_lock") = &_writer_lock; + m.def("get_writer_lock", &get_writer_lock, py::return_value_policy::reference); + m.def("get_policy_lock", &get_policy_lock, py::return_value_policy::reference); py::class_(m, "DaqEventInfo") - .def(py::init()) + .def(py::init()) ; py::class_(m, "Stim") @@ -43,6 +35,14 @@ PYBIND11_MODULE(stim, m) { .def("setDaqListMode", &Stim::setDaqListMode) .def("startStopDaqList", &Stim::startStopDaqList) + .def("set_policy_feeder", [](Stim& self, const py::function& callback) { + self.set_policy_feeder(callback); + }) + + .def("set_frame_sender", [](Stim& self, const py::function& callback) { + self.set_frame_sender(callback); + }) + ; py::class_ (m, "Mutex") diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index cdcbcd0..92a59bf 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -19,7 +19,6 @@ from ..utils import SHORT_SLEEP from pyxcp.config import Configuration - class FrameAcquisitionPolicy: """ Base class for all frame acquisition policies. @@ -203,6 +202,11 @@ def __del__(self): def loadConfig(self, config): """Load configuration data.""" self.config = Configuration(self.PARAMETER_MAP or {}, config or {}) + def set_writer_lock(self, lock): + self.writer_lock = lock + + def set_policy_lock(self, lock): + self.policy_lock = lock def close(self): """Close the transport-layer connection and event-loop.""" @@ -231,8 +235,10 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): with self.command_lock: frame = self._prepare_request(cmd, *data) self.timing.start() - self.policy.feed(types.FrameCategory.CMD, self.counterSend, perf_counter(), frame) - self.send(frame) + with self.policy_lock: + self.policy.feed(types.FrameCategory.CMD, self.counterSend, perf_counter(), frame) + with self.writer_lock: + self.send(frame) try: xcpPDU = get( self.resQueue, @@ -242,7 +248,8 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): except EmptyFrameError: if not ignore_timeout: MSG = f"Response timed out (timeout={self.timeout}s)" - self.policy.feed(types.FrameCategory.METADATA, self.counterSend, perf_counter(), bytes(MSG, "ascii")) + with self.policy_lock: + self.policy.feed(types.FrameCategory.METADATA, self.counterSend, perf_counter(), bytes(MSG, "ascii")) raise types.XcpTimeoutError(MSG) from None else: self.timing.stop() @@ -250,7 +257,8 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): self.timing.stop() pid = types.Response.parse(xcpPDU).type if pid == "ERR" and cmd.name != "SYNCH": - self.policy.feed(types.FrameCategory.ERROR, self.counterReceived, perf_counter(), xcpPDU[1:]) + with self.policy_lock: + self.policy.feed(types.FrameCategory.ERROR, self.counterReceived, perf_counter(), xcpPDU[1:]) err = types.XcpError.parse(xcpPDU[1:]) raise types.XcpResponseError(err) @@ -279,7 +287,8 @@ def block_request(self, cmd, *data): raise types.XcpResponseError(err) frame = self._prepare_request(cmd, *data) - self.send(frame) + with self.writer_lock: + self.send(frame) def _prepare_request(self, cmd, *data): """ @@ -371,13 +380,16 @@ def processResponse(self, response, length, counter, recv_timestamp=None): self.logger.debug(f"<- L{length} C{counter} {hexDump(response)}") if pid >= 0xFE: self.resQueue.append(response) - self.policy.feed(types.FrameCategory.RESPONSE, self.counterReceived, perf_counter(), response) + with self.policy_lock: + self.policy.feed(types.FrameCategory.RESPONSE, self.counterReceived, perf_counter(), response) self.recv_timestamp = recv_timestamp elif pid == 0xFD: self.process_event_packet(response) - self.policy.feed(types.FrameCategory.EVENT, self.counterReceived, perf_counter(), response) + with self.policy_lock: + self.policy.feed(types.FrameCategory.EVENT, self.counterReceived, perf_counter(), response) elif pid == 0xFC: - self.policy.feed(types.FrameCategory.SERV, self.counterReceived, perf_counter(), response) + with self.policy_lock: + self.policy.feed(types.FrameCategory.SERV, self.counterReceived, perf_counter(), response) else: if self._debug: self.logger.debug(f"<- L{length} C{counter} ODT_Data[0:8] {hexDump(response[:8])}") @@ -387,7 +399,8 @@ def processResponse(self, response, length, counter, recv_timestamp=None): timestamp = recv_timestamp else: timestamp = 0.0 - self.policy.feed(types.FrameCategory.DAQ, self.counterReceived, timestamp, response) + with self.policy_lock: + self.policy.feed(types.FrameCategory.DAQ, self.counterReceived, timestamp, response) def createTransport(name, *args, **kws): From fce9d23bb000b0e41498686a754b2830fb0acdae Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 22 Aug 2023 07:48:22 +0200 Subject: [PATCH 06/99] Add .clang-format --- .clang-format | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8ed8ff6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,208 @@ +--- +Language: Cpp +AccessModifierOffset: -1 +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: Left +AlignConsecutiveMacros: true +# Enabled: true +# AcrossEmptyLines: true +# AcrossComments: true +AlignConsecutiveAssignments: true +# Enabled: true +# AcrossEmptyLines: true +# AcrossComments: true +AlignConsecutiveDeclarations: true +# Enabled: false +# AcrossEmptyLines: true +# AcrossComments: true +AlignConsecutiveBitFields: true +# Enabled: false +# AcrossEmptyLines: true +# AcrossComments: true +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +# Starting with clang-format 16: +# Kind: Always +# OverEmptyLines: 2 +# MaxEmptyLinesToKeep: 2 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +# AttributeMacros: ['__capability', '__output', '__ununsed'] # useful for language extensions or static analyzer annotations. +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: After +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +# BracedInitializerIndentWidth: 4 +# BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: true +BreakBeforeBinaryOperators: None +# BreakBeforeConceptDeclarations: Allowed +BreakBeforeInheritanceComma: false +BreakInheritanceList: AfterComma +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +BreakStringLiterals: true +ColumnLimit: 132 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +EmptyLineAfterAccessModifier: Always +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + - Regex: '.*' + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentCaseBlocks: true +IndentExternBlock: Indent +IndentGotoLabels: true +IndentPPDirectives: BeforeHash +# IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +# InsertBraces: true # be careful! +# InsertNewlineAtEOF: true # clang-format 16 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: Inner +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PPIndentWidth: 4 +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReferenceAlignment: Left +ReflowComments: true +SeparateDefinitionBlocks: Always +SortIncludes: true +SortUsingDeclarations: true + +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false + +SpaceAroundPointerQualifiers: Both + +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptControlMacros +# SpaceBeforeParensOptions -- TODO: +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpacesBeforeTrailingComments: 2 + +SpacesInAngles: Leave +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false + +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +... + From 995a2cf4694d85d72568edf9da5dcba18e13796d Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 22 Aug 2023 07:51:29 +0200 Subject: [PATCH 07/99] run pre-commit --- pyxcp/dllif.py | 1 - pyxcp/errormatrix.py | 1 + pyxcp/logger.py | 3 +- pyxcp/master/errorhandler.py | 18 +- pyxcp/master/master.py | 10 +- pyxcp/scripts/xcp_fetch_a2l.py | 7 +- pyxcp/stim/__init__.py | 0 pyxcp/stim/do.cmd | 3 + pyxcp/stim/main.cpp | 7 + pyxcp/stim/run.cmd | 2 + pyxcp/stim/scheduler.cpp | 43 ++- pyxcp/stim/scheduler.hpp | 31 +-- pyxcp/stim/setup.py | 40 +++ pyxcp/stim/stim.cpp | 3 - pyxcp/stim/stim.hpp | 353 ++++++++++++------------ pyxcp/stim/stim_wrapper.cpp | 70 +++-- pyxcp/tests/test_asam_types.py | 4 +- pyxcp/tests/test_can.py | 5 +- pyxcp/tests/test_checksum.py | 4 +- pyxcp/tests/test_config.py | 1 + pyxcp/tests/test_master.py | 4 +- pyxcp/tests/test_transport.py | 4 +- pyxcp/tests/test_utils.py | 3 +- pyxcp/timing.py | 1 - pyxcp/transport/base.py | 2 + pyxcp/transport/candriver/pc_pcan.py | 4 +- pyxcp/transport/candriver/python_can.py | 3 +- pyxcp/transport/eth.py | 1 - pyxcp/transport/sxi.py | 2 - pyxcp/transport/usb_transport.py | 3 - pyxcp/types.py | 25 +- 31 files changed, 350 insertions(+), 308 deletions(-) create mode 100644 pyxcp/stim/__init__.py create mode 100644 pyxcp/stim/do.cmd create mode 100644 pyxcp/stim/main.cpp create mode 100644 pyxcp/stim/run.cmd create mode 100644 pyxcp/stim/setup.py diff --git a/pyxcp/dllif.py b/pyxcp/dllif.py index 92584e7..3191535 100644 --- a/pyxcp/dllif.py +++ b/pyxcp/dllif.py @@ -38,7 +38,6 @@ class SeedNKeyError(Exception): def getKey(logger, dllName: str, privilege: int, seed: str, assume_same_bit_width: bool): - dllName = str(Path(dllName).absolute()) # Fix loader issues. use_ctypes = False diff --git a/pyxcp/errormatrix.py b/pyxcp/errormatrix.py index c95dcf8..aa52345 100644 --- a/pyxcp/errormatrix.py +++ b/pyxcp/errormatrix.py @@ -4,6 +4,7 @@ """ import enum from collections import namedtuple + from pyxcp.types import Command from pyxcp.types import XcpError diff --git a/pyxcp/logger.py b/pyxcp/logger.py index 4ccccc9..dda4c9e 100644 --- a/pyxcp/logger.py +++ b/pyxcp/logger.py @@ -6,7 +6,6 @@ class Logger(object): - LOGGER_BASE_NAME = "pyxcp" FORMAT = "[%(levelname)s (%(name)s)]: %(message)s" @@ -18,7 +17,7 @@ def __init__(self, name, level=logging.WARN): formatter = logging.Formatter(self.FORMAT) handler.setFormatter(formatter) self.logger.addHandler(handler) - #self.logger.propagate = False + # self.logger.propagate = False self.lastMessage = None self.lastSeverity = None diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index 57475fe..1cb790c 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -9,6 +9,7 @@ import time import types from collections import namedtuple + from pyxcp.errormatrix import Action from pyxcp.errormatrix import ERROR_MATRIX from pyxcp.errormatrix import PreAction @@ -22,6 +23,7 @@ logger = logging.getLogger("pyxcp.errorhandler") + class SingletonBase(object): _lock = threading.Lock() @@ -144,7 +146,7 @@ class Repeater: def __init__(self, initial_value: int): self._counter = initial_value - #print("\tREPEATER ctor", hex(id(self))) + # print("\tREPEATER ctor", hex(id(self))) def repeat(self): """Check if repetition is required. @@ -153,7 +155,7 @@ def repeat(self): ------- bool """ - #print("\t\tCOUNTER:", hex(id(self)), self._counter) + # print("\t\tCOUNTER:", hex(id(self)), self._counter) if self._counter == Repeater.INFINITE: return True elif self._counter > 0: @@ -198,12 +200,12 @@ def __eq__(self, other): @property def repeater(self): - #print("\tGet repeater", hex(id(self._repeater)), self._repeater is None) + # print("\tGet repeater", hex(id(self._repeater)), self._repeater is None) return self._repeater @repeater.setter def repeater(self, value): - #print("\tSet repeater", hex(id(value))) + # print("\tSet repeater", hex(id(value))) self._repeater = value def execute(self): @@ -334,12 +336,12 @@ def __call__(self, inst, func, arguments): self.arguments = arguments handler = Handler(inst, func, arguments) self.handlerStack.push(handler) - #print("\tENTER handler:", hex(id(handler))) + # print("\tENTER handler:", hex(id(handler))) try: while True: try: handler = self.handlerStack.tos() - #print("\t\tEXEC", hex(id(handler))) + # print("\t\tEXEC", hex(id(handler))) res = handler.execute() except XcpResponseError as e: self.logger.error(f"XcpResponseError [{str(e)}]") @@ -368,7 +370,9 @@ def __call__(self, inst, func, arguments): if handler.repeater.repeat(): continue else: - raise UnrecoverableError(f"Max. repetition count reached while trying to execute service '{handler.func.__name__}'.") + raise UnrecoverableError( + f"Max. repetition count reached while trying to execute service '{handler.func.__name__}'." + ) finally: # cleanup of class variables self.previous_error_code = None diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 96e0fcd..d86d908 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -21,6 +21,8 @@ from typing import Union from .stim import DaqEventInfo +from .stim import get_policy_lock +from .stim import get_writer_lock from .stim import Stim from pyxcp import checksum from pyxcp import types @@ -41,13 +43,7 @@ from pyxcp.utils import decode_bytes from pyxcp.utils import delay from pyxcp.utils import SHORT_SLEEP -<<<<<<< HEAD -||||||| parent of cb36baf (Today()) -from .stim import Stim, DaqEventInfo -======= -from .stim import Stim, DaqEventInfo, get_writer_lock, get_policy_lock ->>>>>>> cb36baf (Today()) def broadcasted(func: Callable): """""" @@ -1742,7 +1738,7 @@ def getDaqInfo(self): "stim": stim_supported, "packed": packed_supported, }, - }git ns + } daq_event_info = DaqEventInfo( name, types.EVENT_CHANNEL_TIME_UNIT_TO_EXP[time_unit], diff --git a/pyxcp/scripts/xcp_fetch_a2l.py b/pyxcp/scripts/xcp_fetch_a2l.py index 2ab6ddb..fd53649 100644 --- a/pyxcp/scripts/xcp_fetch_a2l.py +++ b/pyxcp/scripts/xcp_fetch_a2l.py @@ -1,11 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - """Fetch A2L file from XCP slave (if supported). """ - -from pathlib import Path import sys +from pathlib import Path from pyxcp.cmdline import ArgumentParser from pyxcp.types import XcpGetIdType @@ -27,9 +25,10 @@ def main(): if phile.exists(): print(f"{file_name} already exists.") sys.exit(1) - with phile.open("wt", encoding = "utf-8") as of: + with phile.open("wt", encoding="utf-8") as of: of.write(content) print(f"Created {file_name}") + if __name__ == "__main__": main() diff --git a/pyxcp/stim/__init__.py b/pyxcp/stim/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyxcp/stim/do.cmd b/pyxcp/stim/do.cmd new file mode 100644 index 0000000..cff15a3 --- /dev/null +++ b/pyxcp/stim/do.cmd @@ -0,0 +1,3 @@ +call build.cmd +cls +python .\xcphello_ext.py -c .\conf_eth_user.toml -d -l DEBUG diff --git a/pyxcp/stim/main.cpp b/pyxcp/stim/main.cpp new file mode 100644 index 0000000..3c5de18 --- /dev/null +++ b/pyxcp/stim/main.cpp @@ -0,0 +1,7 @@ +#include + +#include "stim_wrapper.hpp" + +int main() { + return 0; +} diff --git a/pyxcp/stim/run.cmd b/pyxcp/stim/run.cmd new file mode 100644 index 0000000..0bb777f --- /dev/null +++ b/pyxcp/stim/run.cmd @@ -0,0 +1,2 @@ +cls +python .\xcphello_ext.py -c .\conf_eth_user.toml -d -l DEBUG diff --git a/pyxcp/stim/scheduler.cpp b/pyxcp/stim/scheduler.cpp index 5e5f6a2..108bb71 100644 --- a/pyxcp/stim/scheduler.cpp +++ b/pyxcp/stim/scheduler.cpp @@ -1,34 +1,25 @@ #include "scheduler.hpp" -VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) -{ -if (lpParam == NULL) -{ -printf("TimerRoutine lpParam is NULL\n"); -} -else -{ -// lpParam points to the argument; in this case it is an int +VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) { + if (lpParam == NULL) { + printf("TimerRoutine lpParam is NULL\n"); + } else { + // lpParam points to the argument; in this case it is an int -printf("Timer routine called. Parameter is %d.\n", -*(int*)lpParam); -if(TimerOrWaitFired) -{ -printf("The wait timed out.\n"); -} -else -{ -printf("The wait event was signaled.\n"); -} -} + printf("Timer routine called. Parameter is %d.\n", *(int*)lpParam); + if (TimerOrWaitFired) { + printf("The wait timed out.\n"); + } else { + printf("The wait event was signaled.\n"); + } + } } #include -void mul4_vectorized( float* ptr ) -{ - __m128 f = _mm_loadu_ps( ptr ); - f = _mm_mul_ps( f, f ); - _mm_storeu_ps( ptr, f ); -} \ No newline at end of file +void mul4_vectorized(float* ptr) { + __m128 f = _mm_loadu_ps(ptr); + f = _mm_mul_ps(f, f); + _mm_storeu_ps(ptr, f); +} diff --git a/pyxcp/stim/scheduler.hpp b/pyxcp/stim/scheduler.hpp index 2cdb6cd..b0bdbcb 100644 --- a/pyxcp/stim/scheduler.hpp +++ b/pyxcp/stim/scheduler.hpp @@ -3,18 +3,17 @@ #ifndef STIM_SCHEDULER_HPP #define STIM_SCHEDULER_HPP -#define _CRT_SECURE_NO_WARNINGS (1) +#define _CRT_SECURE_NO_WARNINGS (1) -#include - -#include #include +#include + +#include VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired); struct Scheduler { - - Scheduler() = default; + Scheduler() = default; ~Scheduler() = default; bool start_thread() noexcept { @@ -29,19 +28,17 @@ struct Scheduler { } // Set a timer to call the timer routine in 10 seconds. - if (!CreateTimerQueueTimer( &m_timer, m_TimerQueue, - (WAITORTIMERCALLBACK)TimerRoutine, nullptr , 1, 500, 0)) - { + if (!CreateTimerQueueTimer(&m_timer, m_TimerQueue, (WAITORTIMERCALLBACK)TimerRoutine, nullptr, 1, 500, 0)) { printf("CreateTimerQueueTimer failed (%d)\n", GetLastError()); return false; } stop_timer_thread_flag = false; - timer_thread = std::jthread([this]() { + timer_thread = std::jthread([this]() { while (!stop_timer_thread_flag) { printf("ENTER SLEEP loop!!!\n"); - SleepEx(INFINITE,TRUE ); - stop_timer_thread_flag=TRUE; + SleepEx(INFINITE, TRUE); + stop_timer_thread_flag = TRUE; } }); return true; @@ -52,15 +49,15 @@ struct Scheduler { return false; } stop_timer_thread_flag = true; - //my_queue.put(std::nullopt); + // my_queue.put(std::nullopt); timer_thread.join(); return true; } std::jthread timer_thread{}; - bool stop_timer_thread_flag{}; - HANDLE m_timer{}; - HANDLE m_TimerQueue; + bool stop_timer_thread_flag{}; + HANDLE m_timer{}; + HANDLE m_TimerQueue; }; -#endif //STIM_SCHEDULER_HPP +#endif // STIM_SCHEDULER_HPP diff --git a/pyxcp/stim/setup.py b/pyxcp/stim/setup.py new file mode 100644 index 0000000..3d3078d --- /dev/null +++ b/pyxcp/stim/setup.py @@ -0,0 +1,40 @@ +import os +import subprocess + +from distutils.core import Extension +from distutils.core import setup +from pybind11.setup_helpers import build_ext +from pybind11.setup_helpers import naive_recompile +from pybind11.setup_helpers import ParallelCompile +from pybind11.setup_helpers import Pybind11Extension + +# ParallelCompile("NPY_NUM_BUILD_JOBS").install() +ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() + +INCLUDE_DIRS = subprocess.getoutput("pybind11-config --include") + +# os.environ ["CFLAGS"] = '' + +PKG_NAME = "stim" +EXT_NAMES = ["stim"] +__version__ = "0.0.1" + +ext_modules = [ + Pybind11Extension( + EXT_NAMES[0], + include_dirs=[INCLUDE_DIRS], + sources=["stim.cpp", "stim_wrapper.cpp", "scheduler.cpp"], + define_macros=[("EXTENSION_NAME", EXT_NAMES[0])], + cxx_std=20, # Extension will use C++20 generators/coroutines. + ), +] + +setup( + name=PKG_NAME, + version="0.0.1", + author="John Doe", + description="Example", + ext_modules=ext_modules, + cmdclass={"build_ext": build_ext}, + zip_save=False, +) diff --git a/pyxcp/stim/stim.cpp b/pyxcp/stim/stim.cpp index 0f7db6d..ace8954 100644 --- a/pyxcp/stim/stim.cpp +++ b/pyxcp/stim/stim.cpp @@ -5,12 +5,9 @@ #include "stim.hpp" void make_dto() { - } void init() { - - } static Mutex writer_lock{}; diff --git a/pyxcp/stim/stim.hpp b/pyxcp/stim/stim.hpp index d656ea4..d88bc20 100644 --- a/pyxcp/stim/stim.hpp +++ b/pyxcp/stim/stim.hpp @@ -1,30 +1,29 @@ #if !defined(__STIM_HPP) -#define __STIM_HPP + #define __STIM_HPP -#include + #include + #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include -#include -#include + #include "scheduler.hpp" -#include "scheduler.hpp" - -constexpr double TMR_RESOLUTION = 1.0 / 1000.0; // Timer resolution is one millisecond. +constexpr double TMR_RESOLUTION = 1.0 / 1000.0; // Timer resolution is one millisecond. class Timer { -public: + public: Timer() { QueryPerformanceFrequency(&m_ticks_per_sec); @@ -44,14 +43,16 @@ class Timer { return ela.QuadPart; } -private: + private: + LARGE_INTEGER m_ticks_per_sec; LARGE_INTEGER m_starting_time; }; -template +template class TsQueue { -public: + public: + TsQueue() = default; TsQueue(const TsQueue& other) noexcept { @@ -67,7 +68,7 @@ class TsQueue { std::shared_ptr get() noexcept { std::unique_lock lock(m_mtx); - m_cond.wait(lock, [this]{return !m_queue.empty();}); + m_cond.wait(lock, [this] { return !m_queue.empty(); }); std::shared_ptr result(std::make_shared(m_queue.front())); m_queue.pop(); return result; @@ -78,18 +79,21 @@ class TsQueue { return m_queue.empty(); } -private: - mutable std::mutex m_mtx; - std::queue m_queue; + private: + + mutable std::mutex m_mtx; + std::queue m_queue; std::condition_variable m_cond; }; struct Mutex { - ~Mutex() {} + ~Mutex() { + } void lock() { m_mtx.lock(); } + void unlock() { m_mtx.unlock(); } @@ -100,17 +104,15 @@ struct Mutex { Mutex& get_writer_lock(); Mutex& get_policy_lock(); -constexpr std::uint8_t MIN_STIM_PID=0x00; -constexpr std::uint8_t MAX_STIM_PID=0xBF; +constexpr std::uint8_t MIN_STIM_PID = 0x00; +constexpr std::uint8_t MAX_STIM_PID = 0xBF; -struct DAQListType { - -}; +struct DAQListType {}; ///// From BlueParrot. using XcpDaq_ODTEntryIntegerType = std::uint16_t; -using XcpDaq_ODTIntegerType = std::uint16_t; +using XcpDaq_ODTIntegerType = std::uint16_t; typedef enum tagXcpDaq_DirectionType { XCP_DIRECTION_NONE, @@ -122,12 +124,11 @@ typedef enum tagXcpDaq_DirectionType { //////////////// C++ style //////////////// struct OdtEntryType { - void clear() { - address=0; - address_extension=0; - bitOffset=0; - entry_size=0; + address = 0; + address_extension = 0; + bitOffset = 0; + entry_size = 0; } std::uint32_t address; @@ -138,7 +139,7 @@ struct OdtEntryType { struct OdtType { XcpDaq_ODTEntryIntegerType numOdtEntries; - std::uint16_t firstOdtEntry; + std::uint16_t firstOdtEntry; void clear() { m_entries.resize(0); @@ -152,15 +153,14 @@ struct OdtType { }; struct DynamicListType { - void clear() { - numOdts=0; - firstOdt=0; - mode=0; - prescaler=0; - counter=0; - event_channel_number=0; - priority=0; + numOdts = 0; + firstOdt = 0; + mode = 0; + prescaler = 0; + counter = 0; + event_channel_number = 0; + priority = 0; m_odts.resize(0); } @@ -169,8 +169,8 @@ struct DynamicListType { } XcpDaq_ODTIntegerType numOdts; - std::uint16_t firstOdt; - std::uint16_t mode; + std::uint16_t firstOdt; + std::uint16_t mode; std::uint16_t prescaler; std::uint16_t event_channel_number; @@ -184,15 +184,15 @@ struct DynamicListType { typedef struct tagXcpDaq_ListConfigurationType { XcpDaq_ODTIntegerType numOdts; - std::uint16_t firstOdt; + std::uint16_t firstOdt; } XcpDaq_ListConfigurationType; typedef struct tagXcpDaq_ListStateType { std::uint16_t mode; -#if XCP_DAQ_ENABLE_PRESCALER == XCP_ON + #if XCP_DAQ_ENABLE_PRESCALER == XCP_ON std::uint16_t prescaler; std::uint16_t counter; -#endif /* XCP_DAQ_ENABLE_PRESCALER */ + #endif /* XCP_DAQ_ENABLE_PRESCALER */ } XcpDaq_ListStateType; typedef enum tagXcpDaq_EntityKindType { @@ -203,17 +203,17 @@ typedef enum tagXcpDaq_EntityKindType { } XcpDaq_EntityKindType; typedef struct tagXcpDaq_EventType { - std::uint8_t const *const name; - std::uint8_t nameLen; - std::uint8_t properties; - std::uint8_t timeunit; - std::uint8_t cycle; + std::uint8_t const * const name; + std::uint8_t nameLen; + std::uint8_t properties; + std::uint8_t timeunit; + std::uint8_t cycle; /* unit8_t priority; */ } XcpDaq_EventType; typedef struct tagXcpDaq_MessageType { - std::uint8_t dlc; - std::uint8_t const *data; + std::uint8_t dlc; + std::uint8_t const * data; } XcpDaq_MessageType; ///// @@ -223,82 +223,92 @@ struct StimParameters { }; struct DaqEventInfo { - - explicit DaqEventInfo(/*std::string_view*/const std::string& name, std::int8_t unit_exp, std::size_t cycle, - std::size_t maxDaqList, std::size_t priority, std::string_view consistency, bool daq, bool stim, bool packed) : - m_name(name), m_unit_exp(unit_exp), m_cycle(cycle), m_maxDaqList(maxDaqList), - m_priority(priority), m_consistency(consistency), m_daq(daq), m_stim(stim), m_packed(packed) { - if (cycle == 0) { - m_periodic = false; - m_cycle_time = 0; - } else { - m_periodic = true; - auto cycle_time = cycle * std::pow(10, unit_exp); - if (cycle_time < TMR_RESOLUTION) { - cycle_time = TMR_RESOLUTION; - } - m_cycle_time = static_cast(cycle_time * 1000.0); - std::cout << "\tTMR_TMP: " << m_cycle_time << std::endl; + explicit DaqEventInfo( + /*std::string_view*/ const std::string& name, std::int8_t unit_exp, std::size_t cycle, std::size_t maxDaqList, + std::size_t priority, std::string_view consistency, bool daq, bool stim, bool packed + ) : + m_name(name), + m_unit_exp(unit_exp), + m_cycle(cycle), + m_maxDaqList(maxDaqList), + m_priority(priority), + m_consistency(consistency), + m_daq(daq), + m_stim(stim), + m_packed(packed) { + if (cycle == 0) { + m_periodic = false; + m_cycle_time = 0; + } else { + m_periodic = true; + auto cycle_time = cycle * std::pow(10, unit_exp); + if (cycle_time < TMR_RESOLUTION) { + cycle_time = TMR_RESOLUTION; } + m_cycle_time = static_cast(cycle_time * 1000.0); + std::cout << "\tTMR_TMP: " << m_cycle_time << std::endl; + } + + std::cout << "Event: " << m_name << " Zaikel: " << m_cycle_time << "ms - periodic? " << m_periodic << std::endl; + } + + public: - std::cout << "Event: " << m_name << " Zaikel: " << m_cycle_time << "ms - periodic? " << m_periodic << std::endl; - } - -public: - std::string m_name; - std::int8_t m_unit_exp; - std::size_t m_cycle; - std::size_t m_maxDaqList; - std::size_t m_priority; - std::string m_consistency{}; - bool m_daq; - bool m_stim; - bool m_packed; - bool m_periodic; - std::size_t m_cycle_time; + std::string m_name; + std::int8_t m_unit_exp; + std::size_t m_cycle; + std::size_t m_maxDaqList; + std::size_t m_priority; + std::string m_consistency{}; + bool m_daq; + bool m_stim; + bool m_packed; + bool m_periodic; + std::size_t m_cycle_time; std::set m_daq_lists{}; }; -enum class EventChannelTimeUnit : std::uint8_t { - EVENT_CHANNEL_TIME_UNIT_1NS=0, - EVENT_CHANNEL_TIME_UNIT_10NS=1, - EVENT_CHANNEL_TIME_UNIT_100NS=2, - EVENT_CHANNEL_TIME_UNIT_1US=3, - EVENT_CHANNEL_TIME_UNIT_10US=4, - EVENT_CHANNEL_TIME_UNIT_100US=5, - EVENT_CHANNEL_TIME_UNIT_1MS=6, - EVENT_CHANNEL_TIME_UNIT_10MS=7, - EVENT_CHANNEL_TIME_UNIT_100MS=8, - EVENT_CHANNEL_TIME_UNIT_1S=9, - EVENT_CHANNEL_TIME_UNIT_1PS=10, - EVENT_CHANNEL_TIME_UNIT_10PS=11, - EVENT_CHANNEL_TIME_UNIT_100PS=12, +enum class EventChannelTimeUnit : std::uint8_t { + EVENT_CHANNEL_TIME_UNIT_1NS = 0, + EVENT_CHANNEL_TIME_UNIT_10NS = 1, + EVENT_CHANNEL_TIME_UNIT_100NS = 2, + EVENT_CHANNEL_TIME_UNIT_1US = 3, + EVENT_CHANNEL_TIME_UNIT_10US = 4, + EVENT_CHANNEL_TIME_UNIT_100US = 5, + EVENT_CHANNEL_TIME_UNIT_1MS = 6, + EVENT_CHANNEL_TIME_UNIT_10MS = 7, + EVENT_CHANNEL_TIME_UNIT_100MS = 8, + EVENT_CHANNEL_TIME_UNIT_1S = 9, + EVENT_CHANNEL_TIME_UNIT_1PS = 10, + EVENT_CHANNEL_TIME_UNIT_10PS = 11, + EVENT_CHANNEL_TIME_UNIT_100PS = 12, }; enum class DAQ_TIMESTAMP_UNIT_TO_EXP : std::int8_t { - DAQ_TIMESTAMP_UNIT_1PS= -12, - DAQ_TIMESTAMP_UNIT_10PS= -11, - DAQ_TIMESTAMP_UNIT_100PS= -10, - DAQ_TIMESTAMP_UNIT_1NS= -9, - DAQ_TIMESTAMP_UNIT_10NS= -8, - DAQ_TIMESTAMP_UNIT_100NS= -7, - DAQ_TIMESTAMP_UNIT_1US= -6, - DAQ_TIMESTAMP_UNIT_10US= -5, - DAQ_TIMESTAMP_UNIT_100US= -4, - DAQ_TIMESTAMP_UNIT_1MS= -3, - DAQ_TIMESTAMP_UNIT_10MS=-2, - DAQ_TIMESTAMP_UNIT_100MS= -1, - DAQ_TIMESTAMP_UNIT_1S = 0, + DAQ_TIMESTAMP_UNIT_1PS = -12, + DAQ_TIMESTAMP_UNIT_10PS = -11, + DAQ_TIMESTAMP_UNIT_100PS = -10, + DAQ_TIMESTAMP_UNIT_1NS = -9, + DAQ_TIMESTAMP_UNIT_10NS = -8, + DAQ_TIMESTAMP_UNIT_100NS = -7, + DAQ_TIMESTAMP_UNIT_1US = -6, + DAQ_TIMESTAMP_UNIT_10US = -5, + DAQ_TIMESTAMP_UNIT_100US = -4, + DAQ_TIMESTAMP_UNIT_1MS = -3, + DAQ_TIMESTAMP_UNIT_10MS = -2, + DAQ_TIMESTAMP_UNIT_100MS = -1, + DAQ_TIMESTAMP_UNIT_1S = 0, }; void sched_init(); // TODO: Incl. class Stim { -public: + public: + const std::uint8_t DIRECTION_STIM = 0x02; - using event_info_t = std::vector; - using feed_function_t = std::function)>; - using send_function_t = std::function)>; + using event_info_t = std::vector; + using feed_function_t = std::function)>; + using send_function_t = std::function)>; explicit Stim() { if (timeBeginPeriod(100) == TIMERR_NOERROR) { @@ -307,15 +317,15 @@ class Stim { std::cout << "timeBeginPeriod() failed!!!" << std::endl; } - DWORD task_index=0UL; + DWORD task_index = 0UL; auto xxx = AvSetMmThreadCharacteristics("Pro Audio", &task_index); std::cout << "AvSetMmThreadCharacteristics() " << xxx << ":" << task_index << std::endl; auto start = timeGetTime(); m_scheduler.start_thread(); - //Sleep(1650); - //std::cout << "Elapsed: " << timeGetTime() - start; + // Sleep(1650); + // std::cout << "Elapsed: " << timeGetTime() - start; } void setParameters(const StimParameters& params) { @@ -324,9 +334,9 @@ class Stim { void setDaqEventInfo(const event_info_t& daq_event_info) { m_daq_event_info = daq_event_info; - std::size_t idx=0; + std::size_t idx = 0; - for (const auto& event: daq_event_info) { + for (const auto& event : daq_event_info) { if (event.m_stim) { m_stim_events.emplace(idx); std::cout << "\tSTIM: " << event.m_name << ":" << idx << std::endl; @@ -339,7 +349,7 @@ class Stim { if (!validateEntryNumber(daqListNumber, odtNumber, odtEntryNumber)) { return; } - m_daq_ptr = {daqListNumber, odtNumber, odtEntryNumber}; + m_daq_ptr = { daqListNumber, odtNumber, odtEntryNumber }; std::cout << "SET_DAQ_PTR " << daqListNumber << ":" << odtNumber << ":" << odtEntryNumber << std::endl; } @@ -354,14 +364,14 @@ class Stim { void writeDaq(std::uint16_t bitOffset, std::uint16_t entrySize, std::uint16_t addressExt, std::uint32_t address) { auto [d, o, e] = m_daq_ptr; - auto entry = m_daq_lists[d].m_odts[o].m_entries[e]; + auto entry = m_daq_lists[d].m_odts[o].m_entries[e]; std::cout << "WRITE_DAQ " << bitOffset << ":" << entrySize << ":" << addressExt << ":" << address << std::endl; - entry.bitOffset = bitOffset; - entry.address=address; - entry.address_extension=addressExt; - entry.entry_size=entrySize; + entry.bitOffset = bitOffset; + entry.address = address; + entry.address_extension = addressExt; + entry.entry_size = entrySize; std::cout << "\tBO: " << entry.bitOffset << std::endl; std::cout << "\tES: " << entry.entry_size << std::endl; @@ -369,23 +379,26 @@ class Stim { std::cout << "\tAE: " << entry.address_extension << std::endl; } - void setDaqListMode(std::uint16_t mode,std::uint16_t daqListNumber, std::uint16_t eventChannelNumber, - std::uint16_t prescaler, std::uint16_t priority) { + void setDaqListMode( + std::uint16_t mode, std::uint16_t daqListNumber, std::uint16_t eventChannelNumber, std::uint16_t prescaler, + std::uint16_t priority + ) { if (!validateEntryNumber(daqListNumber)) { return; } - auto entry = m_daq_lists[daqListNumber]; - entry.mode = mode; + auto entry = m_daq_lists[daqListNumber]; + entry.mode = mode; entry.prescaler = prescaler; // The use of a prescaler is only used for DAQ lists with DIRECTION = DAQ. - entry.priority=priority; - entry.event_channel_number=eventChannelNumber; + entry.priority = priority; + entry.event_channel_number = eventChannelNumber; auto& event = m_daq_event_info[eventChannelNumber]; event.m_daq_lists.emplace(daqListNumber); - std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" << priority << std::endl; + std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" + << priority << std::endl; if ((mode & DIRECTION_STIM) == DIRECTION_STIM) { std::cout << "\tSTIM-MODE!!!\n"; // TODO: Calculate timebase on the fly. @@ -395,7 +408,7 @@ class Stim { } } - void startStopDaqList(std::uint16_t mode,std::uint16_t daqListNumber) { + void startStopDaqList(std::uint16_t mode, std::uint16_t daqListNumber) { if (!validateEntryNumber(daqListNumber)) { return; } @@ -403,18 +416,20 @@ class Stim { } void startStopSynch(std::uint16_t mode) { - } - void writeDaqMultiple(/* const std::vector&*/std::uint16_t daqElements) { - + void writeDaqMultiple(/* const std::vector&*/ std::uint16_t daqElements) { } - void dtoCtrProperties(std::uint16_t modifier,std::uint16_t eventChannel,std::uint16_t relatedEventChannel,std::uint16_t mode) { - + void dtoCtrProperties( + std::uint16_t modifier, std::uint16_t eventChannel, std::uint16_t relatedEventChannel, std::uint16_t mode + ) { } - void setDaqPackedMode(std::uint16_t, std::uint16_t daqListNumber, std::uint16_t daqPackedMode, std::uint16_t dpmTimestampMode=0, std::uint16_t dpmSampleCount=0) { + void setDaqPackedMode( + std::uint16_t, std::uint16_t daqListNumber, std::uint16_t daqPackedMode, std::uint16_t dpmTimestampMode = 0, + std::uint16_t dpmSampleCount = 0 + ) { if (!validateEntryNumber(daqListNumber)) { return; } @@ -435,9 +450,7 @@ class Stim { void allocDaq(std::uint16_t daqCount) { m_daq_lists.resize(daqCount); std::cout << "ALLOC_DAQ " << daqCount << std::endl; - std::for_each(m_daq_lists.cbegin(), m_daq_lists.cend(), [](auto elem) { - elem.clear(); - }); + std::for_each(m_daq_lists.cbegin(), m_daq_lists.cend(), [](auto elem) { elem.clear(); }); } void allocOdt(std::uint16_t daqListNumber, std::uint16_t odtCount) { @@ -448,7 +461,7 @@ class Stim { m_daq_lists[daqListNumber].resize(odtCount); } - void allocOdtEntry(std::uint16_t daqListNumber,std::uint16_t odtNumber,std::uint16_t odtEntriesCount) { + void allocOdtEntry(std::uint16_t daqListNumber, std::uint16_t odtNumber, std::uint16_t odtEntriesCount) { if (!validateEntryNumber(daqListNumber, odtNumber)) { return; } @@ -466,12 +479,12 @@ class Stim { void send(const std::vector frame) { if (m_send_function) { - std::scoped_lock _ {get_writer_lock()}; + std::scoped_lock _{ get_writer_lock() }; m_send_function.value()(frame); } } -protected: + protected: void calculate_scheduler_period(std::size_t value) { if (!m_scheduler_period) { @@ -481,20 +494,21 @@ class Stim { *m_scheduler_max_value = value; } std::cout << "SCHED_Value: " << value << std::endl; - *m_scheduler_period = std::gcd(*m_scheduler_period, value); + *m_scheduler_period = std::gcd(*m_scheduler_period, value); *m_scheduler_max_value = std::lcm(*m_scheduler_max_value, value); std::cout << "SCHED_Period: " << *m_scheduler_period << " max: " << *m_scheduler_max_value << std::endl; } - bool validateEntryNumber(std::uint16_t daqListNumber, std::optional odtNumber=std::nullopt, - std::optional odtEntryNumber=std::nullopt) const { - + bool validateEntryNumber( + std::uint16_t daqListNumber, std::optional odtNumber = std::nullopt, + std::optional odtEntryNumber = std::nullopt + ) const { if (daqListNumber >= std::size(m_daq_lists)) { return false; } if (odtNumber) { auto entry = m_daq_lists[daqListNumber]; - if (*odtNumber >= std::size(entry.m_odts)) { + if (*odtNumber >= std::size(entry.m_odts)) { return false; } } @@ -508,18 +522,19 @@ class Stim { return true; } -private: - StimParameters m_params{}; - std::vector m_daq_lists{}; + private: + + StimParameters m_params{}; + std::vector m_daq_lists{}; std::tuple m_daq_ptr; - std::optional m_scheduler_period{std::nullopt}; - std::optional m_scheduler_max_value{std::nullopt}; - event_info_t m_daq_event_info; - //std::map> m_event_mapping{}; - std::set m_stim_events {}; - std::optional m_feed_function {std::nullopt}; - std::optional m_send_function {std::nullopt}; - Scheduler m_scheduler{}; + std::optional m_scheduler_period{ std::nullopt }; + std::optional m_scheduler_max_value{ std::nullopt }; + event_info_t m_daq_event_info; + // std::map> m_event_mapping{}; + std::set m_stim_events{}; + std::optional m_feed_function{ std::nullopt }; + std::optional m_send_function{ std::nullopt }; + Scheduler m_scheduler{}; }; -#endif // __STIM_HPP +#endif // __STIM_HPP diff --git a/pyxcp/stim/stim_wrapper.cpp b/pyxcp/stim/stim_wrapper.cpp index 5bcbbea..b9782a6 100644 --- a/pyxcp/stim/stim_wrapper.cpp +++ b/pyxcp/stim/stim_wrapper.cpp @@ -11,47 +11,39 @@ using namespace py::literals; #include "stim.hpp" - PYBIND11_MODULE(stim, m) { - - m.def("get_writer_lock", &get_writer_lock, py::return_value_policy::reference); - m.def("get_policy_lock", &get_policy_lock, py::return_value_policy::reference); - - py::class_(m, "DaqEventInfo") - .def(py::init()) - ; + m.def("get_writer_lock", &get_writer_lock, py::return_value_policy::reference); + m.def("get_policy_lock", &get_policy_lock, py::return_value_policy::reference); + + py::class_(m, "DaqEventInfo") + .def(py::init() + ); py::class_(m, "Stim") .def(py::init<>()) - .def("setDaqEventInfo", &Stim::setDaqEventInfo) - .def("clear", &Stim::clear) - .def("freeDaq", &Stim::freeDaq) - .def("allocDaq", &Stim::allocDaq) - .def("allocOdt", &Stim::allocOdt) - .def("allocOdtEntry", &Stim::allocOdtEntry) - .def("setDaqPtr", &Stim::setDaqPtr) - .def("clearDaqList", &Stim::clearDaqList) - .def("writeDaq", &Stim::writeDaq) - .def("setDaqListMode", &Stim::setDaqListMode) - .def("startStopDaqList", &Stim::startStopDaqList) - - .def("set_policy_feeder", [](Stim& self, const py::function& callback) { - self.set_policy_feeder(callback); - }) - - .def("set_frame_sender", [](Stim& self, const py::function& callback) { - self.set_frame_sender(callback); - }) - - ; - - py::class_ (m, "Mutex") - .def("__enter__", [&] (Mutex& self) { - self.lock(); - }) - .def("__exit__", - [&] (Mutex& self, const std::optional& exc_type, const std::optional& exc_value, const std::optional& traceback) { - self.unlock(); - }) - ; + .def("setDaqEventInfo", &Stim::setDaqEventInfo) + .def("clear", &Stim::clear) + .def("freeDaq", &Stim::freeDaq) + .def("allocDaq", &Stim::allocDaq) + .def("allocOdt", &Stim::allocOdt) + .def("allocOdtEntry", &Stim::allocOdtEntry) + .def("setDaqPtr", &Stim::setDaqPtr) + .def("clearDaqList", &Stim::clearDaqList) + .def("writeDaq", &Stim::writeDaq) + .def("setDaqListMode", &Stim::setDaqListMode) + .def("startStopDaqList", &Stim::startStopDaqList) + + .def("set_policy_feeder", [](Stim& self, const py::function& callback) { self.set_policy_feeder(callback); }) + + .def("set_frame_sender", [](Stim& self, const py::function& callback) { self.set_frame_sender(callback); }) + + ; + + py::class_(m, "Mutex") + .def("__enter__", [&](Mutex& self) { self.lock(); }) + .def( + "__exit__", + [&](Mutex& self, const std::optional& exc_type, const std::optional& exc_value, + const std::optional& traceback) { self.unlock(); } + ); } diff --git a/pyxcp/tests/test_asam_types.py b/pyxcp/tests/test_asam_types.py index ed76579..e2edf0c 100644 --- a/pyxcp/tests/test_asam_types.py +++ b/pyxcp/tests/test_asam_types.py @@ -1,7 +1,7 @@ -from pyxcp.asam import types - import pytest +from pyxcp.asam import types + def testEncodeUint32_0(): assert types.A_Uint32("<").encode(3415750566) == b"\xa67\x98\xcb" diff --git a/pyxcp/tests/test_can.py b/pyxcp/tests/test_can.py index 8a99af3..687bb6f 100644 --- a/pyxcp/tests/test_can.py +++ b/pyxcp/tests/test_can.py @@ -1,3 +1,5 @@ +import pytest + from pyxcp.transport.can import calculateFilter from pyxcp.transport.can import CAN_EXTENDED_ID from pyxcp.transport.can import Identifier @@ -8,8 +10,6 @@ from pyxcp.transport.can import setDLC from pyxcp.transport.can import stripIdentifier -import pytest - def testSet0(): assert setDLC(0) == 0 @@ -181,6 +181,7 @@ def test_identifier_max_works2(): assert i.is_extended is False assert i.raw_id == MAX_11_BIT_IDENTIFIER + @pytest.mark.skip def test_identifier_outof_range_raises1(): with pytest.raises(IdentifierOutOfRangeError): diff --git a/pyxcp/tests/test_checksum.py b/pyxcp/tests/test_checksum.py index 60793d3..2a258d7 100644 --- a/pyxcp/tests/test_checksum.py +++ b/pyxcp/tests/test_checksum.py @@ -1,7 +1,7 @@ -from pyxcp import checksum - import pytest +from pyxcp import checksum + """ XCP_ADD_11 0x10 0x10 XCP_ADD_12 0x0F10 0x0F10 diff --git a/pyxcp/tests/test_config.py b/pyxcp/tests/test_config.py index bddc44d..b1c4951 100644 --- a/pyxcp/tests/test_config.py +++ b/pyxcp/tests/test_config.py @@ -1,5 +1,6 @@ from collections import OrderedDict from io import StringIO + from pyxcp.config import readConfiguration JSON = """{ diff --git a/pyxcp/tests/test_master.py b/pyxcp/tests/test_master.py index d6896d9..038872b 100644 --- a/pyxcp/tests/test_master.py +++ b/pyxcp/tests/test_master.py @@ -3,10 +3,11 @@ import struct import time from collections import deque +from unittest import mock + from pyxcp import types from pyxcp.master import Master from pyxcp.transport.can import CanInterfaceBase -from unittest import mock class MockSocket: @@ -92,7 +93,6 @@ def getTimestampResolution(self): class TestMaster: - DefaultConnectCmd = bytes([0x02, 0x00, 0x00, 0x00, 0xFF, 0x00]) DefaultConnectResponse = "FF 3D C0 FF DC 05 01 01" diff --git a/pyxcp/tests/test_transport.py b/pyxcp/tests/test_transport.py index 1f4321e..cad6b6d 100644 --- a/pyxcp/tests/test_transport.py +++ b/pyxcp/tests/test_transport.py @@ -1,7 +1,7 @@ -import pyxcp.transport.base as tr - import pytest +import pyxcp.transport.base as tr + def test_factory_works(): assert isinstance(tr.createTransport("eth"), tr.BaseTransport) diff --git a/pyxcp/tests/test_utils.py b/pyxcp/tests/test_utils.py index 3b1cadd..cf4ddbc 100644 --- a/pyxcp/tests/test_utils.py +++ b/pyxcp/tests/test_utils.py @@ -1,9 +1,10 @@ +from sys import version_info + from pyxcp.utils import flatten from pyxcp.utils import getPythonVersion from pyxcp.utils import hexDump from pyxcp.utils import PYTHON_VERSION from pyxcp.utils import slicer -from sys import version_info def test_hexdump(capsys): diff --git a/pyxcp/timing.py b/pyxcp/timing.py index 1e9b342..0938d24 100644 --- a/pyxcp/timing.py +++ b/pyxcp/timing.py @@ -4,7 +4,6 @@ class Timing: - T_US = 1000 * 1000 T_MS = 1000 T_S = 1 diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 92a59bf..94a35be 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -19,6 +19,7 @@ from ..utils import SHORT_SLEEP from pyxcp.config import Configuration + class FrameAcquisitionPolicy: """ Base class for all frame acquisition policies. @@ -202,6 +203,7 @@ def __del__(self): def loadConfig(self, config): """Load configuration data.""" self.config = Configuration(self.PARAMETER_MAP or {}, config or {}) + def set_writer_lock(self, lock): self.writer_lock = lock diff --git a/pyxcp/transport/candriver/pc_pcan.py b/pyxcp/transport/candriver/pc_pcan.py index 4b06e4b..fd5f109 100644 --- a/pyxcp/transport/candriver/pc_pcan.py +++ b/pyxcp/transport/candriver/pc_pcan.py @@ -3,11 +3,11 @@ """ python-can driver for Peak System interfaces. """ +from can import BusState + import pyxcp.transport.can as can import pyxcp.transport.candriver.python_can as python_can -from can import BusState - class PCan(python_can.PythonCAN, can.CanInterfaceBase): """""" diff --git a/pyxcp/transport/candriver/python_can.py b/pyxcp/transport/candriver/python_can.py index 81b8392..1722b14 100644 --- a/pyxcp/transport/candriver/python_can.py +++ b/pyxcp/transport/candriver/python_can.py @@ -3,7 +3,6 @@ """ Support for python-can - github.com/hardbyte/python-can """ -import pyxcp.transport.can as can import re from collections import OrderedDict @@ -11,6 +10,8 @@ from can import CanError from can import Message +import pyxcp.transport.can as can + NUMBER = re.compile(r"(?P0[x|X])?(?P[0-9]+)", re.VERBOSE) diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 49415f1..21b8cc8 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -200,7 +200,6 @@ def listen(self): length = None else: - data = data[current_position:] break diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index 3fe03e0..f2ea3b3 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -37,7 +37,6 @@ def __del__(self): self.closeConnection() def connect(self): - self.logger.debug("Trying to open serial commPort {}.".format(self.portName)) try: self.commPort = serial.Serial(self.portName, self.baudrate, timeout=SxI.TIMEOUT) @@ -59,7 +58,6 @@ def flush(self): self.commPort.flush() def listen(self): - while True: if self.closeEvent.isSet(): return diff --git a/pyxcp/transport/usb_transport.py b/pyxcp/transport/usb_transport.py index e69c2bb..f055822 100644 --- a/pyxcp/transport/usb_transport.py +++ b/pyxcp/transport/usb_transport.py @@ -106,7 +106,6 @@ def close(self): self.closeConnection() def _packet_listen(self): - close_event_set = self.closeEvent.isSet _packets = self._packets @@ -194,12 +193,10 @@ def listen(self): length = None else: - data = data[current_position:] break def send(self, frame): - self.pre_send_timestamp = time() try: self.command_endpoint.write(frame) diff --git a/pyxcp/types.py b/pyxcp/types.py index 60b1e80..7cd1f63 100644 --- a/pyxcp/types.py +++ b/pyxcp/types.py @@ -926,21 +926,22 @@ class Event(enum.IntEnum): } EVENT_CHANNEL_TIME_UNIT_TO_EXP = { - "EVENT_CHANNEL_TIME_UNIT_1PS": -12, - "EVENT_CHANNEL_TIME_UNIT_10PS": -11, - "EVENT_CHANNEL_TIME_UNIT_100PS": -10, - "EVENT_CHANNEL_TIME_UNIT_1NS": -9, - "EVENT_CHANNEL_TIME_UNIT_10NS": -8, - "EVENT_CHANNEL_TIME_UNIT_100NS": -7, - "EVENT_CHANNEL_TIME_UNIT_1US": -6, + "EVENT_CHANNEL_TIME_UNIT_1PS": -12, + "EVENT_CHANNEL_TIME_UNIT_10PS": -11, + "EVENT_CHANNEL_TIME_UNIT_100PS": -10, + "EVENT_CHANNEL_TIME_UNIT_1NS": -9, + "EVENT_CHANNEL_TIME_UNIT_10NS": -8, + "EVENT_CHANNEL_TIME_UNIT_100NS": -7, + "EVENT_CHANNEL_TIME_UNIT_1US": -6, "EVENT_CHANNEL_TIME_UNIT_10US": -5, - "EVENT_CHANNEL_TIME_UNIT_100US": -4, - "EVENT_CHANNEL_TIME_UNIT_1MS": -3, - "EVENT_CHANNEL_TIME_UNIT_10MS": -2, - "EVENT_CHANNEL_TIME_UNIT_100MS": -1, - "EVENT_CHANNEL_TIME_UNIT_1S": 0, + "EVENT_CHANNEL_TIME_UNIT_100US": -4, + "EVENT_CHANNEL_TIME_UNIT_1MS": -3, + "EVENT_CHANNEL_TIME_UNIT_10MS": -2, + "EVENT_CHANNEL_TIME_UNIT_100MS": -1, + "EVENT_CHANNEL_TIME_UNIT_1S": 0, } + class XcpGetSeedMode(enum.IntEnum): FIRST_PART = 0 REMAINING = 1 From be8a93191cb9b7092483b12bef9fc7515ea6246e Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 27 Aug 2023 13:13:08 +0200 Subject: [PATCH 08/99] today() --- pyxcp/master/master.py | 8 ++- pyxcp/stim/stim.hpp | 108 ++++++++++++++++++++++++++---------- pyxcp/stim/stim_wrapper.cpp | 3 +- 3 files changed, 87 insertions(+), 32 deletions(-) diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index d86d908..8af1530 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -1053,7 +1053,9 @@ def startStopDaqList(self, mode: int, daqListNumber: int): dln = self.WORD_pack(daqListNumber) response = self.transport.request(types.Command.START_STOP_DAQ_LIST, mode, *dln) self.stim.startStopDaqList(mode, daqListNumber) - return types.StartStopDaqListResponse.parse(response, byteOrder=self.slaveProperties.byteOrder) + firstPid = types.StartStopDaqListResponse.parse(response, byteOrder=self.slaveProperties.byteOrder) + self.stim.set_first_pid(daqListNumber, firstPid.firstPid) + return firstPid @wrapped def startStopSynch(self, mode): @@ -1066,7 +1068,9 @@ def startStopSynch(self, mode): 1 = start selected 2 = stop selected """ - return self.transport.request(types.Command.START_STOP_SYNCH, mode) + res = self.transport.request(types.Command.START_STOP_SYNCH, mode) + self.stim.startStopSynch(mode) + return res @wrapped def writeDaqMultiple(self, daqElements): diff --git a/pyxcp/stim/stim.hpp b/pyxcp/stim/stim.hpp index d88bc20..976b43a 100644 --- a/pyxcp/stim/stim.hpp +++ b/pyxcp/stim/stim.hpp @@ -2,9 +2,6 @@ #if !defined(__STIM_HPP) #define __STIM_HPP - #include - #include - #include #include #include @@ -18,6 +15,9 @@ #include #include + #include + #include + #include "scheduler.hpp" constexpr double TMR_RESOLUTION = 1.0 / 1000.0; // Timer resolution is one millisecond. @@ -305,8 +305,15 @@ void sched_init(); // TODO: Incl. class Stim { public: + const std::uint8_t RESUME = 0x80; + const std::uint8_t RUNNING = 0x40; + const std::uint8_t PID_OFF = 0x20; + const std::uint8_t TIMESTAMP = 0x10; + const std::uint8_t DIRECTION = 0x02; + const std::uint8_t SELECTED = 0x01; + const std::uint8_t DIRECTION_STIM = 0x02; - using event_info_t = std::vector; + using feed_function_t = std::function)>; using send_function_t = std::function)>; @@ -323,22 +330,24 @@ class Stim { std::cout << "AvSetMmThreadCharacteristics() " << xxx << ":" << task_index << std::endl; auto start = timeGetTime(); - m_scheduler.start_thread(); - // Sleep(1650); - // std::cout << "Elapsed: " << timeGetTime() - start; + + //m_scheduler.start_thread(); } void setParameters(const StimParameters& params) { m_params = params; } - void setDaqEventInfo(const event_info_t& daq_event_info) { - m_daq_event_info = daq_event_info; - std::size_t idx = 0; + void setDaqEventInfo(const std::vector& daq_event_info) { + std::uint16_t idx = 0; + + std::cout << "SET_DAQ_EVENT_INFO" << std::endl; for (const auto& event : daq_event_info) { + //m_daq_event_info.try_emplace(std::make_pair(idx, event)); + m_daq_event_info.try_emplace(idx, event); + //m_daq_event_info[idx] = event; if (event.m_stim) { - m_stim_events.emplace(idx); std::cout << "\tSTIM: " << event.m_name << ":" << idx << std::endl; } idx++; @@ -364,7 +373,7 @@ class Stim { void writeDaq(std::uint16_t bitOffset, std::uint16_t entrySize, std::uint16_t addressExt, std::uint32_t address) { auto [d, o, e] = m_daq_ptr; - auto entry = m_daq_lists[d].m_odts[o].m_entries[e]; + auto& entry = m_daq_lists[d].m_odts[o].m_entries[e]; std::cout << "WRITE_DAQ " << bitOffset << ":" << entrySize << ":" << addressExt << ":" << address << std::endl; @@ -383,10 +392,15 @@ class Stim { std::uint16_t mode, std::uint16_t daqListNumber, std::uint16_t eventChannelNumber, std::uint16_t prescaler, std::uint16_t priority ) { + std::cout << "<<< Enter setDaqListMode() !!!\n"; if (!validateEntryNumber(daqListNumber)) { + std::cout << "Invalid DAQ list number!!!\b\n"; return; } - auto entry = m_daq_lists[daqListNumber]; + std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" + << priority << std::endl; + + auto& entry = m_daq_lists.at(daqListNumber); entry.mode = mode; entry.prescaler = prescaler; // The use of a prescaler is only used for DAQ lists with DIRECTION = DAQ. @@ -394,28 +408,53 @@ class Stim { entry.priority = priority; entry.event_channel_number = eventChannelNumber; - auto& event = m_daq_event_info[eventChannelNumber]; + std::cout << "\tAnzahl / Events: " << std::size(m_daq_event_info) << std::endl; + auto& event = m_daq_event_info.at(eventChannelNumber); event.m_daq_lists.emplace(daqListNumber); - std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" - << priority << std::endl; if ((mode & DIRECTION_STIM) == DIRECTION_STIM) { std::cout << "\tSTIM-MODE!!!\n"; - // TODO: Calculate timebase on the fly. + + m_stim_lists.emplace(daqListNumber); std::cout << "\t\tEvent: " << event.m_name << " ==> " << event.m_cycle_time << " - " << event.m_periodic << std::endl; calculate_scheduler_period(event.m_cycle_time); } + std::cout << ">>> Exit setDaqListMode() !!!\n"; } void startStopDaqList(std::uint16_t mode, std::uint16_t daqListNumber) { if (!validateEntryNumber(daqListNumber)) { return; } + auto& entry = m_daq_lists.at(daqListNumber); + /* + * 00 = stop + * 01 = start + * 02 = select + */ + if (mode == 0) { + entry.mode &= ~(SELECTED | RUNNING); + } else if (mode == 1) { + entry.mode |= (RUNNING); + } else if (mode == 2) { + entry.mode |= (SELECTED); + } + std::cout << "START_STOP_DAQ_LIST " << mode << ":" << daqListNumber << std::endl; } void startStopSynch(std::uint16_t mode) { + std::cout << "START_STOP_SYNCH " << mode << ":" << std::endl; + + for (auto dl: m_stim_lists) { + std::cout << "\tRunning list: " << dl << std::endl; + } + auto idx = 0; + for (auto dl: m_daq_lists) { + std::cout << "DAQ-L: " << idx << " " << dl.mode << " - " << dl.event_channel_number << ":" << dl.prescaler << std::endl; + idx++; + } } void writeDaqMultiple(/* const std::vector&*/ std::uint16_t daqElements) { @@ -438,8 +477,9 @@ class Stim { void clear() { m_daq_lists.clear(); - m_daq_event_info.clear(); - m_stim_events.clear(); + //m_daq_event_info.clear(); + m_stim_lists.clear(); + m_first_pids.clear(); } void freeDaq() { @@ -469,6 +509,11 @@ class Stim { m_daq_lists[daqListNumber].m_odts[odtNumber].resize(odtEntriesCount); } + void set_first_pid(std::uint16_t daqListNumber, std::uint16_t firstPid) { + std::cout << "set_first_pid(" << daqListNumber << ", " << firstPid << ")\n"; + m_first_pids[daqListNumber] = firstPid; + } + void set_policy_feeder(const feed_function_t& fun) { m_feed_function = fun; } @@ -488,15 +533,20 @@ class Stim { void calculate_scheduler_period(std::size_t value) { if (!m_scheduler_period) { - *m_scheduler_period = value; + m_scheduler_period = value; + std::cout << "\tSet Period\n"; } if (!m_scheduler_max_value) { - *m_scheduler_max_value = value; + m_scheduler_max_value = value; + std::cout << "\tSet Max\n"; + } + if (value==1) { + value=3; } - std::cout << "SCHED_Value: " << value << std::endl; - *m_scheduler_period = std::gcd(*m_scheduler_period, value); - *m_scheduler_max_value = std::lcm(*m_scheduler_max_value, value); - std::cout << "SCHED_Period: " << *m_scheduler_period << " max: " << *m_scheduler_max_value << std::endl; + std::cout << "\tSCHED_Value: " << value << " - BEFORE: " << *m_scheduler_period << ":" << *m_scheduler_max_value << std::endl; + m_scheduler_period = std::gcd(*m_scheduler_period, value); + m_scheduler_max_value = std::lcm(*m_scheduler_max_value, value); + std::cout << "\tSCHED_Period: " << *m_scheduler_period << " max: " << *m_scheduler_max_value << std::endl; } bool validateEntryNumber( @@ -518,7 +568,6 @@ class Stim { return false; } } - std::cout << "\tOK, number is valid!!!\n"; return true; } @@ -529,12 +578,13 @@ class Stim { std::tuple m_daq_ptr; std::optional m_scheduler_period{ std::nullopt }; std::optional m_scheduler_max_value{ std::nullopt }; - event_info_t m_daq_event_info; - // std::map> m_event_mapping{}; - std::set m_stim_events{}; + std::map m_daq_event_info; + std::map m_first_pids{}; + std::set m_stim_lists{}; std::optional m_feed_function{ std::nullopt }; std::optional m_send_function{ std::nullopt }; Scheduler m_scheduler{}; + bool m_daq_running{false}; }; #endif // __STIM_HPP diff --git a/pyxcp/stim/stim_wrapper.cpp b/pyxcp/stim/stim_wrapper.cpp index b9782a6..544d173 100644 --- a/pyxcp/stim/stim_wrapper.cpp +++ b/pyxcp/stim/stim_wrapper.cpp @@ -32,9 +32,10 @@ PYBIND11_MODULE(stim, m) { .def("writeDaq", &Stim::writeDaq) .def("setDaqListMode", &Stim::setDaqListMode) .def("startStopDaqList", &Stim::startStopDaqList) + .def("startStopSynch", &Stim::startStopSynch) + .def("set_first_pid", &Stim::set_first_pid) .def("set_policy_feeder", [](Stim& self, const py::function& callback) { self.set_policy_feeder(callback); }) - .def("set_frame_sender", [](Stim& self, const py::function& callback) { self.set_frame_sender(callback); }) ; From 4979ac57eeee40f1c83ecdff7ad8cdfc25ec7118 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 27 Aug 2023 13:28:32 +0200 Subject: [PATCH 09/99] Cherry-picking commit --- pyxcp/master/master.py | 8 ++++---- pyxcp/utils.py | 1 - setup.py | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 8af1530..186944f 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -1732,10 +1732,10 @@ def getDaqInfo(self): name = decode_bytes(name) channel = { "name": name, - "priority": priority, - "unit": time_unit, - "cycle": cycle, - "maxDaqList": maxDaqList, + "priority": eci["eventChannelPriority"], + "unit": eci["eventChannelTimeUnit"], + "cycle": eci["eventChannelTimeCycle"], + "maxDaqList": eci["maxDaqList"], "properties": { "consistency": consistency, "daq": daq_supported, diff --git a/pyxcp/utils.py b/pyxcp/utils.py index 1e2b934..069e675 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -58,7 +58,6 @@ def decode_bytes(byte_str: bytes) -> str: else: return byte_str.decode(encoding) - PYTHON_VERSION = getPythonVersion() SHORT_SLEEP = 0.0005 diff --git a/setup.py b/setup.py index aecab8f..aa4f5a3 100644 --- a/setup.py +++ b/setup.py @@ -71,7 +71,6 @@ "traitlets", ] - class AsamKeyDllAutogen(setuptools.Command): """Custom command to compile `asamkeydll.exe`.""" From 0a24f8aab96208eb99e5426b0c7e8fa4b5d8a747 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 29 Aug 2023 08:47:51 +0200 Subject: [PATCH 10/99] today() --- pyxcp/config/__init__.py | 278 +++++++++++++++++++++++++++++++++++++++ pyxcp/config/legacy.py | 59 +++++++++ 2 files changed, 337 insertions(+) create mode 100644 pyxcp/config/__init__.py create mode 100644 pyxcp/config/legacy.py diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py new file mode 100644 index 0000000..8c3338e --- /dev/null +++ b/pyxcp/config/__init__.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import io +import json +import sys +import warnings +from pathlib import Path +from pprint import pprint + +import toml +from traitlets import Bool +from traitlets import Enum +from traitlets import Float +from traitlets import Int +from traitlets import Integer +from traitlets import List +from traitlets import Unicode +from traitlets.config import Application +from traitlets.config import Configurable +from traitlets.config import Instance +from traitlets.config import SingletonConfigurable +from traitlets.config.loader import Config +from traitlets.config.loader import load_pyconfig_files + +from pyxcp.config import legacy + + +class General(SingletonConfigurable): + """ """ + + loglevel = Unicode("WARN").tag(config=True) + disable_error_handling = Bool(False).tag(config=True) + seed_n_key_dll = Unicode(allow_none=True, default_value=None).tag(config=True) + seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) + + def __str__(self): + return str( + f"General(loglevel: '{self.loglevel}', disable_error_handling: {self.disable_error_handling}, seed_n_key_dll: '{self.seed_n_key_dll}', seed_n_key_dll_same_bit_width: {self.seed_n_key_dll_same_bit_width})" + ) # + + +class CanAlystii(SingletonConfigurable): + baud = Integer(allow_none=True).tag(config=True) + timing0 = Integer(allow_none=True).tag(config=True) + timing1 = Integer(allow_none=True).tag(config=True) + + PARAMETER_TO_KW_ARG_MAP = { + "BAUD": "baud", + "TIMING0": "timing0", + "TIMING1": "timing1", + } + + def __str__(self): + return str(f"CanAlystii(baud={self.baud}, timing0={self.timing0}, timing1={self.timing1})") + + +class Etas(SingletonConfigurable): + fd = Bool(False).tag(config=True) + data_bitrate = Integer(250000).tag(config=True) + + PARAMETER_TO_KW_ARG_MAP = { + "fd": "fd", + "data_bitrate": "data_bitrate", + } + + def __str__(self): + return str(f"Etas(fd={self.fd}, data_bitrate={self.data_bitrate})") + + +""" +candriver/iscan (pyXCP.Transport.CAN.iscan) +------------------------------------------- + PARAMETER_MAP = { + # Type Req'd Default + "POLL_INTERVAL": (float, False, 0.01), + } + + PARAMETER_TO_KW_ARG_MAP = { + "POLL_INTERVAL": "poll_interval", + } +""" + + +class CAN(SingletonConfigurable): + can_driver = Unicode("").tag(config=True) + channel = Unicode("").tag(config=True) + max_dlc_required = Bool(False).tag(config=True) + max_can_fd_dlc = Integer(64).tag(config=True) + padding_value = Integer(0).tag(config=True) + can_use_default_listener = Bool(True).tag(config=True) + can_id_master = Integer().tag(config=True) + can_id_slave = Integer().tag(config=True) + can_id_broadcast = Integer().tag(config=True) + bitrate = Integer(250000).tag(config=True) + receive_own_messages = Bool(False).tag(config=True) + + classes = List([CanAlystii, Etas]) + + etas = Instance(Etas).tag(config=True) + canalystii = Instance(CanAlystii).tag(config=True) + + def __init__(self, **kws): + super().__init__(**kws) + self.etas = Etas.instance(config=self.config, parent=self) + self.canalystii = CanAlystii.instance(config=self.config, parent=self) + + def __str__(self): + # return f"Can(can_driver='{self.can_driver}', channel='{self.channel}', max_dlc_required={self.max_dlc_required}, max_can_fd_dlc={self.max_can_fd_dlc}, padding_value={self.padding_value}), CanAlystii={self.CanAlystii}, Etas={self.Etas}" + return f"Can(can_driver='{self.can_driver}', channel='{self.channel}', bitrate={self.bitrate}, max_dlc_required={self.max_dlc_required}, max_can_fd_dlc={self.max_can_fd_dlc}, padding_value={self.padding_value})" + + +class Eth(SingletonConfigurable): + """ + transport/eth (pyXCP.Transport.Eth) + ----------------------------------- + "HOST": (str, False, "localhost"), + "PORT": (int, False, 5555), + "PROTOCOL": (str, False, "TCP"), + "IPV6": (bool, False, False), + "TCP_NODELAY": (bool, False, False), + """ + + host = Unicode("localhost").tag(config=True) + port = Integer(5555).tag(config=True) + protocol = Unicode("TCP").tag(config=True) + ipv6 = Bool(False).tag(config=True) + tcp_nodelay = Bool(False).tag(config=True) + + def __str__(self): + return f"Eth(host='{self.host}', port={self.port}, protocol='{self.protocol}', ipv6={self.ipv6}, tcp_nodelay={self.tcp_nodelay})" + + +class SxI(SingletonConfigurable): + """ + transport/sxi (pyXCP.Transport.SxI) + ----------------------------------- + "PORT": (str, False, "COM1"), + "BITRATE": (int, False, 38400), + "BYTESIZE": (int, False, 8), + "PARITY": (str, False, "N"), + "STOPBITS": (int, False, 1), + + """ + + port = Unicode("COM1").tag(config=True) + bitrate = Integer(38400).tag(config=True) + bytesize = Integer(8).tag(config=True) + parity = Unicode("N").tag(config=True) + stopbits = Integer(1).tag(config=True) + + def __str__(self): + return f"SxI(port='{self.port}', bitrate={self.bitrate}, bytesize={self.bytesize}, parity='{self.parity}', stopbits={self.stopbits})" + + +class USB(SingletonConfigurable): + """ + transport/usb_transport (pyXCP.Transport.USB) + --------------------------------------------- + "serial_number": (str, True, ""), + "configuration_number": (int, True, 1), + "interface_number": (int, True, 2), + "command_endpoint_number": (int, True, 0), + "reply_endpoint_number": (int, True, 1), + """ + + serial_number = Unicode("").tag(config=True) + configuration_number = Integer(1).tag(config=True) + interface_number = Integer(2).tag(config=True) + command_endpoint_number = Integer(0).tag(config=True) + reply_endpoint_number = Integer(1).tag(config=True) + + def __str__(self): + return f"USB(serial_number='{self.serial_number}', configuration_number={self.configuration_number}, interface_number={self.interface_number}, command_endpoint_number={self.command_endpoint_number}, reply_endpoint_number={self.reply_endpoint_number})" + + +class Transport(SingletonConfigurable): + """ """ + + classes = List([CAN, Eth, SxI, USB]) + + layer = Enum(["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=False).tag(config=True) # Enum + create_daq_timestamps = Bool(False).tag(config=True) + timeout = Float(2.0).tag(config=True) + alignment = Enum([1, 2, 4, 8], default_value=1).tag(config=True) + + can = Instance(CAN).tag(config=True) + eth = Instance(Eth).tag(config=True) + sxi = Instance(SxI).tag(config=True) + usb = Instance(USB).tag(config=True) + + def __init__(self, **kws): + super().__init__(**kws) + self.can = CAN.instance(config=self.config, parent=self) + self.eth = Eth.instance(config=self.config, parent=self) + self.sxi = SxI.instance(config=self.config, parent=self) + self.usb = USB.instance(config=self.config, parent=self) + + def __str__(self): + return str( + f"Transport(layer='{self.layer}', create_daq_timestamps={self.create_daq_timestamps}, timeout={self.timeout}, alignment={self.alignment}), Can={self.can}, Eth={self.eth}, SxI={self.sxi}, USB={self.usb}" + ) + + +class PyXCP(Application): + config_file = Unicode(default_value="pyxcp_conf.py", help="base name of config file").tag(config=True) + + classes = List([General, Transport]) + + def initialize(self, argv=None): + self.parse_command_line(argv) + if self.config_file: + self.load_config_file(self.config_file) + # res = load_pyconfig_files([self.config_file], "c:\csprojects") + # print("Loaded CFG-File: ", self.config_file, res, self.config) + print("SC", self.config) + self.general = General.instance(config=self.config, parent=self) + self.transport = Transport.instance(parent=self) # Transport(config=self.config, parent=self) + + def confy(self): + for klass in app._classes_with_config_traits(): + # pprint(klass.class_config_section()) + if hasattr(klass, "classes"): + print("KLASEES", klass.classes) + for name, tr_type in klass._traits.items(): + if isinstance(tr_type, Instance): + print(name, tr_type) + # ctr = klass.class_traits(config=True) + + def __str__(self): + return f"PyXCP: {self.config.general}" + + +class Configuration: + pass + + +# PyXCP.launch_instance() # "conf_test.py" +# app = PyXCP.instance() +app = PyXCP() + +app.initialize(sys.argv) +app.start() + +print(app.config) +print(app.general.loglevel) +# print("TR:", app.config.transport.alignment) +print("TR:", app.transport) +print("CN:", app.transport.can) +print("ET:", app.transport.eth) +print("GN:", app.general) + +ci = CAN.instance() + +# print("SX:", app.transport.sxi) +# print("US:", app.transport.usb) + +# print("etas", app.transport.can.etas) +# print(app.confy()) +# print(app.generate_config_file()) + + +def readConfiguration(phile: io.TextIOWrapper): + pth = Path(phile.name) + suffix = pth.suffix.lower() + if suffix == ".py": + pass + else: + if suffix == ".json": + reader = json + elif suffix == ".toml": + reader = toml + else: + raise ValueError(f"Unknown file type for config: {suffix}") + with pth.open("r") as f: + cfg = reader.loads(f.read()) + if cfg: + cfg = legacy.convert_config(cfg) + return cfg diff --git a/pyxcp/config/legacy.py b/pyxcp/config/legacy.py new file mode 100644 index 0000000..048c06a --- /dev/null +++ b/pyxcp/config/legacy.py @@ -0,0 +1,59 @@ +import warnings +from collections import defaultdict + +from traitlets.config.loader import Config + +# General +""" + "LOGLEVEL": (str, False, "WARN"), + "DISABLE_ERROR_HANDLING": ( + bool, + False, + False, + ), # Bypass error-handling for performance reasons. + "SEED_N_KEY_DLL": (str, False, ""), + "SEED_N_KEY_DLL_SAME_BIT_WIDTH": (bool, False, False), + "DISCONNECT_RESPONSE_OPTIONAL": (bool, False, False), +================ +loglevel = Unicode("WARN").tag(config=True) +disable_error_handling = Bool(False).tag(config=True) +seed_n_key_dll = Unicode(allow_none=True, default_value=None).tag(config=True) +seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) +""" +LEGACY_KEYWORDS = { + # Transport + "TRANSPORT": "Transport.layer", + "CREATE_DAQ_TIMESTAMPS": "Transport.create_daq_timestamps", + "TIMEOUT": "Transport.timeout", + "ALIGNMENT": "Transport.alignment", + # Eth + "HOST": "Eth.host", + "PORT": "Eth.port", + "PROTOCOL": "Eth.protocol", + "IPV6": "Eth.ipv6", + "TCP_NODELAY": "Eth.tcp_nodelay", +} + +""" +{'General': {'seed_n_key_dll': 'vector_xcp.dll'}, + 'Transport': {'CAN': {'bitrate': 10000, 'channel': '123'}, + 'alignment': 2, + 'layer': 'USB', + 'timeout': 3.5}} +""" + + +def nested_update(d: dict, key: str, value) -> dict: + root, *path, key = key.split(".") + if path: + print("???") + d[root][key] = value + + +def convert_config(legacy_config: dict) -> Config: + d = defaultdict(dict) + for key, value in legacy_config.items(): + item = LEGACY_KEYWORDS.get(key) + print(key, value) + nested_update(d=d, key=item, value=value) + return Config(d) From cf459721ab33b38e78a8126b228aa98bd6ae38b8 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 30 Aug 2023 15:29:47 +0200 Subject: [PATCH 11/99] today() --- pyxcp/config/__init__.py | 5 +++-- pyxcp/config/legacy.py | 45 ++++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 8c3338e..cdaff00 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -30,6 +30,7 @@ class General(SingletonConfigurable): loglevel = Unicode("WARN").tag(config=True) disable_error_handling = Bool(False).tag(config=True) + disconnect_response_optional = Bool(False).tag(config=True) seed_n_key_dll = Unicode(allow_none=True, default_value=None).tag(config=True) seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) @@ -82,12 +83,12 @@ def __str__(self): class CAN(SingletonConfigurable): - can_driver = Unicode("").tag(config=True) + driver = Unicode("").tag(config=True) channel = Unicode("").tag(config=True) max_dlc_required = Bool(False).tag(config=True) max_can_fd_dlc = Integer(64).tag(config=True) padding_value = Integer(0).tag(config=True) - can_use_default_listener = Bool(True).tag(config=True) + use_default_listener = Bool(True).tag(config=True) can_id_master = Integer().tag(config=True) can_id_slave = Integer().tag(config=True) can_id_broadcast = Integer().tag(config=True) diff --git a/pyxcp/config/legacy.py b/pyxcp/config/legacy.py index 048c06a..6b1e348 100644 --- a/pyxcp/config/legacy.py +++ b/pyxcp/config/legacy.py @@ -3,35 +3,36 @@ from traitlets.config.loader import Config -# General -""" - "LOGLEVEL": (str, False, "WARN"), - "DISABLE_ERROR_HANDLING": ( - bool, - False, - False, - ), # Bypass error-handling for performance reasons. - "SEED_N_KEY_DLL": (str, False, ""), - "SEED_N_KEY_DLL_SAME_BIT_WIDTH": (bool, False, False), - "DISCONNECT_RESPONSE_OPTIONAL": (bool, False, False), -================ -loglevel = Unicode("WARN").tag(config=True) -disable_error_handling = Bool(False).tag(config=True) -seed_n_key_dll = Unicode(allow_none=True, default_value=None).tag(config=True) -seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) -""" LEGACY_KEYWORDS = { + # General + "LOGLEVEL": "General.loglevel", + "DISABLE_ERROR_HANDLING": "General.disable_error_handling", + "SEED_N_KEY_DLL": "General.seed_n_key_dll", + "SEED_N_KEY_DLL_SAME_BIT_WIDTH": "General.seed_n_key_dll_same_bit_width", + "DISCONNECT_RESPONSE_OPTIONAL": "General.disconnect_response_optional", # Transport "TRANSPORT": "Transport.layer", "CREATE_DAQ_TIMESTAMPS": "Transport.create_daq_timestamps", "TIMEOUT": "Transport.timeout", "ALIGNMENT": "Transport.alignment", # Eth - "HOST": "Eth.host", - "PORT": "Eth.port", - "PROTOCOL": "Eth.protocol", - "IPV6": "Eth.ipv6", - "TCP_NODELAY": "Eth.tcp_nodelay", + "HOST": "Transport.Eth.host", + "PORT": "Transport.Eth.port", + "PROTOCOL": "Transport.Eth.protocol", + "IPV6": "Transport.Eth.ipv6", + "TCP_NODELAY": "Transport.Eth.tcp_nodelay", + # Can + "CAN_DRIVER": "Transport.Can.driver", + "CHANNEL": "Transport.Can.channel", + "MAX_DLC_REQUIRED": "Transport.Can.max_dlc_required", + "MAX_CAN_FD_DLC": "Transport.Can.max_can_fd_dlc", + "PADDING_VALUE": "Transport.Can.padding_value", + "CAN_USE_DEFAULT_LISTENER": "Transport.Can.use_default_listener", + "CAN_ID_MASTER": "Transport.Can.can_id_master", + "CAN_ID_SLAVE": "Transport.Can.can_id_slave", + "CAN_ID_BROADCAST": "Transport.Can.can_id_broadcast", + "BITRATE": "Transport.Can.bitrate", + "RECEIVE_OWN_MESSAGES": "Transport.Can.receive_own_messages", } """ From d797c8cd3b0fd19ba255848f730e06a2f6490e9d Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 11 Sep 2023 17:14:16 +0200 Subject: [PATCH 12/99] today() --- pyxcp/cmdline.py | 15 +- pyxcp/config/__init__.py | 623 +++++++++++++++++++++++++------ pyxcp/config/legacy.py | 21 +- pyxcp/master/errorhandler.py | 4 +- pyxcp/master/master.py | 27 +- pyxcp/transport/base.py | 31 +- pyxcp/transport/can.py | 14 +- pyxcp/transport/eth.py | 18 +- pyxcp/transport/sxi.py | 55 +-- pyxcp/transport/usb_transport.py | 16 +- 10 files changed, 613 insertions(+), 211 deletions(-) diff --git a/pyxcp/cmdline.py b/pyxcp/cmdline.py index e31bf99..d1561a6 100644 --- a/pyxcp/cmdline.py +++ b/pyxcp/cmdline.py @@ -6,6 +6,7 @@ """ import argparse +from pyxcp.config import application from pyxcp.config import readConfiguration from pyxcp.master import Master from pyxcp.transport.can import registered_drivers @@ -56,11 +57,15 @@ def run(self, policy=None): if args.conf is None: raise RuntimeError("Configuration file must be specified! (option: -c )") config = readConfiguration(args.conf) - config["LOGLEVEL"] = args.loglevel - if "TRANSPORT" not in config: - raise AttributeError("TRANSPORT must be specified in config!") - transport = config["TRANSPORT"].lower() - master = Master(transport, config=config, policy=policy) + from pprint import pprint + + pprint(config) + # config["LOGLEVEL"] = args.loglevel + # if "TRANSPORT" not in config: + # raise AttributeError("TRANSPORT must be specified in config!") + # transport = config["TRANSPORT"].lower() + transport = application.transport.layer + master = Master(transport, config=application, policy=policy) if self.callout: self.callout(master, args) return master diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index cdaff00..8d96014 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -7,7 +7,9 @@ from pathlib import Path from pprint import pprint +import can import toml +from traitlets import Any from traitlets import Bool from traitlets import Enum from traitlets import Float @@ -15,6 +17,7 @@ from traitlets import Integer from traitlets import List from traitlets import Unicode +from traitlets import Union from traitlets.config import Application from traitlets.config import Configurable from traitlets.config import Instance @@ -25,150 +28,548 @@ from pyxcp.config import legacy -class General(SingletonConfigurable): - """ """ +class CanBase: + has_fd = False + has_bitrate = True + has_data_bitrate = False + has_poll_interval = False + has_receive_own_messages = False + has_timing = False + + can_param_map = { + "sjw_abr": None, + "tseg1_abr": None, + "tseg2_abr": None, + "sjw_dbr": None, + "tseg1_dbr": None, + "tseg2_dbr": None, + } - loglevel = Unicode("WARN").tag(config=True) - disable_error_handling = Bool(False).tag(config=True) - disconnect_response_optional = Bool(False).tag(config=True) - seed_n_key_dll = Unicode(allow_none=True, default_value=None).tag(config=True) - seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) - def __str__(self): - return str( - f"General(loglevel: '{self.loglevel}', disable_error_handling: {self.disable_error_handling}, seed_n_key_dll: '{self.seed_n_key_dll}', seed_n_key_dll_same_bit_width: {self.seed_n_key_dll_same_bit_width})" - ) # +class CanAlystii(SingletonConfigurable, CanBase): + """CANalyst-II is a USB to CAN Analyzer device produced by Chuangxin Technology.""" + interface_name = "canalystii" -class CanAlystii(SingletonConfigurable): - baud = Integer(allow_none=True).tag(config=True) - timing0 = Integer(allow_none=True).tag(config=True) - timing1 = Integer(allow_none=True).tag(config=True) + has_timing = True - PARAMETER_TO_KW_ARG_MAP = { - "BAUD": "baud", - "TIMING0": "timing0", - "TIMING1": "timing1", - } + device = Integer(default_value=None, allow_none=True, help="""Optional USB device number.""").tag(config=True) + rx_queue_size = Integer( + default_value=None, + allow_none=True, + help="""If set, software received message queue can only grow to this many +messages (for all channels) before older messages are dropped """, + ).tag(config=True) + + +class CanTact(SingletonConfigurable, CanBase): + """Interface for CANtact devices from Linklayer Labs""" + + interface_name = "cantact" + + has_poll_interval = True + has_timing = True + + monitor = Bool(default_value=False, allow_none=True, help="""If true, operate in listen-only monitoring mode""").tag( + config=True + ) + + +class Etas(SingletonConfigurable, CanBase): + """ETAS""" + + interface_name = "etas" + + has_fd = True + has_data_bitrate = True + has_receive_own_messages = True - def __str__(self): - return str(f"CanAlystii(baud={self.baud}, timing0={self.timing0}, timing1={self.timing1})") +class Gs_Usb(SingletonConfigurable, CanBase): + """Geschwister Schneider USB/CAN devices and candleLight USB CAN interfaces.""" -class Etas(SingletonConfigurable): - fd = Bool(False).tag(config=True) - data_bitrate = Integer(250000).tag(config=True) + interface_name = "gs_usb" - PARAMETER_TO_KW_ARG_MAP = { - "fd": "fd", - "data_bitrate": "data_bitrate", + index = Integer( + default_value=None, + allow_none=True, + help="""device number if using automatic scan, starting from 0. +If specified, bus/address shall not be provided.""", + ).tag(config=True) + bus = Integer(default_value=None, allow_none=True, help="""number of the bus that the device is connected to""").tag( + config=True + ) + address = Integer(default_value=None, allow_none=True, help="""address of the device on the bus it is connected to""").tag( + config=True + ) + + +class Ics_Neovi(SingletonConfigurable, CanBase): + """Intrepid Control Systems (ICS) neoVI interfaces.""" + + interface_name = "ics_neovi" + + has_fd = True + has_data_bitrate = True + has_receive_own_messages = True + + use_system_timestamp = Bool( + default_value=None, allow_none=True, help="Use system timestamp for can messages instead of the hardware timestamp" + ).tag(config=True) + serial = Unicode( + default_value=None, allow_none=True, help="Serial to connect (optional, will use the first found if not supplied)" + ).tag(config=True) + override_library_name = Unicode( + default_value=None, allow_none=True, help="Absolute path or relative path to the library including filename." + ).tag(config=True) + + +class IsCan(SingletonConfigurable, CanBase): + """Interface for isCAN from Thorsis Technologies GmbH, former ifak system GmbH.""" + + interface_name = "iscan" + + has_poll_interval = True + + +class Ixxat(SingletonConfigurable, CanBase): + """IXXAT Virtual Communication Interface""" + + interface_name = "ixxat" + + has_fd = True + has_data_bitrate = True + has_receive_own_messages = True + + unique_hardware_id = Integer( + default_value=None, + allow_none=True, + help="""UniqueHardwareId to connect (optional, will use the first found if not supplied)""", + ).tag(config=True) + extended = Bool(default_value=None, allow_none=True, help="""Enables the capability to use extended IDs.""").tag(config=True) + rx_fifo_size = Integer(default_value=None, allow_none=True, help="""Receive fifo size""").tag(config=True) + tx_fifo_size = Integer(default_value=None, allow_none=True, help="""Transmit fifo size""").tag(config=True) + ssp_dbr = Integer( + default_value=None, + allow_none=True, + help="Secondary sample point (data). Only takes effect with fd and bitrate switch enabled.", + ).tag(config=True) + can_param_map = { + "sjw_abr": "sjw_abr", + "tseg1_abr": "tseg1_abr", + "tseg2_abr": "tseg2_abr", + "sjw_dbr": "sjw_dbr", + "tseg1_dbr": "tseg1_dbr", + "tseg2_dbr": "tseg2_dbr", } - def __str__(self): - return str(f"Etas(fd={self.fd}, data_bitrate={self.data_bitrate})") +class Kvaser(SingletonConfigurable, CanBase): + """Kvaser's CANLib""" -""" -candriver/iscan (pyXCP.Transport.CAN.iscan) -------------------------------------------- - PARAMETER_MAP = { - # Type Req'd Default - "POLL_INTERVAL": (float, False, 0.01), + interface_name = "kvaser" + + has_fd = True + has_data_bitrate = True + has_receive_own_messages = True + + can_param_map = { + "sjw_abr": "sjw", + "tseg1_abr": "tseg1", + "tseg2_abr": "tseg2", } - PARAMETER_TO_KW_ARG_MAP = { - "POLL_INTERVAL": "poll_interval", + accept_virtual = Bool(default_value=None, allow_none=True, help="If virtual channels should be accepted.").tag(config=True) + no_samp = Enum( + [1, 3], + default_value=None, + allow_none=True, + help="""Either 1 or 3. Some CAN controllers can also sample each bit three times. +In this case, the bit will be sampled three quanta in a row, +with the last sample being taken in the edge between TSEG1 and TSEG2. +Three samples should only be used for relatively slow baudrates""", + ).tag(config=True) + driver_mode = Bool(default_value=None, allow_none=True, help="Silent or normal.").tag(config=True) + single_handle = Bool( + default_value=None, + allow_none=True, + help="""Use one Kvaser CANLIB bus handle for both reading and writing. +This can be set if reading and/or writing is done from one thread. """, + ).tag(config=True) + + +class NeouSys(SingletonConfigurable, CanBase): + """Neousys CAN Interface""" + + interface_name = "neousys" + + device = Integer(default_value=None, allow_none=True, help="Device number").tag(config=True) + + +class NiCan(SingletonConfigurable, CanBase): + """National Instruments NI-CAN""" + + interface_name = "nican" + + log_errors = Bool( + default_value=None, + allow_none=True, + help="""If True, communication errors will appear as CAN messages with +``is_error_frame`` set to True and ``arbitration_id`` will identify +the error. """, + ).tag(config=True) + + +class NixNet(SingletonConfigurable, CanBase): + """National Instruments NI-XNET""" + + interface_name = "nixnet" + + has_poll_interval = True + has_receive_own_messages = True + has_timing = True + + +class PCan(SingletonConfigurable, CanBase): + """PCAN Basic API""" + + interface_name = "pcan" + + has_fd = True + has_timing = True + + can_param_map = { + "sjw_abr": "nom_sjw", + "tseg1_abr": "nom_tseg1", + "tseg2_abr": "nom_tseg2", + "sjw_dbr": "data_sjw", + "tseg1_dbr": "data_tseg1", + "tseg2_dbr": "data_tseg2", } -""" + + device_id = Integer( + default_value=None, + allow_none=True, + help="""Select the PCAN interface based on its ID. The device ID is a 8/32bit +value that can be configured for each PCAN device. If you set the +device_id parameter, it takes precedence over the channel parameter. +The constructor searches all connected interfaces and initializes the +first one that matches the parameter value. If no device is found, +an exception is raised.""", + ).tag(config=True) + state = Instance(can.bus.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) + + +# f_clock = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# f_clock_mhz = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + +# nom_brp = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# data_brp = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + +# auto_reset = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class Robotell(SingletonConfigurable, CanBase): + """ """ + + interface_name = "robotell" + + +# ttyBaudrate = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# rtscts = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class SeeedStudio(SingletonConfigurable, CanBase): + """ """ + + interface_name = "seeedstudio" + + +# timeout = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# baudrate = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# frame_type = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# operation_mode = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class Serial(SingletonConfigurable, CanBase): + """ """ + + interface_name = "serial" + + has_bitrate = False + + +# rtscts = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# timeout = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# baudrate = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class SlCan(SingletonConfigurable, CanBase): + """ """ + + interface_name = "slcan" + + has_poll_interval = True + + +# ttyBaudrate = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# rtscts = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# timeout = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# btr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# sleep_after_open = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class SocketCan(SingletonConfigurable, CanBase): + """ """ + + interface_name = "socketcan" + + has_fd = True + has_bitrate = False + has_receive_own_messages = True + + +class SocketCanD(SingletonConfigurable, CanBase): + """ """ + + interface_name = "socketcand" + + has_bitrate = False + + +# host = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# port = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class Systec(SingletonConfigurable, CanBase): + """ """ + + interface_name = "systec" + + has_receive_own_messages = True + + +# state = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# device_number = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# rx_buffer_entries = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# tx_buffer_entries = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class Udp_Multicast(SingletonConfigurable, CanBase): + """ """ + + interface_name = "udp_multicast" + + has_fd = True + has_bitrate = False + has_receive_own_messages = True + + +# port = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# hop_limit = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class Usb2Can(SingletonConfigurable, CanBase): + """ """ + + interface_name = "usb2can" + + +# flags = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# dll = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# serial.1 = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class Vector(SingletonConfigurable, CanBase): + """ """ + + interface_name = "vector" + + has_fd = True + has_data_bitrate = True + has_poll_interval = True + has_receive_own_messages = True + has_timing = True + + +# sjw_abr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# tseg1_abr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# tseg2_abr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# sjw_dbr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# tseg1_dbr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# tseg2_dbr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# serial.1 = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# rx_queue_size.1 = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# app_name = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +class Virtual(SingletonConfigurable, CanBase): + """ """ + + interface_name = "virtual" + + has_bitrate = False + has_receive_own_messages = True + + +# rx_queue_size.1 = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# preserve_timestamps = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) +# protocol = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) class CAN(SingletonConfigurable): - driver = Unicode("").tag(config=True) - channel = Unicode("").tag(config=True) - max_dlc_required = Bool(False).tag(config=True) - max_can_fd_dlc = Integer(64).tag(config=True) - padding_value = Integer(0).tag(config=True) - use_default_listener = Bool(True).tag(config=True) - can_id_master = Integer().tag(config=True) - can_id_slave = Integer().tag(config=True) - can_id_broadcast = Integer().tag(config=True) - bitrate = Integer(250000).tag(config=True) - receive_own_messages = Bool(False).tag(config=True) - - classes = List([CanAlystii, Etas]) - - etas = Instance(Etas).tag(config=True) - canalystii = Instance(CanAlystii).tag(config=True) + interface = Unicode("", help="").tag(config=True) + channel = Any(help="").tag(config=True) + max_dlc_required = Bool(False, help="Master to slave frames always to have DLC = MAX_DLC = 8").tag(config=True) + max_can_fd_dlc = Integer(64, help="").tag(config=True) + padding_value = Integer(0, help="Fill value, if max_dlc_required == True and DLC < MAX_DLC").tag(config=True) + use_default_listener = Bool(True, help="").tag(config=True) + can_id_master = Integer(default_value=None, allow_none=True, help="CAN-ID master -> slave (Bit31= 1: extended identifier)").tag( + config=True + ) + can_id_slave = Integer(default_value=None, allow_none=True, help="CAN-ID slave -> master (Bit31= 1: extended identifier)").tag( + config=True + ) + can_id_broadcast = Integer( + default_value=None, allow_none=True, help="Auto detection CAN-ID (Bit31= 1: extended identifier)" + ).tag(config=True) + bitrate = Integer(250000, help="CAN bitrate in bits/s (arbitration phase, if CAN FD).").tag(config=True) + receive_own_messages = Bool(False, help="Enable self-reception of sent messages.").tag(config=True) + poll_interval = Float(default_value=None, allow_none=True, help="Poll interval in seconds when reading messages.").tag( + config=True + ) + fd = Bool(False, help="If CAN-FD frames should be supported.").tag(config=True) + data_bitrate = Integer(default_value=None, allow_none=True, help="Which bitrate to use for data phase in CAN FD.").tag( + config=True + ) + sjw_abr = Integer( + default_value=None, allow_none=True, help="Bus timing value sample jump width (arbitration, SJW if CAN classic)." + ).tag(config=True) + tseg1_abr = Integer( + default_value=None, allow_none=True, help="Bus timing value tseg1 (arbitration, TSEG1 if CAN classic)." + ).tag(config=True) + tseg2_abr = Integer( + default_value=None, allow_none=True, help="Bus timing value tseg2 (arbitration, TSEG2, if CAN classic)" + ).tag(config=True) + sjw_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value sample jump width (data).").tag(config=True) + tseg1_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg1 (data).").tag(config=True) + tseg2_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg2 (data).").tag(config=True) + timing = Union( + [Instance(can.BitTiming), Instance(can.BitTimingFd)], + default_value=None, + allow_none=True, + help="""Custom bit timing settings. + (.s https://github.com/hardbyte/python-can/blob/develop/can/bit_timing.py) + If this parameter is provided, it takes precedence over all other + timing-related parameters. + """, + ).tag(config=True) + + EXCLUDE_FROM_DRIVERS = () + + classes = List( + [ + CanAlystii, + CanTact, + Etas, + Gs_Usb, + Ics_Neovi, + IsCan, + Ixxat, + Kvaser, + NeouSys, + NiCan, + NixNet, + PCan, + Robotell, + SeeedStudio, + Serial, + SlCan, + SocketCan, + SocketCanD, + Systec, + Udp_Multicast, + Usb2Can, + Vector, + Virtual, + ] + ) def __init__(self, **kws): super().__init__(**kws) - self.etas = Etas.instance(config=self.config, parent=self) + self.canalystii = CanAlystii.instance(config=self.config, parent=self) + self.cantact = CanTact.instance(config=self.config, parent=self) + self.etas = Etas.instance(config=self.config, parent=self) + self.gs_usb = Gs_Usb.instance(config=self.config, parent=self) + self.ics_neovi = Ics_Neovi.instance(config=self.config, parent=self) + self.iscan = IsCan.instance(config=self.config, parent=self) + self.ixxat = Ixxat.instance(config=self.config, parent=self) + self.kvaser = Kvaser.instance(config=self.config, parent=self) + self.neousys = NeouSys.instance(config=self.config, parent=self) + self.nican = NiCan.instance(config=self.config, parent=self) + self.nixnet = NixNet.instance(config=self.config, parent=self) + self.pcan = PCan.instance(config=self.config, parent=self) + self.robotell = Robotell.instance(config=self.config, parent=self) + self.seeedstudio = SeeedStudio.instance(config=self.config, parent=self) + self.serial = Serial.instance(config=self.config, parent=self) + self.slcan = SlCan.instance(config=self.config, parent=self) + self.socketcan = SocketCan.instance(config=self.config, parent=self) + self.socketcand = SocketCanD.instance(config=self.config, parent=self) + self.systec = Systec.instance(config=self.config, parent=self) + self.udp_multicast = Udp_Multicast.instance(config=self.config, parent=self) + self.usb2can = Usb2Can.instance(config=self.config, parent=self) + self.vector = Vector.instance(config=self.config, parent=self) + self.virtual = Virtual.instance(config=self.config, parent=self) def __str__(self): # return f"Can(can_driver='{self.can_driver}', channel='{self.channel}', max_dlc_required={self.max_dlc_required}, max_can_fd_dlc={self.max_can_fd_dlc}, padding_value={self.padding_value}), CanAlystii={self.CanAlystii}, Etas={self.Etas}" - return f"Can(can_driver='{self.can_driver}', channel='{self.channel}', bitrate={self.bitrate}, max_dlc_required={self.max_dlc_required}, max_can_fd_dlc={self.max_can_fd_dlc}, padding_value={self.padding_value})" + return f"Can(can_driver='{self.interface}', channel='{self.channel}', bitrate={self.bitrate}, max_dlc_required={self.max_dlc_required}, max_can_fd_dlc={self.max_can_fd_dlc}, padding_value={self.padding_value})" class Eth(SingletonConfigurable): - """ - transport/eth (pyXCP.Transport.Eth) - ----------------------------------- - "HOST": (str, False, "localhost"), - "PORT": (int, False, 5555), - "PROTOCOL": (str, False, "TCP"), - "IPV6": (bool, False, False), - "TCP_NODELAY": (bool, False, False), - """ + """ """ host = Unicode("localhost").tag(config=True) port = Integer(5555).tag(config=True) - protocol = Unicode("TCP").tag(config=True) + protocol = Enum(["TCP", "UDP"], default_value="UDP").tag(config=True) ipv6 = Bool(False).tag(config=True) tcp_nodelay = Bool(False).tag(config=True) + bind_to_address = Unicode(default_value=None, allow_none=True, help="Specific local address.").tag(config=True) + bind_to_port = Integer(default_value=None, allow_none=True, help="Specific local port.").tag(config=True) def __str__(self): return f"Eth(host='{self.host}', port={self.port}, protocol='{self.protocol}', ipv6={self.ipv6}, tcp_nodelay={self.tcp_nodelay})" class SxI(SingletonConfigurable): - """ - transport/sxi (pyXCP.Transport.SxI) - ----------------------------------- - "PORT": (str, False, "COM1"), - "BITRATE": (int, False, 38400), - "BYTESIZE": (int, False, 8), - "PARITY": (str, False, "N"), - "STOPBITS": (int, False, 1), + """SPI and SCI connections.""" - """ + port = Unicode("COM1", help="").tag(config=True) + bitrate = Integer(38400, help="").tag(config=True) + bytesize = Enum([5, 6, 7, 8], default_value=8, help="").tag(config=True) + parity = Enum(["N", "E", "O", "M", "S"], default_value="N", help="").tag(config=True) + stopbits = Enum([1, 1.5, 2], default_value=1, help="").tag(config=True) - port = Unicode("COM1").tag(config=True) - bitrate = Integer(38400).tag(config=True) - bytesize = Integer(8).tag(config=True) - parity = Unicode("N").tag(config=True) - stopbits = Integer(1).tag(config=True) + """ + -prot Set the SxI protocol type SYNC = 1,CTR = 2,SYNC+CTR = 3 (Default 0) + -cs Set the SxI checksum type LEN+CTR+PACKETS = 1, ONLY PACKETS = 2 (Default 0 no checksum) + """ def __str__(self): return f"SxI(port='{self.port}', bitrate={self.bitrate}, bytesize={self.bytesize}, parity='{self.parity}', stopbits={self.stopbits})" class USB(SingletonConfigurable): - """ - transport/usb_transport (pyXCP.Transport.USB) - --------------------------------------------- - "serial_number": (str, True, ""), - "configuration_number": (int, True, 1), - "interface_number": (int, True, 2), - "command_endpoint_number": (int, True, 0), - "reply_endpoint_number": (int, True, 1), - """ + """ """ serial_number = Unicode("").tag(config=True) configuration_number = Integer(1).tag(config=True) interface_number = Integer(2).tag(config=True) command_endpoint_number = Integer(0).tag(config=True) reply_endpoint_number = Integer(1).tag(config=True) + vendor_id = Integer(0).tag(config=True) + product_id = Integer(0).tag(config=True) def __str__(self): return f"USB(serial_number='{self.serial_number}', configuration_number={self.configuration_number}, interface_number={self.interface_number}, command_endpoint_number={self.command_endpoint_number}, reply_endpoint_number={self.reply_endpoint_number})" @@ -202,6 +603,21 @@ def __str__(self): ) +class General(SingletonConfigurable): + """ """ + + loglevel = Unicode("WARN").tag(config=True) + disable_error_handling = Bool(False).tag(config=True) + disconnect_response_optional = Bool(False).tag(config=True) + seed_n_key_dll = Unicode("", allow_none=False).tag(config=True) + seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) + + def __str__(self): + return str( + f"General(loglevel: '{self.loglevel}', disable_error_handling: {self.disable_error_handling}, seed_n_key_dll: '{self.seed_n_key_dll}', seed_n_key_dll_same_bit_width: {self.seed_n_key_dll_same_bit_width})" + ) + + class PyXCP(Application): config_file = Unicode(default_value="pyxcp_conf.py", help="base name of config file").tag(config=True) @@ -218,7 +634,7 @@ def initialize(self, argv=None): self.transport = Transport.instance(parent=self) # Transport(config=self.config, parent=self) def confy(self): - for klass in app._classes_with_config_traits(): + for klass in application._classes_with_config_traits(): # pprint(klass.class_config_section()) if hasattr(klass, "classes"): print("KLASEES", klass.classes) @@ -235,29 +651,10 @@ class Configuration: pass -# PyXCP.launch_instance() # "conf_test.py" -# app = PyXCP.instance() -app = PyXCP() - -app.initialize(sys.argv) -app.start() - -print(app.config) -print(app.general.loglevel) -# print("TR:", app.config.transport.alignment) -print("TR:", app.transport) -print("CN:", app.transport.can) -print("ET:", app.transport.eth) -print("GN:", app.general) - -ci = CAN.instance() - -# print("SX:", app.transport.sxi) -# print("US:", app.transport.usb) +application = PyXCP() -# print("etas", app.transport.can.etas) -# print(app.confy()) -# print(app.generate_config_file()) +application.initialize(sys.argv) +application.start() def readConfiguration(phile: io.TextIOWrapper): diff --git a/pyxcp/config/legacy.py b/pyxcp/config/legacy.py index 6b1e348..1300e4d 100644 --- a/pyxcp/config/legacy.py +++ b/pyxcp/config/legacy.py @@ -35,20 +35,15 @@ "RECEIVE_OWN_MESSAGES": "Transport.Can.receive_own_messages", } -""" -{'General': {'seed_n_key_dll': 'vector_xcp.dll'}, - 'Transport': {'CAN': {'bitrate': 10000, 'channel': '123'}, - 'alignment': 2, - 'layer': 'USB', - 'timeout': 3.5}} -""" - -def nested_update(d: dict, key: str, value) -> dict: +def nested_dict_update(d: dict, key: str, value) -> None: root, *path, key = key.split(".") - if path: - print("???") - d[root][key] = value + sub_dict = d[root] + for part in path: + if part not in sub_dict: + sub_dict[part] = defaultdict(dict) + sub_dict = sub_dict[part] + sub_dict[key] = value def convert_config(legacy_config: dict) -> Config: @@ -56,5 +51,5 @@ def convert_config(legacy_config: dict) -> Config: for key, value in legacy_config.items(): item = LEGACY_KEYWORDS.get(key) print(key, value) - nested_update(d=d, key=item, value=value) + nested_dict_update(d=d, key=item, value=value) return Config(d) diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index 1cb790c..eebd85e 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -349,8 +349,8 @@ def __call__(self, inst, func, arguments): except XcpTimeoutError as e: self.logger.error(f"XcpTimeoutError [{str(e)}]") self.error_code = XcpError.ERR_TIMEOUT - except Exception as e: - raise UnrecoverableError(f"Don't know how to handle exception '{repr(e)}'") from e + except Exception: + raise else: self.error_code = None # print("\t\t\t*** SUCCESS ***") diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 186944f..c6d9edf 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -26,7 +26,6 @@ from .stim import Stim from pyxcp import checksum from pyxcp import types -from pyxcp.config import Configuration from pyxcp.constants import makeBytePacker from pyxcp.constants import makeByteUnpacker from pyxcp.constants import makeDLongPacker @@ -74,7 +73,7 @@ class Master: Parameters ---------- - transportName : str + transport_name : str XCP transport layer name ['can', 'eth', 'sxi'] config: dict """ @@ -92,16 +91,20 @@ class Master: "DISCONNECT_RESPONSE_OPTIONAL": (bool, False, False), } - def __init__(self, transportName, config=None, policy=None): + def __init__(self, transport_name: str, config, policy=None): self.ctr = 0 self.succeeded = True - self.config = Configuration(self.PARAMETER_MAP or {}, config or {}) - self.logger = logging.getLogger("pyXCP") - self.logger.setLevel(self.config.get("LOGLEVEL")) - disable_error_handling(self.config.get("DISABLE_ERROR_HANDLING")) + self.config = config.general + print(self.config.seed_n_key_dll) + self.logger = config.log + self.logger.setLevel("INFO") + self.logger.info("NEW cfg system!!!") + + disable_error_handling(self.config.disable_error_handling) + self.transport_name = transport_name.lower() + transport_config = config.transport # getattr(config.transport, self.transport_name) + self.transport = createTransport(transport_name, transport_config, policy) - self.transport = createTransport(transportName, config, policy) - self.transport_name = transportName self.transport.set_writer_lock(get_writer_lock()) self.transport.set_policy_lock(get_policy_lock()) self.stim = Stim() @@ -129,9 +132,9 @@ def __init__(self, transportName, config=None, policy=None): self.mta = types.MtaType(None, None) self.currentDaqPtr = None self.currentProtectionStatus = None - self.seedNKeyDLL = self.config.get("SEED_N_KEY_DLL") - self.seedNKeyDLL_same_bit_width = self.config.get("SEED_N_KEY_DLL_SAME_BIT_WIDTH") - self.disconnect_response_optional = self.config.get("DISCONNECT_RESPONSE_OPTIONAL") + self.seedNKeyDLL = self.config.seed_n_key_dll # .get("SEED_N_KEY_DLL") + self.seedNKeyDLL_same_bit_width = self.config.seed_n_key_dll_same_bit_width + self.disconnect_response_optional = self.config.disconnect_response_optional self.slaveProperties = SlaveProperties() self.slaveProperties.pgmProcessor = SlaveProperties() diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 94a35be..2f1090c 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -11,13 +11,11 @@ from time import time import pyxcp.types as types -from ..logger import Logger from ..recorder import XcpLogFileWriter from ..timing import Timing from ..utils import flatten from ..utils import hexDump from ..utils import SHORT_SLEEP -from pyxcp.config import Configuration class FrameAcquisitionPolicy: @@ -159,25 +157,27 @@ class BaseTransport(metaclass=abc.ABCMeta): "ALIGNMENT": (int, False, 1), } - def __init__(self, config=None, policy: FrameAcquisitionPolicy = None): + def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.parent = None - self.config = Configuration(BaseTransport.PARAMETER_MAP or {}, config or {}) self.policy = policy or LegacyFrameAcquisitionPolicy() self.closeEvent = threading.Event() self.command_lock = threading.Lock() - loglevel = self.config.get("LOGLEVEL") - self._debug = loglevel == "DEBUG" - self.logger = Logger("transport.Base") - self.logger.setLevel(loglevel) + # loglevel = self.config.get("LOGLEVEL") + + # self._debug = True + self._debug = False + self.logger = config.log + # self._debug = loglevel == "DEBUG" + # self.logger = Logger("transport.Base") + # self.logger.setLevel(loglevel) + self.counterSend = 0 self.counterReceived = -1 - create_daq_timestamps = self.config.get("CREATE_DAQ_TIMESTAMPS") - self.create_daq_timestamps = False if create_daq_timestamps is None else create_daq_timestamps - timeout = self.config.get("TIMEOUT") - self.alignment = self.config.get("ALIGNMENT") - self.timeout = 2.0 if timeout is None else timeout + self.create_daq_timestamps = config.create_daq_timestamps + self.alignment = config.alignment + self.timeout = config.timeout self.timer_restart_event = threading.Event() self.timing = Timing() self.resQueue = deque() @@ -200,9 +200,10 @@ def __del__(self): self.finishListener() self.closeConnection() - def loadConfig(self, config): + def load_config(self, config): """Load configuration data.""" - self.config = Configuration(self.PARAMETER_MAP or {}, config or {}) + class_name = self.__class__.__name__.lower() + self.config = getattr(config, class_name) def set_writer_lock(self, lock): self.writer_lock = lock diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index e073d7c..a2259b6 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -292,22 +292,22 @@ class Can(BaseTransport): HEADER = EmptyHeader() HEADER_SIZE = 0 - def __init__(self, config=None, policy=None): + def __init__(self, config, policy=None): """init for CAN transport :param config: configuration """ super().__init__(config, policy) - self.loadConfig(config) + self.load_config(config) drivers = registered_drivers() - interfaceName = self.config.get("CAN_DRIVER") - if interfaceName not in drivers: - raise ValueError("{} is an invalid driver name -- choose from {}".format(interfaceName, [x for x in drivers.keys()])) - canInterfaceClass = drivers[interfaceName] + interface_name = self.config.interface + if interface_name not in drivers: + raise ValueError("{} is an invalid driver name -- choose from {}".format(interface_name, [x for x in drivers.keys()])) + canInterfaceClass = drivers[interface_name] self.canInterface = canInterfaceClass() self.useDefaultListener = self.config.get("CAN_USE_DEFAULT_LISTENER") self.can_id_master = Identifier(self.config.get("CAN_ID_MASTER")) self.can_id_slave = Identifier(self.config.get("CAN_ID_SLAVE")) - self.canInterface.loadConfig(config) + self.canInterface.load_config(config) self.canInterface.init(self, self.dataReceived) # # Regarding CAN-FD s. AUTOSAR CP Release 4.3.0, Requirements on CAN; [SRS_Can_01160] Padding of bytes due to discrete CAN FD DLC]: diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 21b8cc8..2a8a8da 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -36,15 +36,15 @@ class Eth(BaseTransport): def __init__(self, config=None, policy=None): super(Eth, self).__init__(config, policy) - self.loadConfig(config) - self.host = self.config.get("HOST") - self.port = self.config.get("PORT") - self.protocol = self.config.get("PROTOCOL") - self.ipv6 = self.config.get("IPV6") - self.use_tcp_no_delay = self.config.get("TCP_NODELAY") - address_to_bind = self.config.get("BIND_TO_ADDRESS") - port_to_bind = self.config.get("BIND_TO_PORT") - self._local_address = (address_to_bind, port_to_bind) if address_to_bind else None + self.load_config(config) + self.host = self.config.host + self.port = self.config.port + self.protocol = self.config.protocol + self.ipv6 = self.config.ipv6 + self.use_tcp_no_delay = self.config.tcp_nodelay + address_to_bind = self.config.bind_to_address + bind_to_port = self.config.bind_to_port + self._local_address = (address_to_bind, bind_to_port) if address_to_bind else None if self.ipv6 and not socket.has_ipv6: raise RuntimeError("IPv6 not supported by your platform.") else: diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index f2ea3b3..259cfd9 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -13,61 +13,62 @@ class SxI(BaseTransport): """""" - PARAMETER_MAP = { - # Type Req'd Default - "PORT": (str, False, "COM1"), - "BITRATE": (int, False, 38400), - "BYTESIZE": (int, False, 8), - "PARITY": (str, False, "N"), - "STOPBITS": (int, False, 1), - } - MAX_DATAGRAM_SIZE = 512 - TIMEOUT = 0.75 HEADER = struct.Struct(" Date: Thu, 19 Oct 2023 08:50:10 +0200 Subject: [PATCH 13/99] today() --- .github/workflows/pythonapp.yml | 2 +- pyxcp/cpp_ext/extension_wrapper.cpp | 38 +- pyxcp/daq_stim/__init__.py | 26 +- pyxcp/master/errorhandler.py | 14 +- pyxcp/master/master.py | 3 + pyxcp/recorder/__init__.py | 19 +- pyxcp/recorder/mio.hpp | 1488 +++++++++++++-------------- pyxcp/recorder/rekorder.hpp | 3 +- pyxcp/recorder/unfolder.hpp | 192 +++- pyxcp/recorder/wrap.cpp | 32 +- 10 files changed, 939 insertions(+), 878 deletions(-) diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 195dbc7..0cad52a 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -68,7 +68,7 @@ jobs: - uses: pypa/gh-action-pypi-publish@v1.4.2 with: - user: ${{ secrets.PYPI_USER_NAME }} # __token__ + user: __token__ password: ${{ secrets.PYPI_PASSWORD }} # To test: repository_url: https://test.pypi.org/legacy/ # diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index 2b34b8b..88d127d 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -7,9 +7,9 @@ #include -#include "mcobject.hpp" #include "bin.hpp" #include "daqlist.hpp" +#include "mcobject.hpp" namespace py = pybind11; using namespace pybind11::literals; @@ -18,9 +18,11 @@ PYBIND11_MODULE(cpp_ext, m) { m.doc() = "C++ extensions for pyXCP."; py::class_(m, "McObject") - .def(py::init&>() - ,"name"_a, "address"_a, "ext"_a, "length"_a, "data_type"_a="", "components"_a=std::vector() - ) + .def( + py::init< + std::string_view, std::uint32_t, std::uint8_t, std::uint16_t, const std::string&, const std::vector&>(), + "name"_a, "address"_a, "ext"_a, "length"_a, "data_type"_a = "", "components"_a = std::vector() + ) .def_property("name", &McObject::get_name, &McObject::set_name) .def_property("address", &McObject::get_address, &McObject::set_address) .def_property("ext", &McObject::get_ext, &McObject::set_ext) @@ -28,10 +30,7 @@ PYBIND11_MODULE(cpp_ext, m) { .def_property("data_type", &McObject::get_data_type, &McObject::set_data_type) .def("add_component", &McObject::add_component, "component"_a) - .def("__repr__", [](const McObject& self) { - return to_string(self); - }) - ; + .def("__repr__", [](const McObject& self) { return to_string(self); }); py::class_(m, "Bin") .def(py::init(), "size"_a) @@ -40,22 +39,17 @@ PYBIND11_MODULE(cpp_ext, m) { .def_property("entries", &Bin::get_entries, nullptr) .def("append", &Bin::append) - .def("__repr__", [](const Bin& self) { - return to_string(self); - }) + .def("__repr__", [](const Bin& self) { return to_string(self); }) - .def("__eq__", [](const Bin& self, const Bin& other) { - return self == other; - }) + .def("__eq__", [](const Bin& self, const Bin& other) { return self == other; }) - .def("__len__", [](const Bin& self) { - return std::size(self.get_entries()); - }) - ; + .def("__len__", [](const Bin& self) { return std::size(self.get_entries()); }); py::class_(m, "DaqList") - .def(py::init&>(), - "name"_a, "event_num"_a, "enable_timestamps"_a, "measurements"_a) + .def( + py::init&>(), "name"_a, + "event_num"_a, "enable_timestamps"_a, "measurements"_a + ) .def_property("name", &DaqList::get_name, nullptr) .def_property("event_num", &DaqList::get_event_num, nullptr) .def_property("enable_timestamps", &DaqList::get_enable_timestamps, nullptr) @@ -64,7 +58,5 @@ PYBIND11_MODULE(cpp_ext, m) { .def_property("header_names", &DaqList::get_header_names, nullptr) .def_property("odt_count", &DaqList::get_odt_count, nullptr) .def_property("total_entries", &DaqList::get_total_entries, nullptr) - .def_property("total_length", &DaqList::get_total_length, nullptr) - ; - + .def_property("total_length", &DaqList::get_total_length, nullptr); } diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index f89be6f..fc07ddc 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- import struct -import time from collections import defaultdict from dataclasses import dataclass from dataclasses import field @@ -18,9 +17,9 @@ from pyxcp.daq_stim.optimize import make_continuous_blocks from pyxcp.daq_stim.optimize import McObject from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing +from pyxcp.recorder import DAQParser as _DAQParser from pyxcp.recorder import MeasurementParameters from pyxcp.recorder import XcpLogFileReader -from pyxcp.recorder import XcpLogFileUnfolder from pyxcp.types import FrameCategory @@ -38,15 +37,11 @@ } -class Daq: - def __init__(self, file_name: str, callback: Optional[Callable[[int, Tuple], None]] = None): +class DAQParser(_DAQParser): + def __init__(self, file_name: str, daq_lists: List[DaqList], callback: Optional[Callable[[int, Tuple], None]] = None): + super().__init__() self.callback = callback self.file_name = file_name - - def set_master(self, xcp_master): - self.xcp_master = xcp_master - - def add_daq_lists(self, daq_lists: List[DaqList]): self.daq_lists = daq_lists def setup(self, write_multiple: bool = True): @@ -81,13 +76,13 @@ def setup(self, write_multiple: bool = True): self.selectable_timestamps = False if not self.supports_timestampes: max_payload_size_first = max_payload_size - print("NO TIMESTAMP SUPPORT") + # print("NO TIMESTAMP SUPPORT") else: if self.ts_fixed: - print("Fixed timestamp") + # print("Fixed timestamp") max_payload_size_first = max_payload_size - self.ts_size else: - print("timestamp variable.") + # print("timestamp variable.") self.selectable_timestamps = True except Exception as e: @@ -116,6 +111,8 @@ def setup(self, write_multiple: bool = True): self.daq_lists, ) + self.set_parameters(self.uf) + self.first_pids = [] daq_count = len(self.daq_lists) self.xcp_master.freeDaq() @@ -154,9 +151,7 @@ def stop(self): import time def reader(self): - unfolder = XcpLogFileUnfolder(self.file_name, self.uf) - unfolder.start(self.first_pids) - + """ print("HEADERS") philez = [] for idx, d in enumerate(self.daq_lists): @@ -171,3 +166,4 @@ def reader(self): for ph in philez: ph.close() print("ETA: ", time.perf_counter() - start_time, "seconds") + """ diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index 2415251..619d04f 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -229,27 +229,27 @@ def actions(self, preActions, actions): fn = Function(self.instance.synch, Arguments()) result_pre_actions.append(fn) elif item == PreAction.GET_SEED_UNLOCK: - raise NotImplementedError("GET_SEED_UNLOCK") + raise NotImplementedError("Pre-action GET_SEED_UNLOCK") elif item == PreAction.SET_MTA: fn = Function(self.instance.setMta, Arguments(self.instance.mta)) result_pre_actions.append(fn) elif item == PreAction.SET_DAQ_PTR: fn = Function(self.instance.setDaqPtr, Arguments(self.instance.currentDaqPtr)) elif item == PreAction.START_STOP_X: - raise NotImplementedError("START_STOP_X") + raise NotImplementedError("Pre-action START_STOP_X") elif item == PreAction.REINIT_DAQ: - raise NotImplementedError("REINIT_DAQ") + raise NotImplementedError("Pre-action REINIT_DAQ") elif item == PreAction.DISPLAY_ERROR: pass elif item == PreAction.DOWNLOAD: - raise NotImplementedError("DOWNLOAD") + raise NotImplementedError("Pre-action DOWNLOAD") elif item == PreAction.PROGRAM: - raise NotImplementedError("PROGRAM") + raise NotImplementedError("Pre-action PROGRAM") elif item == PreAction.UPLOAD: - raise NotImplementedError("UPLOAD") + raise NotImplementedError("Pre-action UPLOAD") elif item == PreAction.UNLOCK_SLAVE: resource = COMMAND_CATEGORIES.get(self.instance.service) # noqa: F841 - raise NotImplementedError("UNLOCK_SLAVE") + raise NotImplementedError("Pre-action UNLOCK_SLAVE") for item in actionIter(actions): if item == Action.NONE: pass diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index d7061e1..ee6ac0c 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -103,6 +103,9 @@ def __init__(self, transportName, config=None, policy=None): self.transport.parent = self self.service = None + # Policies may issue XCP commands on there own. + self.transport.policy.xcp_master = self + # (D)Word (un-)packers are byte-order dependent # -- byte-order is returned by CONNECT_Resp (COMM_MODE_BASIC) self.BYTE_pack = None diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 288cfb5..d870cfa 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -19,6 +19,8 @@ MeasurementParameters = rec._MeasurementParameters +DAQParser = rec.DAQParser + @dataclass class XcpLogFileHeader: @@ -65,23 +67,6 @@ def as_dataframe(self): raise NotImplementedError("method as_dataframe() requires 'pandas' package") -class XcpLogFileUnfolder: - def __init__(self, file_name: str, params: MeasurementParameters): - self._unfolder = rec._XcpLogFileUnfolder(file_name, params) - - def start(self, first_pids: list): - self._unfolder.start(first_pids) - - def __iter__(self): - while True: - block = self._unfolder.next_block() - if block is None: - break - for frame in block: - daq_list, ts0, ts1, payload = frame - yield (daq_list, ts0, ts1, payload) - - class XcpLogFileWriter: """ """ diff --git a/pyxcp/recorder/mio.hpp b/pyxcp/recorder/mio.hpp index a175258..500ed67 100644 --- a/pyxcp/recorder/mio.hpp +++ b/pyxcp/recorder/mio.hpp @@ -43,13 +43,13 @@ */ #ifndef MIO_PAGE_HEADER -#define MIO_PAGE_HEADER + #define MIO_PAGE_HEADER -#ifdef _WIN32 -# include -#else -# include -#endif + #ifdef _WIN32 + #include + #else + #include + #endif namespace mio { @@ -57,8 +57,7 @@ namespace mio { * This is used by `basic_mmap` to determine whether to create a read-only or * a read-write memory mapping. */ -enum class access_mode -{ +enum class access_mode { read, write }; @@ -70,17 +69,15 @@ enum class access_mode * to determine the page size, caches the value, and returns it. Any subsequent call to * this function serves the cached value, so no further syscalls are made. */ -inline size_t page_size() -{ - static const size_t page_size = [] - { -#ifdef _WIN32 +inline size_t page_size() { + static const size_t page_size = [] { + #ifdef _WIN32 SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); return SystemInfo.dwAllocationGranularity; -#else + #else return sysconf(_SC_PAGE_SIZE); -#endif + #endif }(); return page_size; } @@ -90,37 +87,37 @@ inline size_t page_size() * difference until the nearest page boundary before `offset`, or does nothing if * `offset` is already page aligned. */ -inline size_t make_offset_page_aligned(size_t offset) noexcept -{ +inline size_t make_offset_page_aligned(size_t offset) noexcept { const size_t page_size_ = page_size(); // Use integer division to round down to the nearest page alignment. return offset / page_size_ * page_size_; } -} // namespace mio - -#endif // MIO_PAGE_HEADER +} // namespace mio +#endif // MIO_PAGE_HEADER +#include #include #include #include -#include #ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif // WIN32_LEAN_AND_MEAN -# include -#else // ifdef _WIN32 -# define INVALID_HANDLE_VALUE -1 -#endif // ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif // WIN32_LEAN_AND_MEAN + #include +#else // ifdef _WIN32 + #define INVALID_HANDLE_VALUE -1 +#endif // ifdef _WIN32 namespace mio { // This value may be provided as the `length` parameter to the constructor or // `map`, in which case a memory mapping of the entire file is created. -enum { map_entire_file = 0 }; +enum { + map_entire_file = 0 +}; #ifdef _WIN32 using file_handle_type = HANDLE; @@ -133,31 +130,31 @@ using file_handle_type = int; const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE; template -struct basic_mmap -{ - using value_type = ByteT; - using size_type = size_t; - using reference = value_type&; - using const_reference = const value_type&; - using pointer = value_type*; - using const_pointer = const value_type*; - using difference_type = std::ptrdiff_t; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; +struct basic_mmap { + using value_type = ByteT; + using size_type = size_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using difference_type = std::ptrdiff_t; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - using iterator_category = std::random_access_iterator_tag; - using handle_type = file_handle_type; + using iterator_category = std::random_access_iterator_tag; + using handle_type = file_handle_type; static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char."); -private: + private: + // Points to the first requested byte, and not to the actual start of the mapping. pointer data_ = nullptr; // Length--in bytes--requested by user (which may not be the length of the // full mapping) and the length of the full mapping. - size_type length_ = 0; + size_type length_ = 0; size_type mapped_length_ = 0; // Letting user map a file using both an existing file handle and a path @@ -178,7 +175,8 @@ struct basic_mmap // close `file_handle_`. bool is_handle_internal_; -public: + public: + /** * The default constructed mmap object is in a non-mapped state, that is, * any operation that attempts to access nonexistent underlying data will @@ -193,11 +191,12 @@ struct basic_mmap * thrown. */ template - basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) - { + basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); - if(error) { throw std::system_error(error); } + if (error) { + throw std::system_error(error); + } } /** @@ -205,13 +204,14 @@ struct basic_mmap * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ - basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) - { + basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); - if(error) { throw std::system_error(error); } + if (error) { + throw std::system_error(error); + } } -#endif // __cpp_exceptions +#endif // __cpp_exceptions /** * `basic_mmap` has single-ownership semantics, so transferring ownership @@ -233,18 +233,25 @@ struct basic_mmap * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ - handle_type file_handle() const noexcept { return file_handle_; } + handle_type file_handle() const noexcept { + return file_handle_; + } + handle_type mapping_handle() const noexcept; /** Returns whether a valid memory mapping has been created. */ - bool is_open() const noexcept { return file_handle_ != invalid_handle; } + bool is_open() const noexcept { + return file_handle_ != invalid_handle; + } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ - bool empty() const noexcept { return length() == 0; } + bool empty() const noexcept { + return length() == 0; + } /** Returns true if a mapping was established. */ bool is_mapped() const noexcept; @@ -255,13 +262,20 @@ struct basic_mmap * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ - size_type size() const noexcept { return length(); } - size_type length() const noexcept { return length_; } - size_type mapped_length() const noexcept { return mapped_length_; } + size_type size() const noexcept { + return length(); + } + + size_type length() const noexcept { + return length_; + } + + size_type mapped_length() const noexcept { + return mapped_length_; + } /** Returns the offset relative to the start of the mapping. */ - size_type mapping_offset() const noexcept - { + size_type mapping_offset() const noexcept { return mapped_length_ - length_; } @@ -269,68 +283,96 @@ struct basic_mmap * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > pointer data() noexcept { return data_; } - const_pointer data() const noexcept { return data_; } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + pointer data() noexcept { + return data_; + } + + const_pointer data() const noexcept { + return data_; + } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > iterator begin() noexcept { return data(); } - const_iterator begin() const noexcept { return data(); } - const_iterator cbegin() const noexcept { return data(); } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + iterator begin() noexcept { + return data(); + } + + const_iterator begin() const noexcept { + return data(); + } + + const_iterator cbegin() const noexcept { + return data(); + } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > iterator end() noexcept { return data() + length(); } - const_iterator end() const noexcept { return data() + length(); } - const_iterator cend() const noexcept { return data() + length(); } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + iterator end() noexcept { + return data() + length(); + } + + const_iterator end() const noexcept { + return data() + length(); + } + + const_iterator cend() const noexcept { + return data() + length(); + } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - const_reverse_iterator rbegin() const noexcept - { return const_reverse_iterator(end()); } - const_reverse_iterator crbegin() const noexcept - { return const_reverse_iterator(end()); } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + reverse_iterator rbegin() noexcept { + return reverse_iterator(end()); + } + + const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + + const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(end()); + } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - const_reverse_iterator rend() const noexcept - { return const_reverse_iterator(begin()); } - const_reverse_iterator crend() const noexcept - { return const_reverse_iterator(begin()); } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + reverse_iterator rend() noexcept { + return reverse_iterator(begin()); + } + + const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + + const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(begin()); + } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ - reference operator[](const size_type i) noexcept { return data_[i]; } - const_reference operator[](const size_type i) const noexcept { return data_[i]; } + reference operator[](const size_type i) noexcept { + return data_[i]; + } + + const_reference operator[](const size_type i) const noexcept { + return data_[i]; + } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the @@ -353,8 +395,7 @@ struct basic_mmap * case a mapping of the entire file is created. */ template - void map(const String& path, const size_type offset, - const size_type length, std::error_code& error); + void map(const String& path, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the @@ -369,8 +410,7 @@ struct basic_mmap * The entire file is mapped. */ template - void map(const String& path, std::error_code& error) - { + void map(const String& path, std::error_code& error) { map(path, 0, map_entire_file, error); } @@ -393,8 +433,7 @@ struct basic_mmap * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ - void map(const handle_type handle, const size_type offset, - const size_type length, std::error_code& error); + void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is @@ -407,8 +446,7 @@ struct basic_mmap * * The entire file is mapped. */ - void map(const handle_type handle, std::error_code& error) - { + void map(const handle_type handle, std::error_code& error) { map(handle, 0, map_entire_file, error); } @@ -427,25 +465,21 @@ struct basic_mmap /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ template - typename std::enable_if::type - sync(std::error_code& error); + typename std::enable_if::type sync(std::error_code& error); /** * All operators compare the address of the first byte and size of the two mapped * regions. */ -private: - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > pointer get_mapping_start() noexcept - { + private: + + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + pointer get_mapping_start() noexcept { return !data() ? nullptr : data() - mapping_offset(); } - const_pointer get_mapping_start() const noexcept - { + const_pointer get_mapping_start() const noexcept { return !data() ? nullptr : data() - mapping_offset(); } @@ -455,35 +489,28 @@ struct basic_mmap * do SFINAE in a dedicated function, where one syncs and the other is a noop. */ template - typename std::enable_if::type - conditional_sync(); + typename std::enable_if::type conditional_sync(); template typename std::enable_if::type conditional_sync(); }; template -bool operator==(const basic_mmap& a, - const basic_mmap& b); +bool operator==(const basic_mmap& a, const basic_mmap& b); template -bool operator!=(const basic_mmap& a, - const basic_mmap& b); +bool operator!=(const basic_mmap& a, const basic_mmap& b); template -bool operator<(const basic_mmap& a, - const basic_mmap& b); +bool operator<(const basic_mmap& a, const basic_mmap& b); template -bool operator<=(const basic_mmap& a, - const basic_mmap& b); +bool operator<=(const basic_mmap& a, const basic_mmap& b); template -bool operator>(const basic_mmap& a, - const basic_mmap& b); +bool operator>(const basic_mmap& a, const basic_mmap& b); template -bool operator>=(const basic_mmap& a, - const basic_mmap& b); +bool operator>=(const basic_mmap& a, const basic_mmap& b); /** * This is the basis for all read-only mmap objects and should be preferred over @@ -503,22 +530,18 @@ using basic_mmap_sink = basic_mmap; * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ -using mmap_source = basic_mmap_source; +using mmap_source = basic_mmap_source; using ummap_source = basic_mmap_source; -using mmap_sink = basic_mmap_sink; +using mmap_sink = basic_mmap_sink; using ummap_sink = basic_mmap_sink; /** * Convenience factory method that constructs a mapping for any `basic_mmap` or * `basic_mmap` type. */ -template< - typename MMap, - typename MappingToken -> MMap make_mmap(const MappingToken& token, - int64_t offset, int64_t length, std::error_code& error) -{ +template< typename MMap, typename MappingToken > +MMap make_mmap(const MappingToken& token, int64_t offset, int64_t length, std::error_code& error) { MMap mmap; mmap.map(token, offset, length, error); return mmap; @@ -532,15 +555,14 @@ template< * `mmap_source::handle_type`. */ template -mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, - mmap_source::size_type length, std::error_code& error) -{ +mmap_source make_mmap_source( + const MappingToken& token, mmap_source::size_type offset, mmap_source::size_type length, std::error_code& error +) { return make_mmap(token, offset, length, error); } template -mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) -{ +mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) { return make_mmap_source(token, 0, map_entire_file, error); } @@ -552,19 +574,18 @@ mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) * `mmap_sink::handle_type`. */ template -mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, - mmap_sink::size_type length, std::error_code& error) -{ +mmap_sink make_mmap_sink( + const MappingToken& token, mmap_sink::size_type offset, mmap_sink::size_type length, std::error_code& error +) { return make_mmap(token, offset, length, error); } template -mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) -{ +mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) { return make_mmap_sink(token, 0, map_entire_file, error); } -} // namespace mio +} // namespace mio // #include "detail/mmap.ipp" /* Copyright 2017 https://github.com/mandreyel @@ -588,615 +609,534 @@ mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) */ #ifndef MIO_BASIC_MMAP_IMPL -#define MIO_BASIC_MMAP_IMPL + #define MIO_BASIC_MMAP_IMPL -// #include "mio/mmap.hpp" + // #include "mio/mmap.hpp" -// #include "mio/page.hpp" + // #include "mio/page.hpp" -// #include "mio/detail/string_util.hpp" -/* Copyright 2017 https://github.com/mandreyel - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be included in all copies - * or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ + // #include "mio/detail/string_util.hpp" + /* Copyright 2017 https://github.com/mandreyel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ -#ifndef MIO_STRING_UTIL_HEADER -#define MIO_STRING_UTIL_HEADER + #ifndef MIO_STRING_UTIL_HEADER + #define MIO_STRING_UTIL_HEADER -#include + #include namespace mio { namespace detail { -template< - typename S, - typename C = typename std::decay::type, - typename = decltype(std::declval().data()), - typename = typename std::enable_if< - std::is_same::value -#ifdef _WIN32 - || std::is_same::value -#endif - >::type -> struct char_type_helper { - using type = typename C::value_type; -}; - -template -struct char_type { - using type = typename char_type_helper::type; -}; - -// TODO: can we avoid this brute force approach? -template<> -struct char_type { - using type = char; -}; - -template<> -struct char_type { - using type = char; -}; - -template -struct char_type { - using type = char; -}; - -template -struct char_type { - using type = char; -}; - -#ifdef _WIN32 -template<> -struct char_type { - using type = wchar_t; -}; - -template<> -struct char_type { - using type = wchar_t; -}; - -template -struct char_type { - using type = wchar_t; -}; - -template -struct char_type { - using type = wchar_t; -}; -#endif // _WIN32 - -template -struct is_c_str_helper -{ - static constexpr bool value = std::is_same< - CharT*, - // TODO: I'm so sorry for this... Can this be made cleaner? - typename std::add_pointer< - typename std::remove_cv< - typename std::remove_pointer< - typename std::decay< - S - >::type - >::type - >::type - >::type - >::value; -}; - -template -struct is_c_str -{ - static constexpr bool value = is_c_str_helper::value; -}; - -#ifdef _WIN32 -template -struct is_c_wstr -{ - static constexpr bool value = is_c_str_helper::value; -}; -#endif // _WIN32 - -template -struct is_c_str_or_c_wstr -{ - static constexpr bool value = is_c_str::value -#ifdef _WIN32 - || is_c_wstr::value -#endif - ; -}; - -template< - typename String, - typename = decltype(std::declval().data()), - typename = typename std::enable_if::value>::type -> const typename char_type::type* c_str(const String& path) -{ - return path.data(); -} + template< + typename S, typename C = typename std::decay::type, typename = decltype(std::declval().data()), + typename = typename std::enable_if< + std::is_same::value + #ifdef _WIN32 + || std::is_same::value + #endif + >::type > + struct char_type_helper { + using type = typename C::value_type; + }; + + template + struct char_type { + using type = typename char_type_helper::type; + }; + + // TODO: can we avoid this brute force approach? + template<> + struct char_type { + using type = char; + }; + + template<> + struct char_type { + using type = char; + }; + + template + struct char_type { + using type = char; + }; + + template + struct char_type { + using type = char; + }; + + #ifdef _WIN32 + template<> + struct char_type { + using type = wchar_t; + }; + + template<> + struct char_type { + using type = wchar_t; + }; + + template + struct char_type { + using type = wchar_t; + }; + + template + struct char_type { + using type = wchar_t; + }; + #endif // _WIN32 + + template + struct is_c_str_helper { + static constexpr bool value = std::is_same< + CharT*, + // TODO: I'm so sorry for this... Can this be made cleaner? + typename std::add_pointer< typename std::remove_cv< + typename std::remove_pointer< typename std::decay< S >::type >::type >::type >::type >::value; + }; + + template + struct is_c_str { + static constexpr bool value = is_c_str_helper::value; + }; + + #ifdef _WIN32 + template + struct is_c_wstr { + static constexpr bool value = is_c_str_helper::value; + }; + #endif // _WIN32 + + template + struct is_c_str_or_c_wstr { + static constexpr bool value = is_c_str::value + #ifdef _WIN32 + || is_c_wstr::value + #endif + ; + }; -template< - typename String, - typename = decltype(std::declval().empty()), - typename = typename std::enable_if::value>::type -> bool empty(const String& path) -{ - return path.empty(); -} + template< + typename String, typename = decltype(std::declval().data()), + typename = typename std::enable_if::value>::type > + const typename char_type::type* c_str(const String& path) { + return path.data(); + } -template< - typename String, - typename = typename std::enable_if::value>::type -> const typename char_type::type* c_str(String path) -{ - return path; -} + template< + typename String, typename = decltype(std::declval().empty()), + typename = typename std::enable_if::value>::type > + bool empty(const String& path) { + return path.empty(); + } -template< - typename String, - typename = typename std::enable_if::value>::type -> bool empty(String path) -{ - return !path || (*path == 0); -} + template< typename String, typename = typename std::enable_if::value>::type > + const typename char_type::type* c_str(String path) { + return path; + } -} // namespace detail -} // namespace mio + template< typename String, typename = typename std::enable_if::value>::type > + bool empty(String path) { + return !path || (*path == 0); + } -#endif // MIO_STRING_UTIL_HEADER +} // namespace detail +} // namespace mio + #endif // MIO_STRING_UTIL_HEADER -#include + #include -#ifndef _WIN32 -# include -# include -# include -# include -#endif + #ifndef _WIN32 + #include + #include + #include + #include + #endif namespace mio { namespace detail { -#ifdef _WIN32 -namespace win { + #ifdef _WIN32 + namespace win { -/** Returns the 4 upper bytes of an 8-byte integer. */ -inline DWORD int64_high(int64_t n) noexcept -{ - return n >> 32; -} - -/** Returns the 4 lower bytes of an 8-byte integer. */ -inline DWORD int64_low(int64_t n) noexcept -{ - return n & 0xffffffff; -} + /** Returns the 4 upper bytes of an 8-byte integer. */ + inline DWORD int64_high(int64_t n) noexcept { + return n >> 32; + } -std::wstring s_2_ws(const std::string& s) -{ - if (s.empty()) - return{}; - const auto s_length = static_cast(s.length()); - auto buf = std::vector(s_length); - const auto wide_char_count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s_length, buf.data(), s_length); - return std::wstring(buf.data(), wide_char_count); -} + /** Returns the 4 lower bytes of an 8-byte integer. */ + inline DWORD int64_low(int64_t n) noexcept { + return n & 0xffffffff; + } -template< - typename String, - typename = typename std::enable_if< - std::is_same::type, char>::value - >::type -> file_handle_type open_file_helper(const String& path, const access_mode mode) -{ - return ::CreateFileW(s_2_ws(path).c_str(), - mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - 0); -} + std::wstring s_2_ws(const std::string& s) { + if (s.empty()) + return {}; + const auto s_length = static_cast(s.length()); + auto buf = std::vector(s_length); + const auto wide_char_count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), s_length, buf.data(), s_length); + return std::wstring(buf.data(), wide_char_count); + } -template -typename std::enable_if< - std::is_same::type, wchar_t>::value, - file_handle_type ->::type open_file_helper(const String& path, const access_mode mode) -{ - return ::CreateFileW(c_str(path), - mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - 0); -} + template< + typename String, + typename = typename std::enable_if< std::is_same::type, char>::value >::type > + file_handle_type open_file_helper(const String& path, const access_mode mode) { + return ::CreateFileW( + s_2_ws(path).c_str(), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 + ); + } -} // win -#endif // _WIN32 + template + typename std::enable_if< std::is_same::type, wchar_t>::value, file_handle_type >::type + open_file_helper(const String& path, const access_mode mode) { + return ::CreateFileW( + c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 + ); + } -/** - * Returns the last platform specific system error (errno on POSIX and - * GetLastError on Win) as a `std::error_code`. - */ -inline std::error_code last_error() noexcept -{ - std::error_code error; -#ifdef _WIN32 - error.assign(GetLastError(), std::system_category()); -#else - error.assign(errno, std::system_category()); -#endif - return error; -} + } // namespace win + #endif // _WIN32 -template -file_handle_type open_file(const String& path, const access_mode mode, - std::error_code& error) -{ - error.clear(); - if(detail::empty(path)) - { - error = std::make_error_code(std::errc::invalid_argument); - return invalid_handle; - } -#ifdef _WIN32 - const auto handle = win::open_file_helper(path, mode); -#else // POSIX - const auto handle = ::open(c_str(path), - mode == access_mode::read ? O_RDONLY : O_RDWR); -#endif - if(handle == invalid_handle) - { - error = detail::last_error(); + /** + * Returns the last platform specific system error (errno on POSIX and + * GetLastError on Win) as a `std::error_code`. + */ + inline std::error_code last_error() noexcept { + std::error_code error; + #ifdef _WIN32 + error.assign(GetLastError(), std::system_category()); + #else + error.assign(errno, std::system_category()); + #endif + return error; } - return handle; -} -inline size_t query_file_size(file_handle_type handle, std::error_code& error) -{ - error.clear(); -#ifdef _WIN32 - LARGE_INTEGER file_size; - if(::GetFileSizeEx(handle, &file_size) == 0) - { - error = detail::last_error(); - return 0; - } - return static_cast(file_size.QuadPart); -#else // POSIX - struct stat sbuf; - if(::fstat(handle, &sbuf) == -1) - { - error = detail::last_error(); - return 0; + template + file_handle_type open_file(const String& path, const access_mode mode, std::error_code& error) { + error.clear(); + if (detail::empty(path)) { + error = std::make_error_code(std::errc::invalid_argument); + return invalid_handle; + } + #ifdef _WIN32 + const auto handle = win::open_file_helper(path, mode); + #else // POSIX + const auto handle = ::open(c_str(path), mode == access_mode::read ? O_RDONLY : O_RDWR); + #endif + if (handle == invalid_handle) { + error = detail::last_error(); + } + return handle; } - return sbuf.st_size; -#endif -} -struct mmap_context -{ - char* data; - int64_t length; - int64_t mapped_length; -#ifdef _WIN32 - file_handle_type file_mapping_handle; -#endif -}; + inline size_t query_file_size(file_handle_type handle, std::error_code& error) { + error.clear(); + #ifdef _WIN32 + LARGE_INTEGER file_size; + if (::GetFileSizeEx(handle, &file_size) == 0) { + error = detail::last_error(); + return 0; + } + return static_cast(file_size.QuadPart); + #else // POSIX + struct stat sbuf; + if (::fstat(handle, &sbuf) == -1) { + error = detail::last_error(); + return 0; + } + return sbuf.st_size; + #endif + } -inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, - const int64_t length, const access_mode mode, std::error_code& error) -{ - const int64_t aligned_offset = make_offset_page_aligned(offset); - const int64_t length_to_map = offset - aligned_offset + length; -#ifdef _WIN32 - const int64_t max_file_size = offset + length; - const auto file_mapping_handle = ::CreateFileMapping( - file_handle, - 0, - mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, - win::int64_high(max_file_size), - win::int64_low(max_file_size), - 0); - if(file_mapping_handle == invalid_handle) - { - error = detail::last_error(); - return {}; - } - char* mapping_start = static_cast(::MapViewOfFile( - file_mapping_handle, - mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, - win::int64_high(aligned_offset), - win::int64_low(aligned_offset), - length_to_map)); - if(mapping_start == nullptr) - { - // Close file handle if mapping it failed. - ::CloseHandle(file_mapping_handle); - error = detail::last_error(); - return {}; - } -#else // POSIX - char* mapping_start = static_cast(::mmap( - 0, // Don't give hint as to where to map. - length_to_map, - mode == access_mode::read ? PROT_READ : PROT_WRITE, - MAP_SHARED, - file_handle, - aligned_offset)); - if(mapping_start == MAP_FAILED) - { - error = detail::last_error(); - return {}; + struct mmap_context { + char* data; + int64_t length; + int64_t mapped_length; + #ifdef _WIN32 + file_handle_type file_mapping_handle; + #endif + }; + + inline mmap_context memory_map( + const file_handle_type file_handle, const int64_t offset, const int64_t length, const access_mode mode, + std::error_code& error + ) { + const int64_t aligned_offset = make_offset_page_aligned(offset); + const int64_t length_to_map = offset - aligned_offset + length; + #ifdef _WIN32 + const int64_t max_file_size = offset + length; + const auto file_mapping_handle = ::CreateFileMapping( + file_handle, 0, mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, win::int64_high(max_file_size), + win::int64_low(max_file_size), 0 + ); + if (file_mapping_handle == invalid_handle) { + error = detail::last_error(); + return {}; + } + char* mapping_start = static_cast(::MapViewOfFile( + file_mapping_handle, mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, win::int64_high(aligned_offset), + win::int64_low(aligned_offset), length_to_map + )); + if (mapping_start == nullptr) { + // Close file handle if mapping it failed. + ::CloseHandle(file_mapping_handle); + error = detail::last_error(); + return {}; + } + #else // POSIX + char* mapping_start = static_cast(::mmap( + 0, // Don't give hint as to where to map. + length_to_map, mode == access_mode::read ? PROT_READ : PROT_WRITE, MAP_SHARED, file_handle, aligned_offset + )); + if (mapping_start == MAP_FAILED) { + error = detail::last_error(); + return {}; + } + #endif + mmap_context ctx; + ctx.data = mapping_start + offset - aligned_offset; + ctx.length = length; + ctx.mapped_length = length_to_map; + #ifdef _WIN32 + ctx.file_mapping_handle = file_mapping_handle; + #endif + return ctx; } -#endif - mmap_context ctx; - ctx.data = mapping_start + offset - aligned_offset; - ctx.length = length; - ctx.mapped_length = length_to_map; -#ifdef _WIN32 - ctx.file_mapping_handle = file_mapping_handle; -#endif - return ctx; -} -} // namespace detail +} // namespace detail // -- basic_mmap -- template -basic_mmap::~basic_mmap() -{ +basic_mmap::~basic_mmap() { conditional_sync(); unmap(); } template -basic_mmap::basic_mmap(basic_mmap&& other) - : data_(std::move(other.data_)) - , length_(std::move(other.length_)) - , mapped_length_(std::move(other.mapped_length_)) - , file_handle_(std::move(other.file_handle_)) -#ifdef _WIN32 - , file_mapping_handle_(std::move(other.file_mapping_handle_)) -#endif - , is_handle_internal_(std::move(other.is_handle_internal_)) -{ - other.data_ = nullptr; +basic_mmap::basic_mmap(basic_mmap&& other) : + data_(std::move(other.data_)), + length_(std::move(other.length_)), + mapped_length_(std::move(other.mapped_length_)), + file_handle_(std::move(other.file_handle_)) + #ifdef _WIN32 + , + file_mapping_handle_(std::move(other.file_mapping_handle_)) + #endif + , + is_handle_internal_(std::move(other.is_handle_internal_)) { + other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; - other.file_handle_ = invalid_handle; -#ifdef _WIN32 + other.file_handle_ = invalid_handle; + #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; -#endif + #endif } template -basic_mmap& -basic_mmap::operator=(basic_mmap&& other) -{ - if(this != &other) - { +basic_mmap& basic_mmap::operator=(basic_mmap&& other) { + if (this != &other) { // First the existing mapping needs to be removed. unmap(); - data_ = std::move(other.data_); - length_ = std::move(other.length_); + data_ = std::move(other.data_); + length_ = std::move(other.length_); mapped_length_ = std::move(other.mapped_length_); - file_handle_ = std::move(other.file_handle_); -#ifdef _WIN32 + file_handle_ = std::move(other.file_handle_); + #ifdef _WIN32 file_mapping_handle_ = std::move(other.file_mapping_handle_); -#endif + #endif is_handle_internal_ = std::move(other.is_handle_internal_); // The moved from basic_mmap's fields need to be reset, because // otherwise other's destructor will unmap the same mapping that was // just moved into this. - other.data_ = nullptr; + other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; - other.file_handle_ = invalid_handle; -#ifdef _WIN32 + other.file_handle_ = invalid_handle; + #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; -#endif + #endif other.is_handle_internal_ = false; } return *this; } template -typename basic_mmap::handle_type -basic_mmap::mapping_handle() const noexcept -{ -#ifdef _WIN32 +typename basic_mmap::handle_type basic_mmap::mapping_handle() const noexcept { + #ifdef _WIN32 return file_mapping_handle_; -#else + #else return file_handle_; -#endif + #endif } template template -void basic_mmap::map(const String& path, const size_type offset, - const size_type length, std::error_code& error) -{ +void basic_mmap::map( + const String& path, const size_type offset, const size_type length, std::error_code& error +) { error.clear(); - if(detail::empty(path)) - { + if (detail::empty(path)) { error = std::make_error_code(std::errc::invalid_argument); return; } const auto handle = detail::open_file(path, AccessMode, error); - if(error) - { + if (error) { return; } map(handle, offset, length, error); // This MUST be after the call to map, as that sets this to true. - if(!error) - { + if (!error) { is_handle_internal_ = true; } } template -void basic_mmap::map(const handle_type handle, - const size_type offset, const size_type length, std::error_code& error) -{ +void basic_mmap::map( + const handle_type handle, const size_type offset, const size_type length, std::error_code& error +) { error.clear(); - if(handle == invalid_handle) - { + if (handle == invalid_handle) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } const auto file_size = detail::query_file_size(handle, error); - if(error) - { + if (error) { return; } - if(offset + length > file_size) - { + if (offset + length > file_size) { error = std::make_error_code(std::errc::invalid_argument); return; } - const auto ctx = detail::memory_map(handle, offset, - length == map_entire_file ? (file_size - offset) : length, - AccessMode, error); - if(!error) - { + const auto ctx = + detail::memory_map(handle, offset, length == map_entire_file ? (file_size - offset) : length, AccessMode, error); + if (!error) { // We must unmap the previous mapping that may have existed prior to this call. // Note that this must only be invoked after a new mapping has been created in // order to provide the strong guarantee that, should the new mapping fail, the // `map` function leaves this instance in a state as though the function had // never been invoked. unmap(); - file_handle_ = handle; + file_handle_ = handle; is_handle_internal_ = false; - data_ = reinterpret_cast(ctx.data); - length_ = ctx.length; - mapped_length_ = ctx.mapped_length; -#ifdef _WIN32 + data_ = std::bit_cast(ctx.data); + length_ = ctx.length; + mapped_length_ = ctx.mapped_length; + #ifdef _WIN32 file_mapping_handle_ = ctx.file_mapping_handle; -#endif + #endif } } template template -typename std::enable_if::type -basic_mmap::sync(std::error_code& error) -{ +typename std::enable_if::type basic_mmap::sync(std::error_code& error) { error.clear(); - if(!is_open()) - { + if (!is_open()) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } - if(data()) - { -#ifdef _WIN32 - if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 - || ::FlushFileBuffers(file_handle_) == 0) -#else // POSIX - if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) -#endif + if (data()) { + #ifdef _WIN32 + if (::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 || ::FlushFileBuffers(file_handle_) == 0) + #else // POSIX + if (::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) + #endif { error = detail::last_error(); return; } } -#ifdef _WIN32 - if(::FlushFileBuffers(file_handle_) == 0) - { + #ifdef _WIN32 + if (::FlushFileBuffers(file_handle_) == 0) { error = detail::last_error(); } -#endif + #endif } template -void basic_mmap::unmap() -{ - if(!is_open()) { return; } - // TODO do we care about errors here? -#ifdef _WIN32 - if(is_mapped()) - { +void basic_mmap::unmap() { + if (!is_open()) { + return; + } + // TODO do we care about errors here? + #ifdef _WIN32 + if (is_mapped()) { ::UnmapViewOfFile(get_mapping_start()); ::CloseHandle(file_mapping_handle_); } -#else // POSIX - if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); } -#endif + #else // POSIX + if (data_) { + ::munmap(const_cast(get_mapping_start()), mapped_length_); + } + #endif // If `file_handle_` was obtained by our opening it (when map is called with // a path, rather than an existing file handle), we need to close it, // otherwise it must not be closed as it may still be used outside this // instance. - if(is_handle_internal_) - { -#ifdef _WIN32 + if (is_handle_internal_) { + #ifdef _WIN32 ::CloseHandle(file_handle_); -#else // POSIX + #else // POSIX ::close(file_handle_); -#endif + #endif } // Reset fields to their default values. - data_ = nullptr; + data_ = nullptr; length_ = mapped_length_ = 0; - file_handle_ = invalid_handle; -#ifdef _WIN32 + file_handle_ = invalid_handle; + #ifdef _WIN32 file_mapping_handle_ = invalid_handle; -#endif + #endif } template -bool basic_mmap::is_mapped() const noexcept -{ -#ifdef _WIN32 +bool basic_mmap::is_mapped() const noexcept { + #ifdef _WIN32 return file_mapping_handle_ != invalid_handle; -#else // POSIX + #else // POSIX return is_open(); -#endif + #endif } template -void basic_mmap::swap(basic_mmap& other) -{ - if(this != &other) - { +void basic_mmap::swap(basic_mmap& other) { + if (this != &other) { using std::swap; swap(data_, other.data_); swap(file_handle_, other.file_handle_); -#ifdef _WIN32 + #ifdef _WIN32 swap(file_mapping_handle_, other.file_mapping_handle_); -#endif + #endif swap(length_, other.length_); swap(mapped_length_, other.mapped_length_); swap(is_handle_internal_, other.is_handle_internal_); @@ -1205,9 +1145,7 @@ void basic_mmap::swap(basic_mmap& other) template template -typename std::enable_if::type -basic_mmap::conditional_sync() -{ +typename std::enable_if::type basic_mmap::conditional_sync() { // This is invoked from the destructor, so not much we can do about // failures here. std::error_code ec; @@ -1216,63 +1154,51 @@ basic_mmap::conditional_sync() template template -typename std::enable_if::type -basic_mmap::conditional_sync() -{ +typename std::enable_if::type basic_mmap::conditional_sync() { // noop } template -bool operator==(const basic_mmap& a, - const basic_mmap& b) -{ - return a.data() == b.data() - && a.size() == b.size(); +bool operator==(const basic_mmap& a, const basic_mmap& b) { + return a.data() == b.data() && a.size() == b.size(); } template -bool operator!=(const basic_mmap& a, - const basic_mmap& b) -{ +bool operator!=(const basic_mmap& a, const basic_mmap& b) { return !(a == b); } template -bool operator<(const basic_mmap& a, - const basic_mmap& b) -{ - if(a.data() == b.data()) { return a.size() < b.size(); } +bool operator<(const basic_mmap& a, const basic_mmap& b) { + if (a.data() == b.data()) { + return a.size() < b.size(); + } return a.data() < b.data(); } template -bool operator<=(const basic_mmap& a, - const basic_mmap& b) -{ +bool operator<=(const basic_mmap& a, const basic_mmap& b) { return !(a > b); } template -bool operator>(const basic_mmap& a, - const basic_mmap& b) -{ - if(a.data() == b.data()) { return a.size() > b.size(); } +bool operator>(const basic_mmap& a, const basic_mmap& b) { + if (a.data() == b.data()) { + return a.size() > b.size(); + } return a.data() > b.data(); } template -bool operator>=(const basic_mmap& a, - const basic_mmap& b) -{ +bool operator>=(const basic_mmap& a, const basic_mmap& b) { return !(a < b); } -} // namespace mio +} // namespace mio -#endif // MIO_BASIC_MMAP_IMPL +#endif // MIO_BASIC_MMAP_IMPL - -#endif // MIO_MMAP_HEADER +#endif // MIO_MMAP_HEADER /* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this @@ -1297,9 +1223,9 @@ bool operator>=(const basic_mmap& a, #define MIO_PAGE_HEADER #ifdef _WIN32 -# include + #include #else -# include + #include #endif namespace mio { @@ -1308,8 +1234,7 @@ namespace mio { * This is used by `basic_mmap` to determine whether to create a read-only or * a read-write memory mapping. */ -enum class access_mode -{ +enum class access_mode { read, write }; @@ -1321,10 +1246,8 @@ enum class access_mode * to determine the page size, caches the value, and returns it. Any subsequent call to * this function serves the cached value, so no further syscalls are made. */ -inline size_t page_size() -{ - static const size_t page_size = [] - { +inline size_t page_size() { + static const size_t page_size = [] { #ifdef _WIN32 SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); @@ -1341,16 +1264,15 @@ inline size_t page_size() * difference until the nearest page boundary before `offset`, or does nothing if * `offset` is already page aligned. */ -inline size_t make_offset_page_aligned(size_t offset) noexcept -{ +inline size_t make_offset_page_aligned(size_t offset) noexcept { const size_t page_size_ = page_size(); // Use integer division to round down to the nearest page alignment. return offset / page_size_ * page_size_; } -} // namespace mio +} // namespace mio -#endif // MIO_PAGE_HEADER +#endif // MIO_PAGE_HEADER /* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this @@ -1376,9 +1298,8 @@ inline size_t make_offset_page_aligned(size_t offset) noexcept // #include "mio/mmap.hpp" - -#include // std::error_code -#include // std::shared_ptr +#include // std::shared_ptr +#include // std::error_code namespace mio { @@ -1389,54 +1310,50 @@ namespace mio { * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if * shared semantics are not required. */ -template< - access_mode AccessMode, - typename ByteT -> class basic_shared_mmap -{ +template< access_mode AccessMode, typename ByteT > +class basic_shared_mmap { using impl_type = basic_mmap; std::shared_ptr pimpl_; -public: - using value_type = typename impl_type::value_type; - using size_type = typename impl_type::size_type; - using reference = typename impl_type::reference; - using const_reference = typename impl_type::const_reference; - using pointer = typename impl_type::pointer; - using const_pointer = typename impl_type::const_pointer; - using difference_type = typename impl_type::difference_type; - using iterator = typename impl_type::iterator; - using const_iterator = typename impl_type::const_iterator; - using reverse_iterator = typename impl_type::reverse_iterator; + public: + + using value_type = typename impl_type::value_type; + using size_type = typename impl_type::size_type; + using reference = typename impl_type::reference; + using const_reference = typename impl_type::const_reference; + using pointer = typename impl_type::pointer; + using const_pointer = typename impl_type::const_pointer; + using difference_type = typename impl_type::difference_type; + using iterator = typename impl_type::iterator; + using const_iterator = typename impl_type::const_iterator; + using reverse_iterator = typename impl_type::reverse_iterator; using const_reverse_iterator = typename impl_type::const_reverse_iterator; - using iterator_category = typename impl_type::iterator_category; - using handle_type = typename impl_type::handle_type; - using mmap_type = impl_type; + using iterator_category = typename impl_type::iterator_category; + using handle_type = typename impl_type::handle_type; + using mmap_type = impl_type; - basic_shared_mmap() = default; - basic_shared_mmap(const basic_shared_mmap&) = default; + basic_shared_mmap() = default; + basic_shared_mmap(const basic_shared_mmap&) = default; basic_shared_mmap& operator=(const basic_shared_mmap&) = default; - basic_shared_mmap(basic_shared_mmap&&) = default; - basic_shared_mmap& operator=(basic_shared_mmap&&) = default; + basic_shared_mmap(basic_shared_mmap&&) = default; + basic_shared_mmap& operator=(basic_shared_mmap&&) = default; /** Takes ownership of an existing mmap object. */ - basic_shared_mmap(mmap_type&& mmap) - : pimpl_(std::make_shared(std::move(mmap))) - {} + basic_shared_mmap(mmap_type&& mmap) : pimpl_(std::make_shared(std::move(mmap))) { + } /** Takes ownership of an existing mmap object. */ - basic_shared_mmap& operator=(mmap_type&& mmap) - { + basic_shared_mmap& operator=(mmap_type&& mmap) { pimpl_ = std::make_shared(std::move(mmap)); return *this; } /** Initializes this object with an already established shared mmap. */ - basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) {} + basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) { + } /** Initializes this object with an already established shared mmap. */ - basic_shared_mmap& operator=(std::shared_ptr mmap) - { + basic_shared_mmap& operator=(std::shared_ptr mmap) { pimpl_ = std::move(mmap); return *this; } @@ -1448,11 +1365,12 @@ template< * thrown. */ template - basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) - { + basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); - if(error) { throw std::system_error(error); } + if (error) { + throw std::system_error(error); + } } /** @@ -1460,13 +1378,14 @@ template< * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ - basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) - { + basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); - if(error) { throw std::system_error(error); } + if (error) { + throw std::system_error(error); + } } -#endif // __cpp_exceptions +#endif // __cpp_exceptions /** * If this is a read-write mapping and the last reference to the mapping, @@ -1476,32 +1395,36 @@ template< ~basic_shared_mmap() = default; /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */ - std::shared_ptr get_shared_ptr() { return pimpl_; } + std::shared_ptr get_shared_ptr() { + return pimpl_; + } /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ - handle_type file_handle() const noexcept - { + handle_type file_handle() const noexcept { return pimpl_ ? pimpl_->file_handle() : invalid_handle; } - handle_type mapping_handle() const noexcept - { + handle_type mapping_handle() const noexcept { return pimpl_ ? pimpl_->mapping_handle() : invalid_handle; } /** Returns whether a valid memory mapping has been created. */ - bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); } + bool is_open() const noexcept { + return pimpl_ && pimpl_->is_open(); + } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ - bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); } + bool empty() const noexcept { + return !pimpl_ || pimpl_->empty(); + } /** * `size` and `length` both return the logical length, i.e. the number of bytes @@ -1509,10 +1432,15 @@ template< * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ - size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; } - size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; } - size_type mapped_length() const noexcept - { + size_type size() const noexcept { + return pimpl_ ? pimpl_->length() : 0; + } + + size_type length() const noexcept { + return pimpl_ ? pimpl_->length() : 0; + } + + size_type mapped_length() const noexcept { return pimpl_ ? pimpl_->mapped_length() : 0; } @@ -1520,61 +1448,95 @@ template< * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > pointer data() noexcept { return pimpl_->data(); } - const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + pointer data() noexcept { + return pimpl_->data(); + } + + const_pointer data() const noexcept { + return pimpl_ ? pimpl_->data() : nullptr; + } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ - iterator begin() noexcept { return pimpl_->begin(); } - const_iterator begin() const noexcept { return pimpl_->begin(); } - const_iterator cbegin() const noexcept { return pimpl_->cbegin(); } + iterator begin() noexcept { + return pimpl_->begin(); + } + + const_iterator begin() const noexcept { + return pimpl_->begin(); + } + + const_iterator cbegin() const noexcept { + return pimpl_->cbegin(); + } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > iterator end() noexcept { return pimpl_->end(); } - const_iterator end() const noexcept { return pimpl_->end(); } - const_iterator cend() const noexcept { return pimpl_->cend(); } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + iterator end() noexcept { + return pimpl_->end(); + } + + const_iterator end() const noexcept { + return pimpl_->end(); + } + + const_iterator cend() const noexcept { + return pimpl_->cend(); + } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); } - const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); } - const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + reverse_iterator rbegin() noexcept { + return pimpl_->rbegin(); + } + + const_reverse_iterator rbegin() const noexcept { + return pimpl_->rbegin(); + } + + const_reverse_iterator crbegin() const noexcept { + return pimpl_->crbegin(); + } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > reverse_iterator rend() noexcept { return pimpl_->rend(); } - const_reverse_iterator rend() const noexcept { return pimpl_->rend(); } - const_reverse_iterator crend() const noexcept { return pimpl_->crend(); } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + reverse_iterator rend() noexcept { + return pimpl_->rend(); + } + + const_reverse_iterator rend() const noexcept { + return pimpl_->rend(); + } + + const_reverse_iterator crend() const noexcept { + return pimpl_->crend(); + } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ - reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; } - const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; } + reference operator[](const size_type i) noexcept { + return (*pimpl_)[i]; + } + + const_reference operator[](const size_type i) const noexcept { + return (*pimpl_)[i]; + } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the @@ -1597,9 +1559,7 @@ template< * case a mapping of the entire file is created. */ template - void map(const String& path, const size_type offset, - const size_type length, std::error_code& error) - { + void map(const String& path, const size_type offset, const size_type length, std::error_code& error) { map_impl(path, offset, length, error); } @@ -1616,8 +1576,7 @@ template< * The entire file is mapped. */ template - void map(const String& path, std::error_code& error) - { + void map(const String& path, std::error_code& error) { map_impl(path, 0, map_entire_file, error); } @@ -1640,9 +1599,7 @@ template< * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ - void map(const handle_type handle, const size_type offset, - const size_type length, std::error_code& error) - { + void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error) { map_impl(handle, offset, length, error); } @@ -1657,8 +1614,7 @@ template< * * The entire file is mapped. */ - void map(const handle_type handle, std::error_code& error) - { + void map(const handle_type handle, std::error_code& error) { map_impl(handle, 0, map_entire_file, error); } @@ -1671,61 +1627,59 @@ template< * mapping was created using a file path. If, on the other hand, an existing * file handle was used to create the mapping, the file handle is not closed. */ - void unmap() { if(pimpl_) pimpl_->unmap(); } + void unmap() { + if (pimpl_) + pimpl_->unmap(); + } - void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); } + void swap(basic_shared_mmap& other) { + pimpl_.swap(other.pimpl_); + } /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ - template< - access_mode A = AccessMode, - typename = typename std::enable_if::type - > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); } + template< access_mode A = AccessMode, typename = typename std::enable_if::type > + void sync(std::error_code& error) { + if (pimpl_) + pimpl_->sync(error); + } /** All operators compare the underlying `basic_mmap`'s addresses. */ - friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b) - { + friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ == b.pimpl_; } - friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b) - { + friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return !(a == b); } - friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b) - { + friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ < b.pimpl_; } - friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b) - { + friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ <= b.pimpl_; } - friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b) - { + friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ > b.pimpl_; } - friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b) - { + friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ >= b.pimpl_; } -private: + private: + template - void map_impl(const MappingToken& token, const size_type offset, - const size_type length, std::error_code& error) - { - if(!pimpl_) - { + void map_impl(const MappingToken& token, const size_type offset, const size_type length, std::error_code& error) { + if (!pimpl_) { mmap_type mmap = make_mmap(token, offset, length, error); - if(error) { return; } + if (error) { + return; + } pimpl_ = std::make_shared(std::move(mmap)); - } - else - { + } else { pimpl_->map(token, offset, length, error); } } @@ -1749,12 +1703,12 @@ using basic_shared_mmap_sink = basic_shared_mmap; * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ -using shared_mmap_source = basic_shared_mmap_source; +using shared_mmap_source = basic_shared_mmap_source; using shared_ummap_source = basic_shared_mmap_source; -using shared_mmap_sink = basic_shared_mmap_sink; +using shared_mmap_sink = basic_shared_mmap_sink; using shared_ummap_sink = basic_shared_mmap_sink; -} // namespace mio +} // namespace mio -#endif // MIO_SHARED_MMAP_HEADER +#endif // MIO_SHARED_MMAP_HEADER diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index ab62276..a2eb83c 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -91,7 +91,8 @@ struct ContainerHeaderType { uint32_t size_uncompressed; }; -using blob_t = unsigned char; +using blob_t = unsigned char; +using blob_string = std::basic_string; #if STANDALONE_REKORDER == 1 using payload_t = std::shared_ptr; diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index e9bca9a..dc6d6be 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -96,11 +96,11 @@ auto get_value_swapped(blob_t const * buf, std::uint32_t offset) - struct Getter { Getter() = default; - explicit Getter(bool swap, std::uint8_t id_size, std::uint8_t ts_size) : m_id_size(id_size), m_ts_size(ts_size) { + explicit Getter(bool requires_swap, std::uint8_t id_size, std::uint8_t ts_size) : m_id_size(id_size), m_ts_size(ts_size) { int8 = get_value; uint8 = get_value; - if (swap) { + if (requires_swap) { int16 = get_value_swapped; int32 = get_value_swapped; int64 = get_value_swapped; @@ -234,7 +234,7 @@ struct MeasurementParameters { m_daq_lists(daq_lists) { } - std::uint8_t m_byte_order; // INTEL(LITTLE)=0, MOTOROLA(BIG)=1 + std::uint8_t m_byte_order; std::uint8_t m_id_field_size; bool m_timestamps_supported; bool m_ts_fixed; @@ -271,6 +271,8 @@ class DaqListState { m_getter(getter), m_params(params), m_state(state_t::IDLE) { + // std::cout << "DaqListState: " << daq_list_num << " : " << num_odts << " : " << total_entries << " : " << + // enable_timestamps << std::endl; m_buffer.resize(m_total_entries); } @@ -300,7 +302,7 @@ class DaqListState { return m_state; } - bool feed(uint16_t odt_num, double timestamp, const payload_t& payload) { + bool feed(uint16_t odt_num, double timestamp, const std::string& payload) { auto state = check_state(odt_num); auto finished = false; if (state == state_t::COLLECTING) { @@ -318,6 +320,10 @@ class DaqListState { result_buffer.emplace_back(m_daq_list_num, m_timestamp0, m_timestamp1, m_buffer); } + void add_result(measurement_tuple_t& result_buffer) { + result_buffer = { m_daq_list_num, m_timestamp0, m_timestamp1, m_buffer }; + } + protected: void resetSM() { @@ -326,10 +332,10 @@ class DaqListState { m_timestamp0 = 0.0; } - void parse_Odt(uint16_t odt_num, const payload_t& payload) { + void parse_Odt(uint16_t odt_num, const std::string& payload) { auto offset = m_initial_offset; // consider ID field size. - auto payload_data = payload.data(); - auto payload_size = payload.size(); + auto payload_data = reinterpret_cast(payload.data()); + auto payload_size = std::size(payload); if (odt_num == 0) { m_current_idx = 0; @@ -350,17 +356,8 @@ class DaqListState { "Offset is out of range! " + std::to_string(offset) + " >= " + std::to_string(payload_size) ); } - auto data = m_getter.reader(type_index, payload_data, offset); -#if 0 - if (std::holds_alternative(data)) { - std::cout << std::get(data) << " " << std::endl; - } else if (std::holds_alternative(data)) { - std::cout << std::get(data) << "(+/-) " << std::endl; - } else if (std::holds_alternative(data)) { - std::cout << std::get(data) << " (double) " << std::endl; - } -#endif - m_buffer[m_current_idx++] = std::move(data); + + m_buffer[m_current_idx++] = std::move(m_getter.reader(type_index, payload_data, offset)); offset += size; } } @@ -383,34 +380,103 @@ class DaqListState { MeasurementParameters m_params; }; -class XcpLogFileUnfolder { +auto requires_swap(std::uint8_t byte_order) { + // INTEL(LITTLE)=0, MOTOROLA(BIG)=1 + std::endian target_byte_order = (byte_order == 1) ? std::endian::big : std::endian::little; + return (target_byte_order != std::endian::native) ? true : false; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class UnfolderBase { public: - explicit XcpLogFileUnfolder(const std::string& file_name, const MeasurementParameters& params) : - m_reader(file_name), m_byte_order(std::endian::native), m_params(params) { - std::endian target_byte_order; - bool requires_swap; - - if (m_params.m_byte_order == 0) { - target_byte_order = std::endian::little; - } else if (m_params.m_byte_order == 1) { - target_byte_order = std::endian::big; + explicit UnfolderBase(const MeasurementParameters& params) : m_params(params) noexcept { + create_state_vars(params); + } + + UnfolderBase() = delete; + virtual ~UnfolderBase() = default; + + void start(const std::vector& first_pids) noexcept { + m_getter.set_first_pids(m_params.m_daq_lists, first_pids); + } + +#if 0 + std::optional> next_block() { + std::vector result{}; + + const auto& block = m_reader.next_block(); + + if (!block) { + return std::nullopt; } - if (target_byte_order != m_byte_order) { - requires_swap = true; - } else { - requires_swap = false; + + for (const auto& frame : block.value()) { + if (std::get<0>(frame) != static_cast(FrameCategory::DAQ)) { + continue; + } + const auto& [category, counter, frame_timestamp, frame_length, payload] = frame; + auto [daq_num, odt_num] = m_getter.get_id(payload.data()); + + if (m_state[daq_num].feed(odt_num, frame_timestamp, payload)) { + m_state[daq_num].add_result(result); + } + } + return result; + } + +#endif + + void feed(double timestamp, const std::string& payload) noexcept { + const auto data = reinterpret_cast(payload.data()); + auto [daq_num, odt_num] = m_getter.get_id(data); + measurement_tuple_t result; + + if (m_state[daq_num].feed(odt_num, timestamp, payload)) { + m_state[daq_num].add_result(result); + auto [dl, d0, d1, pl] = result; + + // std::cout << "DL: " << dl << " : " << d0 << " : " << d1 << std::endl; } - m_getter = Getter(requires_swap, params.m_id_field_size, params.m_ts_size); + } + + private: + + void create_state_vars(const MeasurementParameters& params) noexcept { + m_getter = Getter(requires_swap(params.m_byte_order), params.m_id_field_size, params.m_ts_size); for (auto idx = 0; idx < params.m_daq_lists.size(); ++idx) { m_state.emplace_back(DaqListState( idx, params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), - m_params.m_daq_lists[idx].get_enable_timestamps(), m_params.m_id_field_size, - m_params.m_daq_lists[idx].get_flatten_odts(), m_getter, m_params + params.m_daq_lists[idx].get_enable_timestamps(), params.m_id_field_size, params.m_daq_lists[idx].get_flatten_odts(), + m_getter, params )); } } + MeasurementParameters m_params; + Getter m_getter; + std::map m_first_pids; + std::vector m_state; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if 0 +class XcpLogFileUnfolder { + public: + + explicit XcpLogFileUnfolder(const std::string& file_name, const MeasurementParameters& params) : + m_reader(file_name), m_params(params) { + create_state_vars(params); + } + + XcpLogFileUnfolder() = delete; + void start(const std::vector& first_pids) { m_getter.set_first_pids(m_params.m_daq_lists, first_pids); } @@ -420,8 +486,6 @@ class XcpLogFileUnfolder { const auto& block = m_reader.next_block(); - std::cout << "Block number: " << xxx_blk_no++ << std::endl; - if (!block) { return std::nullopt; } @@ -442,15 +506,61 @@ class XcpLogFileUnfolder { private: + void create_state_vars(const MeasurementParameters& params) { + m_getter = Getter(requires_swap(params.m_byte_order), params.m_id_field_size, params.m_ts_size); + for (auto idx = 0; idx < params.m_daq_lists.size(); ++idx) { + m_state.emplace_back(DaqListState( + idx, params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), + params.m_daq_lists[idx].get_enable_timestamps(), params.m_id_field_size, params.m_daq_lists[idx].get_flatten_odts(), + m_getter, params + )); + } + } + XcpLogFileReader m_reader; - std::endian m_byte_order; MeasurementParameters m_params; Getter m_getter; std::map m_first_pids; - // std::vector> m_buffers; - std::vector m_state; + std::vector m_state; +}; +#endif + +// startMeasurement +// stopMeasurement + +class DAQParser { + public: + + using callback_t = std::function&)>; + + virtual ~DAQParser() = default; + DAQParser() = default; + + void set_parameters(const MeasurementParameters& params) noexcept { + m_unfolder = std::make_unique(params); + std::cout << "DAQParser::set_parameters: " << std::endl; + } + + virtual void on_daq_list( + std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + ) = 0; + + void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) { + if (frame_cat != static_cast(FrameCategory::DAQ)) { + return; + } + m_unfolder->feed(timestamp, payload); + } + + virtual void post_setup() noexcept { + } + + void finalize() noexcept { + } + + private: - std::uint16_t xxx_blk_no{ 0 }; + std::unique_ptr m_unfolder; }; #endif // RECORDER_UNFOLDER_HPP diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 284a42c..e3ec324 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -10,6 +10,22 @@ namespace py = pybind11; using namespace pybind11::literals; +class PyDAQParser : public DAQParser { + public: + + using DAQParser::DAQParser; + + void on_daq_list( + std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector &measurement + ) override { + PYBIND11_OVERRIDE_PURE(void, DAQParser, feed, daq_list_num, timestamp0, timestamp0, measurement); + } + + void post_setup() override { + PYBIND11_OVERRIDE(void, DAQParser, post_setup); + } +}; + PYBIND11_MODULE(rekorder, m) { m.doc() = "XCP raw frame recorder."; py::class_(m, "_PyXcpLogFileReader") @@ -23,11 +39,15 @@ PYBIND11_MODULE(rekorder, m) { .def("add_frame", &XcpLogFileWriter::add_frame); py::class_(m, "_MeasurementParameters") - .def(py::init&>()); + .def(py::init< + std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector &>( + )); - py::class_(m, "_XcpLogFileUnfolder") - .def(py::init()) - .def("next_block", &XcpLogFileUnfolder::next_block) - .def("start", &XcpLogFileUnfolder::start) - ; + py::class_(m, "DAQParser", py::dynamic_attr()) + .def(py::init<>()) + .def("on_daq_list", &DAQParser::on_daq_list) + .def("feed", &DAQParser::feed) + .def("finalize", &DAQParser::finalize) + .def("set_parameters", &DAQParser::set_parameters) + .def("post_setup", &DAQParser::post_setup); } From a32b23d581574622db5ecb979861d119f2c119c1 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 19 Oct 2023 05:55:10 +0200 Subject: [PATCH 14/99] today() --- pyxcp/__init__.py | 4 + pyxcp/cmdline.py | 62 +--- pyxcp/config/__init__.py | 463 +++++++++++++++++------- pyxcp/config/legacy.py | 59 ++- pyxcp/examples/conf_can.toml | 2 +- pyxcp/examples/conf_eth.toml | 7 +- pyxcp/logger.py | 66 ---- pyxcp/master/errorhandler.py | 27 +- pyxcp/master/master.py | 49 ++- pyxcp/stim/stim_wrapper.cpp | 4 +- pyxcp/tests/test_master.py | 4 +- pyxcp/transport/__init__.py | 1 + pyxcp/transport/base.py | 19 +- pyxcp/transport/can.py | 243 ++++++------- pyxcp/transport/candriver/python_can.py | 98 +---- pyxcp/transport/eth.py | 41 +-- pyxcp/transport/sxi.py | 3 +- pyxcp/transport/usb_transport.py | 4 +- requirements.txt | 1 + setup.py | 1 + 20 files changed, 599 insertions(+), 559 deletions(-) delete mode 100644 pyxcp/logger.py diff --git a/pyxcp/__init__.py b/pyxcp/__init__.py index d72f86c..4ecf37b 100644 --- a/pyxcp/__init__.py +++ b/pyxcp/__init__.py @@ -3,6 +3,10 @@ """Universal Calibration Protocol for Python""" import sys +from rich.traceback import install + +install(show_locals=True, max_frames=3) # Install custom exception handler. + if sys.platform == "win32" and sys.version_info[:2] < (3, 11): # patch the time module with the high resolution alternatives try: diff --git a/pyxcp/cmdline.py b/pyxcp/cmdline.py index d1561a6..dd1981c 100644 --- a/pyxcp/cmdline.py +++ b/pyxcp/cmdline.py @@ -4,70 +4,30 @@ Parse (transport-layer specific) command line parameters and create a XCP master instance. """ -import argparse +import warnings from pyxcp.config import application -from pyxcp.config import readConfiguration from pyxcp.master import Master -from pyxcp.transport.can import registered_drivers -from pyxcp.transport.can import try_to_install_system_supplied_drivers -try_to_install_system_supplied_drivers() +warnings.simplefilter("always") -CAN_DRIVERS = registered_drivers() +class FakeParser: + def __getattr__(self, key): + if key == "add_argument": + warnings.warn("Argument parser extension is currently not supported.", DeprecationWarning) + return lambda *args, **kws: None -class ArgumentParser: - """ - - Parameter - --------- - callout: callable - Process user-supplied arguments. - """ +class ArgumentParser: def __init__(self, callout=None, *args, **kws): - self.callout = callout - kws.update(formatter_class=argparse.RawDescriptionHelpFormatter, add_help=True) - self._parser = argparse.ArgumentParser(*args, **kws) - self._parser.add_argument( - "-c", - "--config-file", - type=argparse.FileType("r"), - dest="conf", - help="File to read (extended) parameters from.", - ) - self._parser.add_argument( - "-l", - "--loglevel", - choices=["ERROR", "WARN", "INFO", "DEBUG"], - default="INFO", - ) - self._parser.epilog = "To get specific help on transport layers\nuse -h, e.g. {} eth -h".format(self._parser.prog) - self._args = [] - - @property - def args(self): - return self._args + self._parser = FakeParser() + if callout is not None: + warnings.warn("callout argument is not supported anymore", DeprecationWarning) def run(self, policy=None): - """""" - self._args = self.parser.parse_args() - args = self.args - if args.conf is None: - raise RuntimeError("Configuration file must be specified! (option: -c )") - config = readConfiguration(args.conf) - from pprint import pprint - - pprint(config) - # config["LOGLEVEL"] = args.loglevel - # if "TRANSPORT" not in config: - # raise AttributeError("TRANSPORT must be specified in config!") - # transport = config["TRANSPORT"].lower() transport = application.transport.layer master = Master(transport, config=application, policy=policy) - if self.callout: - self.callout(master, args) return master @property diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 8d96014..996148d 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -3,19 +3,22 @@ import io import json import sys +import typing import warnings from pathlib import Path -from pprint import pprint import can import toml from traitlets import Any from traitlets import Bool +from traitlets import Callable +from traitlets import Dict from traitlets import Enum from traitlets import Float from traitlets import Int from traitlets import Integer from traitlets import List +from traitlets import TraitError from traitlets import Unicode from traitlets import Union from traitlets.config import Application @@ -27,6 +30,8 @@ from pyxcp.config import legacy +warnings.simplefilter("always") + class CanBase: has_fd = False @@ -36,7 +41,16 @@ class CanBase: has_receive_own_messages = False has_timing = False - can_param_map = { + OPTIONAL_BASE_PARAMS = ( + "has_fd", + "has_bitrate", + "has_data_bitrate", + "has_poll_interval", + "has_receive_own_messages", + "has_timing", + ) + + CAN_PARAM_MAP = { "sjw_abr": None, "tseg1_abr": None, "tseg2_abr": None, @@ -104,10 +118,10 @@ class Gs_Usb(SingletonConfigurable, CanBase): ) -class Ics_Neovi(SingletonConfigurable, CanBase): +class Neovi(SingletonConfigurable, CanBase): """Intrepid Control Systems (ICS) neoVI interfaces.""" - interface_name = "ics_neovi" + interface_name = "neovi" has_fd = True has_data_bitrate = True @@ -154,7 +168,8 @@ class Ixxat(SingletonConfigurable, CanBase): allow_none=True, help="Secondary sample point (data). Only takes effect with fd and bitrate switch enabled.", ).tag(config=True) - can_param_map = { + + CAN_PARAM_MAP = { "sjw_abr": "sjw_abr", "tseg1_abr": "tseg1_abr", "tseg2_abr": "tseg2_abr", @@ -173,7 +188,7 @@ class Kvaser(SingletonConfigurable, CanBase): has_data_bitrate = True has_receive_own_messages = True - can_param_map = { + CAN_PARAM_MAP = { "sjw_abr": "sjw", "tseg1_abr": "tseg1", "tseg2_abr": "tseg2", @@ -228,6 +243,13 @@ class NixNet(SingletonConfigurable, CanBase): has_poll_interval = True has_receive_own_messages = True has_timing = True + has_fd = True + + CAN_PARAM_MAP = { + "data_bitrate": "fd_bitrate", + } + + can_termination = Bool(default_value=None, allow_none=True, help="Enable bus termination.") class PCan(SingletonConfigurable, CanBase): @@ -238,7 +260,7 @@ class PCan(SingletonConfigurable, CanBase): has_fd = True has_timing = True - can_param_map = { + CAN_PARAM_MAP = { "sjw_abr": "nom_sjw", "tseg1_abr": "nom_tseg1", "tseg2_abr": "nom_tseg2", @@ -257,70 +279,108 @@ class PCan(SingletonConfigurable, CanBase): first one that matches the parameter value. If no device is found, an exception is raised.""", ).tag(config=True) - state = Instance(can.bus.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) + state = Instance(can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) + f_clock = Enum( + [20000000, 24000000, 30000000, 40000000, 60000000, 80000000], + default_value=None, + allow_none=True, + help="""Ignored if not using CAN-FD. +Pass either f_clock or f_clock_mhz.""", + ).tag(config=True) + f_clock_mhz = Enum( + [20, 24, 30, 40, 60, 80], + default_value=None, + allow_none=True, + help="""Ignored if not using CAN-FD. +Pass either f_clock or f_clock_mhz. """, + ).tag(config=True) -# f_clock = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# f_clock_mhz = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) - -# nom_brp = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# data_brp = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + nom_brp = Integer( + min=1, + max=1024, + default_value=None, + allow_none=True, + help="""Clock prescaler for nominal time quantum. +Ignored if not using CAN-FD.""", + ).tag(config=True) + data_brp = Integer( + min=1, + max=1024, + default_value=None, + allow_none=True, + help="""Clock prescaler for fast data time quantum. +Ignored if not using CAN-FD.""", + ).tag(config=True) -# auto_reset = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + auto_reset = Bool( + default_value=None, + allow_none=True, + help="""Enable automatic recovery in bus off scenario. +Resetting the driver takes ~500ms during which +it will not be responsive.""", + ).tag(config=True) class Robotell(SingletonConfigurable, CanBase): - """ """ + """Interface for Chinese Robotell compatible interfaces""" interface_name = "robotell" - -# ttyBaudrate = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# rtscts = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + ttyBaudrate = Integer( + default_value=None, + allow_none=True, + help="""baudrate of underlying serial or usb device +(Ignored if set via the `channel` parameter, e.g. COM7@11500).""", + ).tag(config=True) + rtscts = Bool(default_value=None, allow_none=True, help="turn hardware handshake (RTS/CTS) on and off.").tag(config=True) class SeeedStudio(SingletonConfigurable, CanBase): - """ """ + """Seeed USB-Can analyzer interface.""" interface_name = "seeedstudio" - -# timeout = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# baudrate = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# frame_type = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# operation_mode = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) + baudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) + frame_type = Enum(["STD", "EXT"], default_value=None, allow_none=True, help="To select standard or extended messages.").tag( + config=True + ) + operation_mode = Enum( + ["normal", "loopback", "silent", "loopback_and_silent"], default_value=None, allow_none=True, help=""" """ + ).tag(config=True) class Serial(SingletonConfigurable, CanBase): - """ """ + """A text based interface.""" interface_name = "serial" has_bitrate = False - -# rtscts = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# timeout = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# baudrate = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + rtscts = Bool(default_value=None, allow_none=True, help="turn hardware handshake (RTS/CTS) on and off.").tag(config=True) + timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) + baudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) class SlCan(SingletonConfigurable, CanBase): - """ """ + """CAN over Serial / SLCAN.""" interface_name = "slcan" has_poll_interval = True - -# ttyBaudrate = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# rtscts = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# timeout = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# btr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# sleep_after_open = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + ttyBaudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) + rtscts = Bool(default_value=None, allow_none=True, help="turn hardware handshake (RTS/CTS) on and off.").tag(config=True) + timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) + btr = Integer(default_value=None, allow_none=True, help="BTR register value to set custom can speed.").tag(config=True) + sleep_after_open = Float( + default_value=None, allow_none=True, help="Time to wait in seconds after opening serial connection." + ).tag(config=True) class SocketCan(SingletonConfigurable, CanBase): - """ """ + """Linux SocketCAN.""" interface_name = "socketcan" @@ -328,35 +388,50 @@ class SocketCan(SingletonConfigurable, CanBase): has_bitrate = False has_receive_own_messages = True + local_loopback = Bool( + default_value=None, + allow_none=True, + help="""If local loopback should be enabled on this bus. +Please note that local loopback does not mean that messages sent +on a socket will be readable on the same socket, they will only +be readable on other open sockets on the same machine. More info +can be read on the socketcan documentation: +See https://www.kernel.org/doc/html/latest/networking/can.html#socketcan-local-loopback1""", + ).tag(config=True) + class SocketCanD(SingletonConfigurable, CanBase): - """ """ + """Network-to-CAN bridge as a Linux damon.""" interface_name = "socketcand" has_bitrate = False - -# host = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# port = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + host = Unicode(default_value=None, allow_none=True, help=""" """).tag(config=True) + port = Integer(default_value=None, allow_none=True, help=""" """).tag(config=True) class Systec(SingletonConfigurable, CanBase): - """ """ + """SYSTEC interface""" interface_name = "systec" has_receive_own_messages = True - -# state = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# device_number = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# rx_buffer_entries = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# tx_buffer_entries = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + state = Instance(can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) + device_number = Integer(min=0, max=254, default_value=None, allow_none=True, help="The device number of the USB-CAN.").tag( + config=True + ) + rx_buffer_entries = Integer( + default_value=None, allow_none=True, help="The maximum number of entries in the receive buffer." + ).tag(config=True) + tx_buffer_entries = Integer( + default_value=None, allow_none=True, help="The maximum number of entries in the transmit buffer." + ).tag(config=True) class Udp_Multicast(SingletonConfigurable, CanBase): - """ """ + """A virtual interface for CAN communications between multiple processes using UDP over Multicast IP.""" interface_name = "udp_multicast" @@ -364,24 +439,28 @@ class Udp_Multicast(SingletonConfigurable, CanBase): has_bitrate = False has_receive_own_messages = True - -# port = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# hop_limit = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + port = Integer(default_value=None, allow_none=True, help="The IP port to read from and write to.").tag(config=True) + hop_limit = Integer(default_value=None, allow_none=True, help="The hop limit in IPv6 or in IPv4 the time to live (TTL).").tag( + config=True + ) class Usb2Can(SingletonConfigurable, CanBase): - """ """ + """Interface to a USB2CAN Bus.""" interface_name = "usb2can" - -# flags = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# dll = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# serial.1 = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + flags = Integer( + default_value=None, allow_none=True, help="Flags to directly pass to open function of the usb2can abstraction layer." + ).tag(config=True) + dll = Unicode(default_value=None, allow_none=True, help="Path to the DLL with the CANAL API to load.").tag(config=True) + serial = Unicode(default_value=None, allow_none=True, help="Alias for `channel` that is provided for legacy reasons.").tag( + config=True + ) class Vector(SingletonConfigurable, CanBase): - """ """ + """Vector Informatik CAN interfaces.""" interface_name = "vector" @@ -391,16 +470,29 @@ class Vector(SingletonConfigurable, CanBase): has_receive_own_messages = True has_timing = True + CAN_PARAM_MAP = { + "sjw_abr": "sjw_abr", + "tseg1_abr": "tseg1_abr", + "tseg2_abr": "tseg2_abr", + "sjw_dbr": "sjw_dbr", + "tseg1_dbr": "tseg1_dbr", + "tseg2_dbr": "tseg2_dbr", + } -# sjw_abr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# tseg1_abr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# tseg2_abr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# sjw_dbr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# tseg1_dbr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# tseg2_dbr = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# serial.1 = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# rx_queue_size.1 = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# app_name = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + serial = Integer( + default_value=None, + allow_none=True, + help="""Serial number of the hardware to be used. +If set, the channel parameter refers to the channels ONLY on the specified hardware. +If set, the `app_name` does not have to be previously defined in +*Vector Hardware Config*.""", + ).tag(config=True) + rx_queue_size = Integer( + min=16, max=32768, default_value=None, allow_none=True, help="Number of messages in receive queue (power of 2)." + ).tag(config=True) + app_name = Unicode(default_value=None, allow_none=True, help="Name of application in *Vector Hardware Config*.").tag( + config=True + ) class Virtual(SingletonConfigurable, CanBase): @@ -411,25 +503,70 @@ class Virtual(SingletonConfigurable, CanBase): has_bitrate = False has_receive_own_messages = True - -# rx_queue_size.1 = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# preserve_timestamps = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) -# protocol = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) - - -class CAN(SingletonConfigurable): - interface = Unicode("", help="").tag(config=True) - channel = Any(help="").tag(config=True) + rx_queue_size = Integer( + default_value=None, + allow_none=True, + help="""The size of the reception queue. The reception +queue stores messages until they are read. If the queue reaches +its capacity, it will start dropping the oldest messages to make +room for new ones. If set to 0, the queue has an infinite capacity. +Be aware that this can cause memory leaks if messages are read +with a lower frequency than they arrive on the bus. """, + ).tag(config=True) + preserve_timestamps = Bool( + default_value=None, + allow_none=True, + help="""If set to True, messages transmitted via +will keep the timestamp set in the +:class:`~can.Message` instance. Otherwise, the timestamp value +will be replaced with the current system time.""", + ).tag( + config=True + ) # protocol = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + + +CAN_INTERFACE_MAP = { + "canalystii": CanAlystii, + "cantact": CanTact, + "etas": Etas, + "gs_usb": Gs_Usb, + "iscan": IsCan, + "ixxat": Ixxat, + "kvaser": Kvaser, + "neousys": NeouSys, + "neovi": Neovi, + "nican": NiCan, + "nixnet": NixNet, + "pcan": PCan, + "robotell": Robotell, + "seeedstudio": SeeedStudio, + "serial": Serial, + "slcan": SlCan, + "socketcan": SocketCan, + "socketcand": SocketCanD, + "systec": Systec, + "udp_multicast": Udp_Multicast, + "usb2can": Usb2Can, + "vector": Vector, + "virtual": Virtual, +} + + +class Can(SingletonConfigurable): + VALID_INTERFACES = can.interfaces.VALID_INTERFACES + + interface = Enum(VALID_INTERFACES, default_value=None, allow_none=True, help="CAN interface supported by python-can").tag( + config=True + ) + channel = Any( + default_value=None, allow_none=True, help="Channel identification. Expected type and value is backend dependent." + ).tag(config=True) max_dlc_required = Bool(False, help="Master to slave frames always to have DLC = MAX_DLC = 8").tag(config=True) max_can_fd_dlc = Integer(64, help="").tag(config=True) padding_value = Integer(0, help="Fill value, if max_dlc_required == True and DLC < MAX_DLC").tag(config=True) use_default_listener = Bool(True, help="").tag(config=True) - can_id_master = Integer(default_value=None, allow_none=True, help="CAN-ID master -> slave (Bit31= 1: extended identifier)").tag( - config=True - ) - can_id_slave = Integer(default_value=None, allow_none=True, help="CAN-ID slave -> master (Bit31= 1: extended identifier)").tag( - config=True - ) + can_id_master = Integer(allow_none=False, help="CAN-ID master -> slave (Bit31= 1: extended identifier)").tag(config=True) + can_id_slave = Integer(allow_none=True, help="CAN-ID slave -> master (Bit31= 1: extended identifier)").tag(config=True) can_id_broadcast = Integer( default_value=None, allow_none=True, help="Auto detection CAN-ID (Bit31= 1: extended identifier)" ).tag(config=True) @@ -459,21 +596,19 @@ class CAN(SingletonConfigurable): default_value=None, allow_none=True, help="""Custom bit timing settings. - (.s https://github.com/hardbyte/python-can/blob/develop/can/bit_timing.py) - If this parameter is provided, it takes precedence over all other - timing-related parameters. +(.s https://github.com/hardbyte/python-can/blob/develop/can/bit_timing.py) +If this parameter is provided, it takes precedence over all other +timing-related parameters. """, ).tag(config=True) - EXCLUDE_FROM_DRIVERS = () - classes = List( [ CanAlystii, CanTact, Etas, Gs_Usb, - Ics_Neovi, + Neovi, IsCan, Ixxat, Kvaser, @@ -498,11 +633,18 @@ class CAN(SingletonConfigurable): def __init__(self, **kws): super().__init__(**kws) + tos = self.class_own_traits() + + if self.parent.layer == "CAN": + if self.interface is None or self.interface not in self.VALID_INTERFACES: + raise TraitError( + f"CAN interface must be one of {sorted(list(self.VALID_INTERFACES))} not the {type(self.interface).__name__} {self.interface}." + ) self.canalystii = CanAlystii.instance(config=self.config, parent=self) self.cantact = CanTact.instance(config=self.config, parent=self) self.etas = Etas.instance(config=self.config, parent=self) self.gs_usb = Gs_Usb.instance(config=self.config, parent=self) - self.ics_neovi = Ics_Neovi.instance(config=self.config, parent=self) + self.neovi = Neovi.instance(config=self.config, parent=self) self.iscan = IsCan.instance(config=self.config, parent=self) self.ixxat = Ixxat.instance(config=self.config, parent=self) self.kvaser = Kvaser.instance(config=self.config, parent=self) @@ -522,10 +664,6 @@ def __init__(self, **kws): self.vector = Vector.instance(config=self.config, parent=self) self.virtual = Virtual.instance(config=self.config, parent=self) - def __str__(self): - # return f"Can(can_driver='{self.can_driver}', channel='{self.channel}', max_dlc_required={self.max_dlc_required}, max_can_fd_dlc={self.max_can_fd_dlc}, padding_value={self.padding_value}), CanAlystii={self.CanAlystii}, Etas={self.Etas}" - return f"Can(can_driver='{self.interface}', channel='{self.channel}', bitrate={self.bitrate}, max_dlc_required={self.max_dlc_required}, max_can_fd_dlc={self.max_can_fd_dlc}, padding_value={self.padding_value})" - class Eth(SingletonConfigurable): """ """ @@ -578,21 +716,29 @@ def __str__(self): class Transport(SingletonConfigurable): """ """ - classes = List([CAN, Eth, SxI, USB]) + classes = List([Can, Eth, SxI, USB]) - layer = Enum(["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=False).tag(config=True) # Enum - create_daq_timestamps = Bool(False).tag(config=True) - timeout = Float(2.0).tag(config=True) + layer = Enum( + ["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=False, help="Choose one of the supported XCP transport layers." + ).tag( + config=True + ) # Enum + create_daq_timestamps = Bool(False, help="Record time of frame reception or set timestamp to 0.").tag(config=True) + timeout = Float( + 2.0, + help="""raise `XcpTimeoutError` after `timeout` seconds +if there is no response to a command.""", + ).tag(config=True) alignment = Enum([1, 2, 4, 8], default_value=1).tag(config=True) - can = Instance(CAN).tag(config=True) + can = Instance(Can).tag(config=True) eth = Instance(Eth).tag(config=True) sxi = Instance(SxI).tag(config=True) usb = Instance(USB).tag(config=True) def __init__(self, **kws): super().__init__(**kws) - self.can = CAN.instance(config=self.config, parent=self) + self.can = Can.instance(config=self.config, parent=self) self.eth = Eth.instance(config=self.config, parent=self) self.sxi = SxI.instance(config=self.config, parent=self) self.usb = USB.instance(config=self.config, parent=self) @@ -611,6 +757,7 @@ class General(SingletonConfigurable): disconnect_response_optional = Bool(False).tag(config=True) seed_n_key_dll = Unicode("", allow_none=False).tag(config=True) seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) + seed_n_key_function = Callable(default_value=None, allow_none=True).tag(config=True) def __str__(self): return str( @@ -625,23 +772,89 @@ class PyXCP(Application): def initialize(self, argv=None): self.parse_command_line(argv) - if self.config_file: - self.load_config_file(self.config_file) - # res = load_pyconfig_files([self.config_file], "c:\csprojects") - # print("Loaded CFG-File: ", self.config_file, res, self.config) - print("SC", self.config) + self.read_configuration_file() self.general = General.instance(config=self.config, parent=self) - self.transport = Transport.instance(parent=self) # Transport(config=self.config, parent=self) - - def confy(self): - for klass in application._classes_with_config_traits(): - # pprint(klass.class_config_section()) - if hasattr(klass, "classes"): - print("KLASEES", klass.classes) - for name, tr_type in klass._traits.items(): - if isinstance(tr_type, Instance): - print(name, tr_type) - # ctr = klass.class_traits(config=True) + self.transport = Transport.instance(parent=self) + + def read_configuration_file(self): + pth = Path(self.config_file) + suffix = pth.suffix.lower() + if suffix == ".py": + self.load_config_file(self.config_file) + else: + if suffix == ".json": + reader = json + elif suffix == ".toml": + reader = toml + else: + raise ValueError(f"Unknown file type for config: {suffix}") + with pth.open("r") as f: + warnings.warn("Old-style configuration file. Please user python based configuration.", DeprecationWarning) + cfg = reader.loads(f.read()) + if cfg: + cfg = legacy.convert_config(cfg) + self.config = cfg + return cfg + + flags = Dict( # type:ignore[assignment] + dict( + debug=({"PyXCP": {"log_level": 10}}, "Set loglevel to DEBUG"), + ) + ) + + aliases = Dict( # type:ignore[assignment] + dict( + c="PyXCP.config_file", + log_level="PyXCP.log_level", + l="PyXCP.log_level", + ) + ) + + def _iterate_config_class(self, klass, class_names: typing.List[str]) -> None: + sub_classes = [] + class_path = ".".join(class_names) + print( + f"""\n# ------------------------------------------------------------------------------ +# {class_path} configuration +# ------------------------------------------------------------------------------""", + end="\n\n", + ) + if hasattr(klass, "classes"): + kkk = klass.classes + if hasattr(kkk, "default"): + if class_names[-1] not in ("PyXCP"): + sub_classes.extend(kkk.default()) + for name, tr in klass.class_own_traits().items(): + md = tr.metadata + if md.get("config"): + help = md.get("help", "").lstrip() + commented_lines = "\n".join([f"# {line}" for line in help.split("\n")]) + print(f"#{commented_lines}") + value = tr.default() + if isinstance(tr, Instance) and tr.__class__.__name__ not in ("Dict",): + continue + if isinstance(tr, Unicode) and value is not None: + value = f"'{value}'" + if isinstance(tr, Enum): + print(f"# Choices: {tr.info()}") + else: + print(f"# Type: {tr.info()}") + print(f"# Default: {value}") + + print(f"# c.{class_path}.{name} = {value}", end="\n\n") + if class_names is None: + class_names = [] + for sub_klass in sub_classes: + self._iterate_config_class(sub_klass, class_names + [sub_klass.__name__]) + + def generate_config_file(self, file_like: io.IOBase, config=None) -> None: + print("#") + print("# Configuration file for pyXCP.") + print("#") + print("c = get_config() # noqa", end="\n\n") + + for klass in self._classes_with_config_traits(): + self._iterate_config_class(klass, [klass.__name__]) def __str__(self): return f"PyXCP: {self.config.general}" @@ -656,21 +869,9 @@ class Configuration: application.initialize(sys.argv) application.start() +# print(application.generate_config_file()) +# print("*" * 80) -def readConfiguration(phile: io.TextIOWrapper): - pth = Path(phile.name) - suffix = pth.suffix.lower() - if suffix == ".py": - pass - else: - if suffix == ".json": - reader = json - elif suffix == ".toml": - reader = toml - else: - raise ValueError(f"Unknown file type for config: {suffix}") - with pth.open("r") as f: - cfg = reader.loads(f.read()) - if cfg: - cfg = legacy.convert_config(cfg) - return cfg +import sys + +application.generate_config_file(sys.stdout) diff --git a/pyxcp/config/legacy.py b/pyxcp/config/legacy.py index 1300e4d..26e201e 100644 --- a/pyxcp/config/legacy.py +++ b/pyxcp/config/legacy.py @@ -22,7 +22,7 @@ "IPV6": "Transport.Eth.ipv6", "TCP_NODELAY": "Transport.Eth.tcp_nodelay", # Can - "CAN_DRIVER": "Transport.Can.driver", + "CAN_DRIVER": "Transport.Can.interface", "CHANNEL": "Transport.Can.channel", "MAX_DLC_REQUIRED": "Transport.Can.max_dlc_required", "MAX_CAN_FD_DLC": "Transport.Can.max_can_fd_dlc", @@ -33,6 +33,30 @@ "CAN_ID_BROADCAST": "Transport.Can.can_id_broadcast", "BITRATE": "Transport.Can.bitrate", "RECEIVE_OWN_MESSAGES": "Transport.Can.receive_own_messages", + "POLL_INTERVAL": "Transport.Can.poll_interval", + "FD": "Transport.Can.fd", + "DATA_BITRATE": "Transport.Can.data_bitrate", + "ACCEPT_VIRTUAL": "Transport.Can.Kvaser.accept_virtual", + "SJW": "Transport.Can.sjw_abr", + "TSEG1": "Transport.Can.tseg1_abr", + "TSEG2": "Transport.Can.tseg2_abr", + "TTY_BAUDRATE": "Transport.Can.SlCan.ttyBaudrate", + "UNIQUE_HARDWARE_ID": "Transport.Can.Ixxat.unique_hardware_id", + "RX_FIFO_SIZE": "Transport.Can.Ixxat.rx_fifo_size", + "TX_FIFO_SIZE": "Transport.Can.Ixxat.tx_fifo_size", + "DRIVER_MODE": "Transport.Can.Kvaser.driver_mode", + "NO_SAMP": "Transport.Can.Kvaser.no_samp", + "SINGLE_HANDLE": "Transport.Can.Kvaser.single_handle", + "USE_SYSTEM_TIMESTAMP": "Transport.Can.Neovi.use_system_timestamp", + "OVERRIDE_LIBRARY_NAME": "Transport.Can.Neovi.override_library_name", + "BAUDRATE": "Transport.Can.Serial.baudrate", + "SLEEP_AFTER_OPEN": "Transport.Can.SlCan.sleep_after_open", + "DEVICE_NUMBER": "Transport.Can.Systec.device_number", + "RX_BUFFER_ENTRIES": "Transport.Can.Systec.rx_buffer_entries", + "TX_BUFFER_ENTRIES": "Transport.Can.Systec.tx_buffer_entries", + "FLAGS": "Transport.Can.Usb2Can.flags", + "APP_NAME": "Transport.Can.Vector.app_name", + "RX_QUEUE_SIZE": "Transport.Can.Vector.rx_queue_size", } @@ -47,9 +71,38 @@ def nested_dict_update(d: dict, key: str, value) -> None: def convert_config(legacy_config: dict) -> Config: + interface_name = None + resolv = [] d = defaultdict(dict) for key, value in legacy_config.items(): item = LEGACY_KEYWORDS.get(key) - print(key, value) - nested_dict_update(d=d, key=item, value=value) + if item is None: + warnings.warn(f"Unknown keyword '{key}' in config file") # TODO: logger + continue + if key == "CAN_DRIVER": + value = value.lower() + interface_name = value + if key in ("SERIAL", "LOG_ERRORS", "STATE", "RTSCTS"): + resolv.append((key, value)) + else: + nested_dict_update(d=d, key=item, value=value) + for key, value in resolv: + if key == "SERIAL": + if interface_name == "neovi": + d["Transport.Can.Neovi.serial"] = value + elif interface_name == "vector": + d["Transport.Can.Vector.serial"] = value + elif key == "LOG_ERRORS": + if interface_name == "nican": + d["Transport.Can.NiCan.log_errors"] = value + elif key == "STATE": + if interface_name == "pcan": + d["Transport.Can.PCan.state"] = value + elif interface_name == "systec": + d["Transport.Can.Systec.state"] = value + elif key == "RTSCTS": + if interface_name == "serial": + d["Transport.Can.Serial.rtscts"] = value + elif interface_name == "slcan": + d["Transport.Can.SlCan.rtscts"] = value return Config(d) diff --git a/pyxcp/examples/conf_can.toml b/pyxcp/examples/conf_can.toml index 1172d14..7578276 100644 --- a/pyxcp/examples/conf_can.toml +++ b/pyxcp/examples/conf_can.toml @@ -1,5 +1,5 @@ TRANSPORT = "CAN" -CAN_DRIVER = "Kvaser" +CAN_DRIVER = "KVaser" CAN_USE_DEFAULT_LISTENER = true CHANNEL = "0" ACCEPT_VIRTUAL = true diff --git a/pyxcp/examples/conf_eth.toml b/pyxcp/examples/conf_eth.toml index 6c16717..05a143f 100644 --- a/pyxcp/examples/conf_eth.toml +++ b/pyxcp/examples/conf_eth.toml @@ -1,6 +1,11 @@ -TRANSPORT = "ETH" +# TRANSPORT = "ETH" +TRANSPORT = "CAN" +CAN_DRIVER = "kvaser" +CHANNEL=0 HOST = "localhost" PORT = 5555 PROTOCOL = "UDP" IPV6 = false CREATE_DAQ_TIMESTAMPS = false +SEED_N_KEY_DLL = "SeedNKeyXcp.dll" + diff --git a/pyxcp/logger.py b/pyxcp/logger.py deleted file mode 100644 index dda4c9e..0000000 --- a/pyxcp/logger.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import logging - -logging.basicConfig() - - -class Logger(object): - LOGGER_BASE_NAME = "pyxcp" - FORMAT = "[%(levelname)s (%(name)s)]: %(message)s" - - def __init__(self, name, level=logging.WARN): - self.logger = logging.getLogger("{0}.{1}".format(self.LOGGER_BASE_NAME, name)) - self.logger.setLevel(level) - handler = logging.StreamHandler() - handler.setLevel(level) - formatter = logging.Formatter(self.FORMAT) - handler.setFormatter(formatter) - self.logger.addHandler(handler) - # self.logger.propagate = False - self.lastMessage = None - self.lastSeverity = None - - def getLastError(self): - result = (self.lastSeverity, self.lastMessage) - self.lastSeverity = self.lastMessage = None - return result - - def log(self, message, level): - self.lastSeverity = level - self.lastMessage = message - self.logger.log(level, "{0}".format(message)) - # print("{0}{1}".format(level, message)) - - def info(self, message): - self.log(message, logging.INFO) - - def warn(self, message): - self.log(message, logging.WARN) - - def debug(self, message): - self.log(message, logging.DEBUG) - - def error(self, message): - self.log(message, logging.ERROR) - - def critical(self, message): - self.log(message, logging.CRITICAL) - - def verbose(self): - self.logger.setLevel(logging.DEBUG) - - def silent(self): - self.logger.setLevel(logging.CRITICAL) - - def setLevel(self, level): - LEVEL_MAP = { - "INFO": logging.INFO, - "WARN": logging.WARN, - "DEBUG": logging.DEBUG, - "ERROR": logging.ERROR, - "CRITICAL": logging.CRITICAL, - } - if isinstance(level, str): - level = LEVEL_MAP.get(level.upper(), logging.WARN) - self.logger.setLevel(level) diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index eebd85e..5eda711 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -3,13 +3,15 @@ """Implements error-handling according to XCP spec. """ import functools -import logging import os import threading import time import types from collections import namedtuple +import can + +from pyxcp.config import application from pyxcp.errormatrix import Action from pyxcp.errormatrix import ERROR_MATRIX from pyxcp.errormatrix import PreAction @@ -18,11 +20,8 @@ from pyxcp.types import XcpResponseError from pyxcp.types import XcpTimeoutError - handle_errors = True # enable/disable XCP error-handling. -logger = logging.getLogger("pyxcp.errorhandler") - class SingletonBase(object): _lock = threading.Lock() @@ -176,7 +175,7 @@ def display_error(): class Handler: """""" - logger = logger + logger = application.log def __init__(self, instance, func, arguments, error_code=None): self.instance = instance @@ -209,7 +208,7 @@ def repeater(self, value): self._repeater = value def execute(self): - self.logger.debug(f"EXECUTE func = {func_name(self.func)} arguments = {self.arguments})") + self.logger.debug(f"Execute({func_name(self.func)} arguments = {self.arguments})") if isinstance(self.func, types.MethodType): return self.func(*self.arguments.args, **self.arguments.kwargs) else: @@ -323,14 +322,13 @@ class Executor(SingletonBase): handlerStack = HandlerStack() repeater = None - logger = logger + logger = application.log previous_error_code = None error_code = None func = None arguments = None def __call__(self, inst, func, arguments): - self.logger.debug(f"__call__({func.__qualname__})") self.inst = inst self.func = func self.arguments = arguments @@ -341,15 +339,20 @@ def __call__(self, inst, func, arguments): while True: try: handler = self.handlerStack.tos() - # print("\t\tEXEC", hex(id(handler))) res = handler.execute() except XcpResponseError as e: - self.logger.error(f"XcpResponseError [{str(e)}]") + # self.logger.critical(f"XcpResponseError [{str(e)}]") self.error_code = e.get_error_code() - except XcpTimeoutError as e: - self.logger.error(f"XcpTimeoutError [{str(e)}]") + except XcpTimeoutError: + # self.logger.error(f"XcpTimeoutError [{str(e)}]") self.error_code = XcpError.ERR_TIMEOUT + except TimeoutError: + raise + except can.CanError: + # self.logger.critical(f"Exception raised by Python CAN [{str(e)}]") + raise except Exception: + # self.logger.critical(f"Exception [{str(e)}]") raise else: self.error_code = None diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index c6d9edf..8114a94 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -78,31 +78,15 @@ class Master: config: dict """ - PARAMETER_MAP = { - # Type Req'd Default - "LOGLEVEL": (str, False, "WARN"), - "DISABLE_ERROR_HANDLING": ( - bool, - False, - False, - ), # Bypass error-handling for performance reasons. - "SEED_N_KEY_DLL": (str, False, ""), - "SEED_N_KEY_DLL_SAME_BIT_WIDTH": (bool, False, False), - "DISCONNECT_RESPONSE_OPTIONAL": (bool, False, False), - } - def __init__(self, transport_name: str, config, policy=None): self.ctr = 0 self.succeeded = True self.config = config.general - print(self.config.seed_n_key_dll) self.logger = config.log - self.logger.setLevel("INFO") - self.logger.info("NEW cfg system!!!") disable_error_handling(self.config.disable_error_handling) self.transport_name = transport_name.lower() - transport_config = config.transport # getattr(config.transport, self.transport_name) + transport_config = config.transport self.transport = createTransport(transport_name, transport_config, policy) self.transport.set_writer_lock(get_writer_lock()) @@ -132,8 +116,9 @@ def __init__(self, transport_name: str, config, policy=None): self.mta = types.MtaType(None, None) self.currentDaqPtr = None self.currentProtectionStatus = None - self.seedNKeyDLL = self.config.seed_n_key_dll # .get("SEED_N_KEY_DLL") - self.seedNKeyDLL_same_bit_width = self.config.seed_n_key_dll_same_bit_width + self.seed_n_key_dll = self.config.seed_n_key_dll + self.seed_n_key_function = self.config.seed_n_key_function + self.seed_n_key_dll_same_bit_width = self.config.seed_n_key_dll_same_bit_width self.disconnect_response_optional = self.config.disconnect_response_optional self.slaveProperties = SlaveProperties() self.slaveProperties.pgmProcessor = SlaveProperties() @@ -1807,8 +1792,8 @@ def cond_unlock(self, resources=None): MAX_PAYLOAD = self.slaveProperties["maxCto"] - 2 - if not self.seedNKeyDLL: - raise RuntimeError("No seed and key DLL specified, cannot proceed.") + if not (self.seed_n_key_dll or self.seed_n_key_function): + raise RuntimeError("Neither seed and key DLL or function specified, cannot proceed.") if resources is None: result = [] if self.slaveProperties["supportsCalpag"]: @@ -1839,15 +1824,23 @@ def cond_unlock(self, resources=None): result = self.getSeed(types.XcpGetSeedMode.REMAINING, resource_value) seed.extend(list(result.seed)) remaining = result.length - result, key = getKey( - self.logger, - self.seedNKeyDLL, - resource_value, - bytes(seed), - self.seedNKeyDLL_same_bit_width, - ) + self.logger.debug(f"Got seed {seed} for resource {resource_value}.") + if self.seed_n_key_function: + key = self.seed_n_key_function(resource_value, bytes(seed)) + self.logger.debug(f"Using seed and key function '{self.seed_n_key_function.__name__}()'.") + result = SeedNKeyResult.ACK + elif self.seed_n_key_dll: + self.logger.debug(f"Using seed and key DLL '{self.seed_n_key_dll}'.") + result, key = getKey( + self.logger, + self.seed_n_key_dll, + resource_value, + bytes(seed), + self.seed_n_key_dll_same_bit_width, + ) if result == SeedNKeyResult.ACK: key = list(key) + self.logger.debug(f"Unlocking resource {resource_value} with key {key}.") total_length = len(key) offset = 0 while offset < total_length: diff --git a/pyxcp/stim/stim_wrapper.cpp b/pyxcp/stim/stim_wrapper.cpp index 544d173..e3b7f72 100644 --- a/pyxcp/stim/stim_wrapper.cpp +++ b/pyxcp/stim/stim_wrapper.cpp @@ -41,10 +41,10 @@ PYBIND11_MODULE(stim, m) { ; py::class_(m, "Mutex") - .def("__enter__", [&](Mutex& self) { self.lock(); }) + .def("__enter__", [&](Mutex& self) { /*std::cout << "__enter__ Mutex()\n";*/ /*self.lock();*/ }) .def( "__exit__", [&](Mutex& self, const std::optional& exc_type, const std::optional& exc_value, - const std::optional& traceback) { self.unlock(); } + const std::optional& traceback) { /*std::cout << "__exit____ Mutex()\n";*/ /*self.unlock();*/ } ); } diff --git a/pyxcp/tests/test_master.py b/pyxcp/tests/test_master.py index 038872b..d3d4d85 100644 --- a/pyxcp/tests/test_master.py +++ b/pyxcp/tests/test_master.py @@ -748,7 +748,7 @@ def testDownloadBlock(self): "CAN_USE_DEFAULT_LISTENER": False, } with Master("can", config=conf) as xm: - mock_caninterface = xm.transport.canInterface + mock_caninterface = xm.transport.can_interface mock_caninterface.push_packet(self.DefaultConnectResponse) xm.connect() @@ -817,7 +817,7 @@ def testDownloadNextBlock(self): "CAN_USE_DEFAULT_LISTENER": False, } with Master("can", config=conf) as xm: - mock_caninterface = xm.transport.canInterface + mock_caninterface = xm.transport.can_interface mock_caninterface.push_packet(self.DefaultConnectResponse) xm.connect() diff --git a/pyxcp/transport/__init__.py b/pyxcp/transport/__init__.py index 871200a..36b5015 100644 --- a/pyxcp/transport/__init__.py +++ b/pyxcp/transport/__init__.py @@ -3,6 +3,7 @@ from .base import FrameAcquisitionPolicy from .base import FrameRecorderPolicy from .base import LegacyFrameAcquisitionPolicy +from .base import NoOpPolicy from .base import StdoutPolicy from .can import Can from .eth import Eth diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 2f1090c..9ad1cf0 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -42,10 +42,11 @@ def filtered_out(self) -> typing.Set[types.FrameCategory]: def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None: ... - def finalize(self) -> None: + def finalize(self, *args) -> None: """ Finalize the frame acquisition policy (if required). """ + ... class NoOpPolicy(FrameAcquisitionPolicy): @@ -149,14 +150,6 @@ class BaseTransport(metaclass=abc.ABCMeta): """ - PARAMETER_MAP = { - # Type Req'd Default - "CREATE_DAQ_TIMESTAMPS": (bool, False, False), - "LOGLEVEL": (str, False, "WARN"), - "TIMEOUT": (float, False, 2.0), - "ALIGNMENT": (int, False, 1), - } - def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.parent = None self.policy = policy or LegacyFrameAcquisitionPolicy() @@ -164,14 +157,8 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.command_lock = threading.Lock() - # loglevel = self.config.get("LOGLEVEL") - - # self._debug = True - self._debug = False self.logger = config.log - # self._debug = loglevel == "DEBUG" - # self.logger = Logger("transport.Base") - # self.logger.setLevel(loglevel) + self._debug = self.logger.level == 10 self.counterSend = 0 self.counterReceived = -1 diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index a2259b6..3d949ba 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -6,11 +6,20 @@ import functools import operator from bisect import bisect_left -from collections import OrderedDict -from time import perf_counter from time import time - -from pyxcp.config import Configuration +from typing import Any +from typing import Callable +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple +from typing import Union + +from can import CanError +from can import Message +from can.interface import _get_class_for_interface + +from pyxcp.config import CAN_INTERFACE_MAP from pyxcp.transport.base import BaseTransport @@ -194,61 +203,70 @@ def __init__(self, id_: Identifier, dlc: int, data: bytes, timestamp: int): self.timestamp = timestamp def __repr__(self): - return "Frame(id = 0x{:08x}, dlc = {}, data = {}, timestamp = {})".format(self.id, self.dlc, self.data, self.timestamp) + return f"Frame(id = 0x{self.id:08x}, dlc = {self.dlc}, data = {self.data}, timestamp = {self.timestamp})" __str__ = __repr__ -class CanInterfaceBase(metaclass=abc.ABCMeta): - """ - Abstract CAN interface handler that can be implemented for any actual CAN device driver - """ - - PARAMETER_MAP = {} - - @abc.abstractmethod - def init(self, parent, receive_callback): - """ - Must implement any required action for initing the can interface +class PythonCanWrapper: + """Wrapper around python-can - github.com/hardbyte/python-can""" - Parameters - ---------- - parent: :class:`Can` - Refers to owner. - receive_callback: callable - Receive callback function to register with the following argument: payload: bytes - """ + def __init__(self, parent, interface_name: str, **parameters): + self.parent = parent + self.interface_name = interface_name + self.parameters = parameters + self.can_interface_class = _get_class_for_interface(self.interface_name) + self.connected = False - @abc.abstractmethod - def transmit(self, payload: bytes): - """ - Must transmit the given payload on the master can id. - - Parameters - ---------- - payload: bytes - payload to transmit - """ - - @abc.abstractmethod - def close(self): - """Must implement any required action for disconnecting from the can interface""" - - @abc.abstractmethod def connect(self): - """Open connection to can interface""" + if self.connected: + return + can_id = self.parent.can_id_master + can_filter = { + "can_id": can_id.id, + "can_mask": MAX_29_BIT_IDENTIFIER if can_id.is_extended else MAX_11_BIT_IDENTIFIER, + "extended": can_id.is_extended, + } + self.can_interface = self.can_interface_class(interface=self.interface_name, **self.parameters) + self.can_interface.set_filters([can_filter]) + self.parent.logger.debug("Python-CAN driver: {} - {}]".format(self.interface_name, self.can_interface)) + self.connected = True - @abc.abstractmethod - def read(self): - """Read incoming data""" + def close(self): + if self.connected: + self.can_interface.shutdown() + self.connected = False + + def transmit(self, payload: bytes) -> None: + frame = Message( + arbitration_id=self.parent.can_id_slave.id, + is_extended_id=True if self.parent.can_id_slave.is_extended else False, + is_fd=self.parent.fd, + data=payload, + ) + self.can_interface.send(frame) - @abc.abstractmethod - def getTimestampResolution(self): - """Get timestamp resolution in nano seconds.""" + def read(self) -> Optional[Frame]: + if not self.connected: + return None + try: + frame = self.can_interface.recv(5) + except CanError: + return None + else: + if frame is None or frame.arbitration_id != self.parent.can_id_master.id or not len(frame.data): + return None # Timeout condition. + extended = frame.is_extended_id + identifier = Identifier.make_identifier(frame.arbitration_id, extended) + return Frame( + id_=identifier, + dlc=frame.dlc, + data=frame.data, + timestamp=frame.timestamp, + ) - def loadConfig(self, config): - """Load configuration data.""" - self.config = Configuration(self.PARAMETER_MAP or {}, config or {}) + def getTimestampResolution(self) -> int: + return 10 * 1000 class EmptyHeader: @@ -258,64 +276,62 @@ def pack(self, *args, **kwargs): return b"" -# can.detect_available_configs() - - class Can(BaseTransport): """""" - PARAMETER_MAP = { - # Type Req'd Default - "CAN_DRIVER": (str, True, None), - "CHANNEL": (str, False, ""), - "MAX_DLC_REQUIRED": (bool, False, False), - "MAX_CAN_FD_DLC": (int, False, 64), - "PADDING_VALUE": (int, False, 0), - "CAN_USE_DEFAULT_LISTENER": (bool, False, True), - # defaults to True, in this case the default listener thread is used. - # If the canInterface implements a listener service, this parameter - # can be set to False, and the default listener thread won't be started. - "CAN_ID_MASTER": (int, True, None), - "CAN_ID_SLAVE": (int, True, None), - "CAN_ID_BROADCAST": (int, False, None), - "BITRATE": (int, False, 250000), - "RECEIVE_OWN_MESSAGES": (bool, False, False), - } - - PARAMETER_TO_KW_ARG_MAP = { - "RECEIVE_OWN_MESSAGES": "receive_own_messages", - "CHANNEL": "channel", - "BITRATE": "bitrate", - } - MAX_DATAGRAM_SIZE = 7 HEADER = EmptyHeader() HEADER_SIZE = 0 def __init__(self, config, policy=None): - """init for CAN transport - :param config: configuration - """ super().__init__(config, policy) self.load_config(config) - drivers = registered_drivers() - interface_name = self.config.interface - if interface_name not in drivers: - raise ValueError("{} is an invalid driver name -- choose from {}".format(interface_name, [x for x in drivers.keys()])) - canInterfaceClass = drivers[interface_name] - self.canInterface = canInterfaceClass() - self.useDefaultListener = self.config.get("CAN_USE_DEFAULT_LISTENER") - self.can_id_master = Identifier(self.config.get("CAN_ID_MASTER")) - self.can_id_slave = Identifier(self.config.get("CAN_ID_SLAVE")) - self.canInterface.load_config(config) - self.canInterface.init(self, self.dataReceived) - # + self.useDefaultListener = self.config.use_default_listener + self.can_id_master = Identifier(self.config.can_id_master) + self.can_id_slave = Identifier(self.config.can_id_slave) + # Regarding CAN-FD s. AUTOSAR CP Release 4.3.0, Requirements on CAN; [SRS_Can_01160] Padding of bytes due to discrete CAN FD DLC]: # "... If a PDU does not exactly match these configurable sizes the unused bytes shall be padded." # - self.max_dlc_required = self.config.get("MAX_DLC_REQUIRED") or self.canInterface.is_fd - self.padding_value = self.config.get("PADDING_VALUE") - self.padding_len = self.config.get("MAX_CAN_FD_DLC") if self.canInterface.is_fd else MAX_DLC_CLASSIC + self.fd = self.config.fd + self.max_dlc_required = self.config.max_dlc_required # or self.fd + self.padding_value = self.config.padding_value + self.padding_len = self.config.max_can_fd_dlc if self.fd else MAX_DLC_CLASSIC + self.interface_name = self.config.interface + + parameters = self.get_interface_parameters() + self.logger.debug(f"Opening '{self.interface_name}' CAN-interface -- {list(parameters.items())}") + self.can_interface = PythonCanWrapper(self, self.interface_name, **parameters) + + def get_interface_parameters(self) -> Dict[str, Any]: + result = dict(channel=self.config.channel) + + can_interface_config_class = CAN_INTERFACE_MAP[self.interface_name] + + # Optional base class parameters. + optional = [(p, p.removeprefix("has_")) for p in can_interface_config_class.OPTIONAL_BASE_PARAMS] + for o, n in optional: + opt = getattr(can_interface_config_class, o) + value = getattr(self.config, n) + if opt: + if value is not None: + result[n] = value + elif value is not None: + self.logger.warning(f"'{self.interface_name}' has no support for parameter '{n}'.") + + # Parameter names that need to be mapped. + for base_name, name in can_interface_config_class.CAN_PARAM_MAP.items(): + value = getattr(self.config, base_name) + if value is not None: + result[name] = value + + # Interface specific parameters. + cxx = getattr(self.config, self.interface_name) + for name in can_interface_config_class.class_own_traits().keys(): + value = getattr(cxx, name) + if value is not None: + result[name] = value + return result def dataReceived(self, payload: bytes, recv_timestamp: float = None): self.processResponse( @@ -327,30 +343,30 @@ def dataReceived(self, payload: bytes, recv_timestamp: float = None): def listen(self): while True: - if self.closeEvent.isSet(): + if self.closeEvent.is_set(): return - frame = self.canInterface.read() + frame = self.can_interface.read() if frame: self.dataReceived(frame.data, frame.timestamp) def connect(self): if self.useDefaultListener: self.startListener() - self.canInterface.connect() + self.can_interface.connect() self.status = 1 # connected - def send(self, frame): + def send(self, frame: bytes) -> None: # XCP on CAN trailer: if required, FILL bytes must be appended if self.max_dlc_required: frame = padFrame(frame, self.padding_value, self.padding_len) # send the request self.pre_send_timestamp = time() - self.canInterface.transmit(payload=frame) + self.can_interface.transmit(payload=frame) self.post_send_timestamp = time() def closeConnection(self): - if hasattr(self, "canInterface"): - self.canInterface.close() + if hasattr(self, "can_interface"): + self.can_interface.close() def close(self): self.finishListener() @@ -389,28 +405,3 @@ def calculateFilter(ids: list): cmask = functools.reduce(operator.or_, raw_ids) ^ cfilter cmask ^= 0x1FFFFFFF if any_extended_ids else 0x7FF return (cfilter, cmask) - - -def try_to_install_system_supplied_drivers(): - """Register available pyxcp CAN drivers.""" - import importlib - import pkgutil - import pyxcp.transport.candriver as cdr - - for _, modname, _ in pkgutil.walk_packages(cdr.__path__, "{}.".format(cdr.__name__)): - try: - importlib.import_module(modname) - except Exception: - pass - - -def registered_drivers(): - """ - Returns - ------- - dict (name, class) - Dictionary containing CAN driver names and classes of all - available drivers (pyxcp supplied and user-defined). - """ - sub_classes = CanInterfaceBase.__subclasses__() - return OrderedDict(zip(([c.__name__ for c in sub_classes]), sub_classes)) diff --git a/pyxcp/transport/candriver/python_can.py b/pyxcp/transport/candriver/python_can.py index 1722b14..eb0094d 100644 --- a/pyxcp/transport/candriver/python_can.py +++ b/pyxcp/transport/candriver/python_can.py @@ -3,100 +3,8 @@ """ Support for python-can - github.com/hardbyte/python-can """ -import re -from collections import OrderedDict +from typing import Optional -from can import Bus -from can import CanError -from can import Message +from can.interface import _get_class_for_interface -import pyxcp.transport.can as can - -NUMBER = re.compile(r"(?P0[x|X])?(?P[0-9]+)", re.VERBOSE) - - -class PythonCAN: - """""" - - def __init__(self, bustype): - self.bustype = bustype - self.connected = False - - def init(self, parent, receive_callback): - self.parent = parent - self.is_fd = self.config.get("FD") - - def connect(self): - if self.connected: - return - - self.kwargs = OrderedDict() - # Fetch driver keyword arguments. - self._fetch_kwargs(False) - self._fetch_kwargs(True) - can_id = self.parent.can_id_master - can_filter = { - "can_id": can_id.id, - "can_mask": can.MAX_29_BIT_IDENTIFIER if can_id.is_extended else can.MAX_11_BIT_IDENTIFIER, - "extended": can_id.is_extended, - } - self.bus = Bus(bustype=self.bustype, **self.kwargs) - self.bus.set_filters([can_filter]) - self.parent.logger.debug("Python-CAN driver: {} - {}]".format(self.bustype, self.bus)) - self.connected = True - - def _fetch_kwargs(self, local): - if local: - base = self - else: - base = self.parent - for param, arg in base.PARAMETER_TO_KW_ARG_MAP.items(): - value = base.config.get(param) - # if param == "CHANNEL": - # value = self._handle_channel(value) - self.kwargs[arg] = value - - def _handle_channel(self, value): - match = NUMBER.match(value) - if match: - gd = match.groupdict() - base = 16 if not gd["hex"] is None else 10 - return int(value, base) - else: - return value - - def close(self): - if self.connected: - self.bus.shutdown() - self.connected = False - - def transmit(self, payload): - frame = Message( - arbitration_id=self.parent.can_id_slave.id, - is_extended_id=True if self.parent.can_id_slave.is_extended else False, - is_fd=self.is_fd, - data=payload, - ) - self.bus.send(frame) - - def read(self): - if not self.connected: - return None - try: - frame = self.bus.recv(5) - except CanError: - return None - else: - if frame is None or frame.arbitration_id != self.parent.can_id_master.id or not len(frame.data): - return None # Timeout condition. - extended = frame.is_extended_id - identifier = can.Identifier.make_identifier(frame.arbitration_id, extended) - return can.Frame( - id_=identifier, - dlc=frame.dlc, - data=frame.data, - timestamp=frame.timestamp, - ) - - def getTimestampResolution(self): - return 10 * 1000 +# import pyxcp.transport.can as can diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 2a8a8da..8ddb753 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -19,17 +19,6 @@ class Eth(BaseTransport): """""" - PARAMETER_MAP = { - # Type Req'd Default - "HOST": (str, False, "localhost"), - "PORT": (int, False, 5555), - "BIND_TO_ADDRESS": (str, False, ""), - "BIND_TO_PORT": (int, False, 5555), - "PROTOCOL": (str, False, "TCP"), - "IPV6": (bool, False, False), - "TCP_NODELAY": (bool, False, False), - } - MAX_DATAGRAM_SIZE = 512 HEADER = struct.Struct("=2.9 mako traitlets chardet +rich pyserial numpydoc sphinxcontrib-napoleon diff --git a/setup.py b/setup.py index aa4f5a3..736d2aa 100644 --- a/setup.py +++ b/setup.py @@ -69,6 +69,7 @@ "uptime", "chardet", "traitlets", + "rich", ] class AsamKeyDllAutogen(setuptools.Command): From bf36b282e0dcf57c2b33ac119470dab09dbc42a0 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 8 Oct 2023 11:19:17 +0200 Subject: [PATCH 15/99] today() --- pyxcp/cpp_ext/__init__.py | 3 + pyxcp/cpp_ext/bin.hpp | 81 ++++ pyxcp/cpp_ext/daqlist.hpp | 101 +++++ pyxcp/cpp_ext/extension_wrapper.cpp | 69 +++ pyxcp/cpp_ext/mcobject.hpp | 139 ++++++ pyxcp/daq_stim/__init__.py | 157 +++++++ pyxcp/daq_stim/optimize/__init__.py | 68 +++ pyxcp/daq_stim/optimize/binpacking.py | 43 ++ pyxcp/examples/xcp_policy.py | 6 +- pyxcp/master/errorhandler.py | 7 +- pyxcp/recorder/__init__.py | 6 +- pyxcp/recorder/reader.hpp | 121 +++++ pyxcp/recorder/rekorder.hpp | 625 ++++++-------------------- pyxcp/recorder/setup.py | 2 +- pyxcp/recorder/unfolder.hpp | 362 +++++++++++++++ pyxcp/recorder/wrap.cpp | 25 +- pyxcp/recorder/writer.hpp | 218 +++++++++ pyxcp/tests/test_binpacking.py | 186 ++++++++ pyxcp/tests/test_daq.py | 194 ++++++++ 19 files changed, 1907 insertions(+), 506 deletions(-) create mode 100644 pyxcp/cpp_ext/__init__.py create mode 100644 pyxcp/cpp_ext/bin.hpp create mode 100644 pyxcp/cpp_ext/daqlist.hpp create mode 100644 pyxcp/cpp_ext/extension_wrapper.cpp create mode 100644 pyxcp/cpp_ext/mcobject.hpp create mode 100644 pyxcp/daq_stim/__init__.py create mode 100644 pyxcp/daq_stim/optimize/__init__.py create mode 100644 pyxcp/daq_stim/optimize/binpacking.py create mode 100644 pyxcp/recorder/reader.hpp create mode 100644 pyxcp/recorder/unfolder.hpp create mode 100644 pyxcp/recorder/writer.hpp create mode 100644 pyxcp/tests/test_binpacking.py create mode 100644 pyxcp/tests/test_daq.py diff --git a/pyxcp/cpp_ext/__init__.py b/pyxcp/cpp_ext/__init__.py new file mode 100644 index 0000000..b49713c --- /dev/null +++ b/pyxcp/cpp_ext/__init__.py @@ -0,0 +1,3 @@ +from .cpp_ext import Bin +from .cpp_ext import DaqList +from .cpp_ext import McObject diff --git a/pyxcp/cpp_ext/bin.hpp b/pyxcp/cpp_ext/bin.hpp new file mode 100644 index 0000000..36052d5 --- /dev/null +++ b/pyxcp/cpp_ext/bin.hpp @@ -0,0 +1,81 @@ + +#if !defined(__BIN_HPP) + #define __BIN_HPP + + #include + #include + #include + #include + #include + #include + + #include "mcobject.hpp" + +class Bin { + public: + + Bin(std::uint16_t size) : m_size(size), m_residual_capacity(size) { + } + + void append(const McObject& bin) { + m_entries.emplace_back(bin); + } + + std::uint16_t get_size() const { + return m_size; + } + + void set_size(const std::uint16_t size) { + m_size = size; + } + + std::uint16_t get_residual_capacity() const { + return m_residual_capacity; + } + + void set_residual_capacity(const std::uint16_t residual_capacity) { + m_residual_capacity = residual_capacity; + } + + const std::vector& get_entries() const { + return m_entries; + } + + bool operator==(const Bin& other) const { + return (m_size == other.m_size) && (m_residual_capacity == other.m_residual_capacity) && (m_entries == other.m_entries); + } + + private: + + std::uint16_t m_size; + std::uint16_t m_residual_capacity; + std::vector m_entries{}; +}; + +std::string bin_entries_to_string(const std::vector& entries); + +std::string to_string(const Bin& obj) { + std::stringstream ss; + + ss << "Bin(residual_capacity=" << obj.get_residual_capacity() << ", entries=[" << bin_entries_to_string(obj.get_entries()) + << "])"; + return ss.str(); +} + +std::string bin_entries_to_string(const std::vector& entries) { + std::stringstream ss; + + for (const auto& entry : entries) { + ss << to_string(entry) << ", "; + } + return ss.str(); +} + + #if 0 + + @property + def __len__(self) -> int: + return len(self.entries) + #endif + +#endif // __BIN_HPP diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp new file mode 100644 index 0000000..4f6d7bc --- /dev/null +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -0,0 +1,101 @@ + + +#if !defined(__DAQ_LIST_HPP) + #define __DAQ_LIST_HPP + + #include "bin.hpp" + #include "mcobject.hpp" + +class DaqList { + public: + + using daq_list_initialzer_t = std::tuple; + using flatten_odts_t = + std::vector>>; + + DaqList(std::uint16_t event_num, bool enable_timestamps, const std::vector& measurements) : + m_event_num(event_num), m_enable_timestamps(enable_timestamps) { + for (const auto& measurement : measurements) { + auto const& [name, address, ext, length, dt_name] = measurement; + // std::cout << "DL-obj: " << name << ", " << address << ", " << ext << ", " << length << ", " << dt_name << std::endl; + m_measurements.emplace_back(McObject(name, address, ext, length, dt_name)); + } + } + + bool get_enable_timestamps() const { + return m_enable_timestamps; + } + + bool get_event_num() const { + return m_event_num; + } + + const std::vector& get_measurements() const { + return m_measurements; + } + + const std::vector& get_measurements_opt() const { + return m_measurements_opt; + } + + const std::vector& get_header_names() const { + return m_header_names; + } + + std::uint16_t get_odt_count() const { + return m_odt_count; + } + + std::uint16_t get_total_entries() const { + return m_total_entries; + } + + std::uint16_t get_total_length() const { + return m_total_length; + } + + const flatten_odts_t& get_flatten_odts() const { + return m_flatten_odts; + } + + void set_measurements_opt(const std::vector& measurements_opt) { + m_measurements_opt = measurements_opt; + + auto odt_count = 0u; + auto total_entries = 0u; + auto total_length = 0u; + for (const auto& bin : measurements_opt) { + odt_count++; + std::vector> flatten_odt{}; + for (const auto& mc_obj : bin.get_entries()) { + for (const auto& component : mc_obj.get_components()) { + m_header_names.emplace_back(component.get_name()); + flatten_odt.emplace_back( + component.get_name(), component.get_address(), component.get_ext(), component.get_length(), + component.get_type_index() + ); + total_entries++; + total_length += component.get_length(); + } + } + m_flatten_odts.emplace_back(flatten_odt); + } + m_odt_count = odt_count; + m_total_entries = total_entries; + m_total_length = total_length; + } + + private: + + std::uint16_t m_event_num; + bool m_enable_timestamps; + std::vector m_measurements; + std::vector m_measurements_opt; + std::vector m_header_names; + std::uint16_t m_odt_count; + std::uint16_t m_total_entries; + std::uint16_t m_total_length; + flatten_odts_t m_flatten_odts; +}; + +#endif // __DAQ_LIST_HPP diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp new file mode 100644 index 0000000..5fb4939 --- /dev/null +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -0,0 +1,69 @@ + +#include +#include +#include +#include +#include + +#include + +#include "mcobject.hpp" +#include "bin.hpp" +#include "daqlist.hpp" + +namespace py = pybind11; +using namespace pybind11::literals; + +PYBIND11_MODULE(cpp_ext, m) { + m.doc() = "C++ extensions for pyXCP."; + + py::class_(m, "McObject") + .def(py::init&>() + ,"name"_a, "address"_a, "ext"_a, "length"_a, "data_type"_a="", "components"_a=std::vector() + ) + .def_property("name", &McObject::get_name, &McObject::set_name) + .def_property("address", &McObject::get_address, &McObject::set_address) + .def_property("ext", &McObject::get_ext, &McObject::set_ext) + .def_property("length", &McObject::get_length, &McObject::set_length) + .def_property("data_type", &McObject::get_data_type, &McObject::set_data_type) + + .def("add_component", &McObject::add_component, "component"_a) + .def("__repr__", [](const McObject& self) { + return to_string(self); + }) + ; + + py::class_(m, "Bin") + .def(py::init(), "size"_a) + .def_property("size", &Bin::get_size, &Bin::set_size) + .def_property("residual_capacity", &Bin::get_residual_capacity, &Bin::set_residual_capacity) + .def_property("entries", &Bin::get_entries, nullptr) + .def("append", &Bin::append) + + .def("__repr__", [](const Bin& self) { + return to_string(self); + }) + + .def("__eq__", [](const Bin& self, const Bin& other) { + return self == other; + }) + + .def("__len__", [](const Bin& self) { + return std::size(self.get_entries()); + }) + ; + + py::class_(m, "DaqList") + .def(py::init&>(), + "event_num"_a, "enable_timestamps"_a, "measurements"_a) + .def_property("event_num", &DaqList::get_event_num, nullptr) + .def_property("enable_timestamps", &DaqList::get_enable_timestamps, nullptr) + .def_property("measurements", &DaqList::get_measurements, nullptr) + .def_property("measurements_opt", &DaqList::get_measurements_opt, &DaqList::set_measurements_opt) + .def_property("header_names", &DaqList::get_header_names, nullptr) + .def_property("odt_count", &DaqList::get_odt_count, nullptr) + .def_property("total_entries", &DaqList::get_total_entries, nullptr) + .def_property("total_length", &DaqList::get_total_length, nullptr) + ; + +} diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp new file mode 100644 index 0000000..620bf1e --- /dev/null +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -0,0 +1,139 @@ + +#if !defined(__MC_OBJECT_HPP) + #define __MC_OBJECT_HPP + + #include + #include + #include + #include + #include + +const std::map> TYPE_MAP = { + {"U8", { 0, 1 }}, + { "I8", { 1, 1 }}, + { "U16", { 2, 2 }}, + { "I16", { 3, 2 }}, + { "U32", { 4, 4 }}, + { "I32", { 5, 4 }}, + { "U64", { 6, 8 }}, + { "I64", { 7, 8 }}, + { "F32", { 8, 4 }}, + { "F64", { 9, 8 }}, +}; + +class McObject { + public: + + explicit McObject( + std::string_view name, std::uint32_t address, std::uint8_t ext, std::uint16_t length, const std::string& data_type, + const std::vector& components = std::vector() + ) : + m_name(name), + m_address(address), + m_ext(ext), + m_length(length), + m_data_type(data_type), + m_components(components), + m_type_index(-1) { + if (data_type != "") { + const auto [ti, len] = TYPE_MAP.at(data_type); + m_type_index = ti; + m_length = len; + } + } + + McObject(const McObject& obj) = default; + McObject(McObject&& obj) = default; + McObject& operator=(const McObject&) = default; + McObject& operator=(McObject&&) = default; + + const std::string& get_name() const { + return m_name; + } + + void set_name(std::string_view name) { + m_name = name; + } + + std::uint32_t get_address() const { + return m_address; + } + + void set_address(std::uint32_t address) { + m_address = address; + } + + std::uint8_t get_ext() const { + return m_ext; + } + + void set_ext(std::uint8_t ext) { + m_ext = ext; + } + + const std::string& get_data_type() const { + return m_data_type; + } + + void set_data_type(const std::string& value) { + m_data_type = value; + } + + std::uint16_t get_length() const { + return m_length; + } + + void set_length(std::uint16_t length) { + m_length = length; + } + + std::int32_t get_type_index() const { + return m_type_index; + } + + const std::vector& get_components() const { + return m_components; + } + + void add_component(const McObject& obj) { + m_components.emplace_back(obj); + } + + bool operator==(const McObject& other) const { + return (m_name == other.m_name) && (m_address == other.m_address) && (m_ext == other.m_ext) && + (m_length == other.m_length) && (m_data_type == other.m_data_type) && + (std::equal(m_components.begin(), m_components.end(), other.m_components.begin(), other.m_components.end())); + } + + private: + + std::string m_name; + std::uint32_t m_address; + std::uint8_t m_ext; + std::uint16_t m_length; + std::string m_data_type; + std::int16_t m_type_index; + std::vector m_components{}; +}; + +std::string mc_components_to_string(const std::vector& components); + +std::string to_string(const McObject& obj) { + std::stringstream ss; + + ss << "McObject(name='" << obj.get_name() << "', address=" << obj.get_address() + << ", ext=" << static_cast(obj.get_ext()) << ", data_type='" << obj.get_data_type() + << "', length=" << obj.get_length() << ", components=[" << mc_components_to_string(obj.get_components()) << "])"; + return ss.str(); +} + +std::string mc_components_to_string(const std::vector& components) { + std::stringstream ss; + + for (const auto& obj : components) { + ss << to_string(obj) << ", "; + } + return ss.str(); +} + +#endif // __MC_OBJECT_HPP diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py new file mode 100644 index 0000000..86855a8 --- /dev/null +++ b/pyxcp/daq_stim/__init__.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import struct +from collections import defaultdict +from dataclasses import dataclass +from dataclasses import field +from pprint import pprint +from typing import Any +from typing import Callable +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple + +from pyxcp import types +from pyxcp.cpp_ext import DaqList +from pyxcp.daq_stim.optimize import make_continuous_blocks +from pyxcp.daq_stim.optimize import McObject +from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing +from pyxcp.recorder import UnfoldingParameters +from pyxcp.recorder import XcpLogFileReader +from pyxcp.recorder import XcpLogFileUnfolder +from pyxcp.types import FrameCategory + + +DAQ_ID_FIELD_SIZE = { + "IDF_ABS_ODT_NUMBER": 1, + "IDF_REL_ODT_NUMBER_ABS_DAQ_LIST_NUMBER_BYTE": 2, + "IDF_REL_ODT_NUMBER_ABS_DAQ_LIST_NUMBER_WORD": 3, + "IDF_REL_ODT_NUMBER_ABS_DAQ_LIST_NUMBER_WORD_ALIGNED": 4, +} + +DAQ_TIMESTAMP_SIZE = { + "S1": 1, + "S2": 2, + "S4": 4, +} + + +class Daq: + def __init__(self, file_name: str, callback: Optional[Callable[[int, Tuple], None]] = None): + self.callback = callback + self.file_name = file_name + + def set_master(self, xcp_master): + self.xcp_master = xcp_master + + def add_daq_lists(self, daq_lists: List[DaqList]): + self.daq_lists = daq_lists + + def setup(self, write_multiple: bool = True): + self.daq_info = self.xcp_master.getDaqInfo() + pprint(self.daq_info) + try: + processor = self.daq_info.get("processor") + properties = processor.get("properties") + resolution = self.daq_info.get("resolution") + if properties["configType"] == "STATIC": + raise TypeError("DAQ configuration is static, cannot proceed.") + self.supports_timestampes = properties["timestampSupported"] + self.supports_prescaler = properties["prescalerSupported"] + if self.supports_timestampes: + mode = resolution.get("timestampMode") + self.ts_fixed = mode.get("fixed") + self.ts_size = DAQ_TIMESTAMP_SIZE[mode.get("size")] + ts_unit_exp = types.DAQ_TIMESTAMP_UNIT_TO_EXP[mode.get("unit")] + ts_ticks = resolution.get("timestampTicks") + self.ts_scale_factor = (10**ts_unit_exp) * ts_ticks + else: + self.ts_size = 0 + self.ts_fixed = False + key_byte = processor.get("keyByte") + header_len = DAQ_ID_FIELD_SIZE[key_byte["identificationField"]] + max_dto = self.xcp_master.slaveProperties.maxDto + max_odt_entry_size = resolution.get("maxOdtEntrySizeDaq") + max_payload_size = min(max_odt_entry_size, max_dto - header_len) + self.min_daq = processor.get("minDaq") + except Exception as e: + raise TypeError(f"DAQ_INFO corrupted: {e}") from e + + # DAQ optimization. + for daq_list in self.daq_lists: + ttt = make_continuous_blocks(daq_list.measurements, max_payload_size) + daq_list.measurements_opt = first_fit_decreasing(ttt, max_payload_size) + + byte_order = 0 if self.xcp_master.slaveProperties.byteOrder == "INTEL" else 1 + self.uf = UnfoldingParameters(byte_order, header_len, self.ts_scale_factor, False, self.ts_size, self.daq_lists) + + self.first_pids = [] + daq_count = len(self.daq_lists) + self.xcp_master.freeDaq() + # Allocate + self.xcp_master.allocDaq(daq_count) + for i, daq_list in enumerate(self.daq_lists, self.min_daq): + measurements = daq_list.measurements_opt + odt_count = len(measurements) + self.xcp_master.allocOdt(i, odt_count) + for j, measurement in enumerate(measurements): + entry_count = len(measurement.entries) + self.xcp_master.allocOdtEntry(i, j, entry_count) + # Write DAQs + for i, daq_list in enumerate(self.daq_lists, self.min_daq): + # self.xcp_master.setDaqListMode(daqListNumber=i, mode=0x10, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xff) + measurements = daq_list.measurements_opt + for j, measurement in enumerate(measurements): + self.xcp_master.setDaqPtr(i, j, 0) + for entry in measurement.entries: + self.xcp_master.writeDaq(0xFF, entry.length, entry.ext, entry.address) + + def start(self): + for i, daq_list in enumerate(self.daq_lists, self.min_daq): + mode = 0x10 if daq_list.enable_timestamps else 0x00 + self.xcp_master.setDaqListMode( + daqListNumber=i, mode=mode, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xFF # TODO: + MIN_DAQ + ) + res = self.xcp_master.startStopDaqList(0x02, i) + self.first_pids.append(res.firstPid) + self.xcp_master.startStopSynch(0x01) + + def stop(self): + self.xcp_master.startStopSynch(0x00) + + def reader(self): + unfolder = XcpLogFileUnfolder(self.file_name, self.uf) + unfolder.start(self.first_pids) + + for block in unfolder.next_block(): + print(block) + + +class Collector: + def __init__(self, daq_num: int, num_odts: int, unfolder, callback): + self.daq_num = daq_num + self.num_odts = num_odts + self.current_odt_num = 0 + self.frames = [None] * num_odts + self.unfolder = unfolder + self.callback = callback + + def add(self, odt_num, frame): + if odt_num != self.current_odt_num: + print(f"WRONG SEQ-NO {odt_num} expected {self.current_odt_num} [LIST: {self.daq_num}]") + self.current_odt_num = odt_num + self.frames[self.current_odt_num] = frame + self.current_odt_num += 1 + if self.current_odt_num == self.num_odts: + result = {} + for idx, frame in enumerate(self.frames): + offset = 0 + for name, length in self.unfolder[idx]: + data = frame[offset : offset + length] + result[name] = bytes(data) + offset += length + if self.callback is not None: + self.callback(0, result) + # print("DAQ", self.daq_num, result) + self.current_odt_num %= self.num_odts diff --git a/pyxcp/daq_stim/optimize/__init__.py b/pyxcp/daq_stim/optimize/__init__.py new file mode 100644 index 0000000..e5d8b96 --- /dev/null +++ b/pyxcp/daq_stim/optimize/__init__.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Optimize data-structures like memory sections.""" +from collections import defaultdict +from dataclasses import dataclass +from dataclasses import field +from itertools import groupby +from operator import attrgetter +from typing import Any +from typing import Dict +from typing import List +from typing import Tuple +from typing import Union + +from pyxcp.cpp_ext import McObject + + +def make_continuous_blocks(chunks: List[McObject], upper_bound=None, upper_bound_initial=None) -> List[McObject]: + """Try to make continous blocks from a list of small, unordered `chunks`. + + Parameters + ---------- + chunks: list of `McObject` + + Returns + ------- + sorted list of `McObject` + """ + + def key_func(x): + return (x.ext, x.address) + + values = [] + # 1. Groupy by address. + for _key, value in groupby(sorted(chunks, key=key_func), key=key_func): + # 2. Pick the largest one. + values.append(max(value, key=attrgetter("length"))) + result_sections = [] + last_section = None + last_ext = None + while values: + section = values.pop(0) + if (last_section and section.address <= last_section.address + last_section.length) and not (section.ext != last_ext): + last_end = last_section.address + last_section.length - 1 + current_end = section.address + section.length - 1 + if last_end > section.address: + pass + else: + offset = current_end - last_end + if upper_bound: + if last_section.length + offset <= upper_bound: + last_section.length += offset + last_section.add_component(section) + else: + result_sections.append( + McObject(name="", address=section.address, ext=section.ext, length=section.length, components=[section]) + ) + else: + last_section.length += offset + last_section.add_component(section) + else: + # Create a new section. + result_sections.append( + McObject(name="", address=section.address, ext=section.ext, length=section.length, components=[section]) + ) + last_section = result_sections[-1] + last_ext = last_section.ext + return result_sections diff --git a/pyxcp/daq_stim/optimize/binpacking.py b/pyxcp/daq_stim/optimize/binpacking.py new file mode 100644 index 0000000..9bed51c --- /dev/null +++ b/pyxcp/daq_stim/optimize/binpacking.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Bin-packing algorithms. +""" +from typing import List +from typing import Optional + +from pyxcp.cpp_ext import Bin + + +def first_fit_decreasing(items, bin_size: int, initial_bin_size: Optional[int] = None) -> List[Bin]: + """bin-packing with first-fit-decreasing algorithm. + + Parameters + ---------- + items: list + items that need to be stored/allocated. + + bin_size: int + + Returns + ------- + list + Resulting bins + """ + if initial_bin_size is None: + initial_bin_size = bin_size + # bin_size = max(bin_size, initial_bin_size) + bins = [Bin(size=initial_bin_size)] # Initial bin + for item in sorted(items, key=lambda x: x.length, reverse=True): + if item.length > bin_size: + raise ValueError(f"Item '{item}' is too large to fit in a {bin_size} byte sized bin.") + for bin in bins: + if bin.residual_capacity >= item.length: + bin.append(item) + bin.residual_capacity -= item.length + break + else: + new_bin = Bin(size=bin_size) + bins.append(new_bin) + new_bin.append(item) + new_bin.residual_capacity -= item.length + return bins diff --git a/pyxcp/examples/xcp_policy.py b/pyxcp/examples/xcp_policy.py index f1ac96e..85d1f04 100644 --- a/pyxcp/examples/xcp_policy.py +++ b/pyxcp/examples/xcp_policy.py @@ -1,18 +1,18 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -"""Demostrates how to use frame recording policies. +"""Demostrates how to use frame recording policies including recorder extension. """ from pprint import pprint from pyxcp.cmdline import ArgumentParser -from pyxcp.transport.base import FrameRecorderAcquisitionPolicy +from pyxcp.transport.base import FrameRecorderPolicy from pyxcp.transport.base import StdoutPolicy ap = ArgumentParser(description="pyXCP frame recording policy example.") LOG_FILE = "pyxcp" -policy = FrameRecorderAcquisitionPolicy(LOG_FILE) +policy = FrameRecorderPolicy(LOG_FILE) use_recorder = True # policy = StdoutPolicy() # You may also try this one. diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index 5eda711..fd7d994 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -356,12 +356,15 @@ def __call__(self, inst, func, arguments): raise else: self.error_code = None - # print("\t\t\t*** SUCCESS ***") self.handlerStack.pop() if self.handlerStack.empty(): - # print("OK, all handlers passed: '{}'.".format(res)) return res + if self.error_code == XcpError.ERR_CMD_SYNCH: + # Don't care about SYNCH for now... + self.inst.logger.info("SYNCH received.") + continue + if self.error_code is not None: preActions, actions, repeater = handler.actions(*getActions(inst.service, self.error_code)) if handler.repeater is None: diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 81473ba..2766d68 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -15,7 +15,11 @@ else: HAS_PANDAS = True -import rekorder as rec +import pyxcp.recorder.rekorder as rec + + +UnfoldingParameters = rec._UnfoldingParameters +XcpLogFileUnfolder = rec._XcpLogFileUnfolder @dataclass diff --git a/pyxcp/recorder/reader.hpp b/pyxcp/recorder/reader.hpp new file mode 100644 index 0000000..ba6efd8 --- /dev/null +++ b/pyxcp/recorder/reader.hpp @@ -0,0 +1,121 @@ + +#ifndef RECORDER_READER_HPP +#define RECORDER_READER_HPP + +class XcpLogFileReader { + public: + + explicit XcpLogFileReader(const std::string &file_name) { + if (!file_name.ends_with(detail::FILE_EXTENSION)) { + m_file_name = file_name + detail::FILE_EXTENSION; + } else { + m_file_name = file_name; + } + + m_mmap = new mio::mmap_source(m_file_name); + blob_t magic[detail::MAGIC_SIZE + 1]; + + read_bytes(0UL, detail::MAGIC_SIZE, magic); + if (memcmp(detail::MAGIC.c_str(), magic, detail::MAGIC_SIZE) != 0) { + throw std::runtime_error("Invalid file magic."); + } + m_offset = detail::MAGIC_SIZE; + + read_bytes(m_offset, detail::FILE_HEADER_SIZE, reinterpret_cast(&m_header)); + // printf("Sizes: %u %u %.3f\n", m_header.size_uncompressed, + // m_header.size_compressed, + // float(m_header.size_uncompressed) / float(m_header.size_compressed)); + if (m_header.hdr_size != detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE) { + throw std::runtime_error("File header size does not match."); + } + if (detail::VERSION != m_header.version) { + throw std::runtime_error("File version mismatch."); + } + + if (m_header.num_containers < 1) { + throw std::runtime_error("At least one container required."); + } + + m_offset += detail::FILE_HEADER_SIZE; + } + + [[nodiscard]] FileHeaderType get_header() const noexcept { + return m_header; + } + + [[nodiscard]] auto get_header_as_tuple() const noexcept -> HeaderTuple { + auto hdr = get_header(); + + return std::make_tuple( + hdr.num_containers, hdr.record_count, hdr.size_uncompressed, hdr.size_compressed, + (double)((std::uint64_t)(((double)hdr.size_uncompressed / (double)hdr.size_compressed * 100.0) + 0.5)) / 100.0 + ); + } + + void reset() noexcept { + m_current_container = 0; + m_offset = file_header_size(); + } + + std::optional next_block() { + auto container = ContainerHeaderType{}; + auto frame = frame_header_t{}; + std::uint32_t boffs = 0; + auto result = FrameVector{}; + payload_t payload; + + if (m_current_container >= m_header.num_containers) { + return std::nullopt; + } + read_bytes(m_offset, detail::CONTAINER_SIZE, reinterpret_cast(&container)); + __ALIGN auto buffer = new blob_t[container.size_uncompressed]; + m_offset += detail::CONTAINER_SIZE; + result.reserve(container.record_count); + const int uc_size = ::LZ4_decompress_safe( + reinterpret_cast(ptr(m_offset)), reinterpret_cast(buffer), container.size_compressed, + container.size_uncompressed + ); + if (uc_size < 0) { + throw std::runtime_error("LZ4 decompression failed."); + } + boffs = 0; + for (std::uint32_t idx = 0; idx < container.record_count; ++idx) { + _fcopy(reinterpret_cast(&frame), reinterpret_cast(&(buffer[boffs])), sizeof(frame_header_t)); + boffs += sizeof(frame_header_t); + result.emplace_back( + frame.category, frame.counter, frame.timestamp, frame.length, create_payload(frame.length, &buffer[boffs]) + ); + boffs += frame.length; + } + m_offset += container.size_compressed; + m_current_container += 1; + delete[] buffer; + + return std::optional{ result }; + } + + ~XcpLogFileReader() noexcept { + delete m_mmap; + } + + protected: + + [[nodiscard]] blob_t const *ptr(std::uint32_t pos = 0) const { + return reinterpret_cast(m_mmap->data() + pos); + } + + void read_bytes(std::uint32_t pos, std::uint32_t count, blob_t *buf) const { + auto addr = reinterpret_cast(ptr(pos)); + _fcopy(reinterpret_cast(buf), addr, count); + } + + private: + + std::string m_file_name; + std::uint32_t m_offset{ 0 }; + std::uint32_t m_current_container{ 0 }; + mio::mmap_source *m_mmap{ nullptr }; + FileHeaderType m_header; +}; + +#endif // RECORDER_READER_HPP diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 662eb3b..15ba649 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -1,84 +1,76 @@ #if !defined(__REKORDER_HPP) -#define __REKORDER_HPP - -#if !defined(STANDALONE_REKORDER) - #define STANDALONE_REKORDER 0 -#endif /* STANDALONE_REKORDER */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - - -#if defined(_WIN32) - #include - #include - - #include -#endif /* _WIN32 */ - - -#include "lz4.h" -#include "mio.hpp" - -#if STANDALONE_REKORDER == 0 - #include - #include - #include - - namespace py = pybind11; - using namespace pybind11::literals; -#endif /* STANDALONE_REKORDER */ - -#if !defined(__BIGGEST_ALIGNMENT__) - #define __BIGGEST_ALIGNMENT__ (8) -#endif - - -#define __ALIGNMENT_REQUIREMENT __BIGGEST_ALIGNMENT__ -#define __ALIGN alignas(__ALIGNMENT_REQUIREMENT) + #define __REKORDER_HPP + + #if !defined(STANDALONE_REKORDER) + #define STANDALONE_REKORDER 0 + #endif /* STANDALONE_REKORDER */ + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #if defined(_WIN32) + #include + #include + #include + #endif /* _WIN32 */ + + #include "lz4.h" + #include "mio.hpp" + + #if STANDALONE_REKORDER == 0 + #include + #include + #include + +namespace py = pybind11; +using namespace pybind11::literals; + #endif /* STANDALONE_REKORDER */ + + #if !defined(__BIGGEST_ALIGNMENT__) + #define __BIGGEST_ALIGNMENT__ (8) + #endif + #define __ALIGNMENT_REQUIREMENT __BIGGEST_ALIGNMENT__ + #define __ALIGN alignas(__ALIGNMENT_REQUIREMENT) -constexpr auto kilobytes(std::uint32_t value) -> std::uint32_t -{ +constexpr auto kilobytes(std::uint32_t value) -> std::uint32_t { return value * 1024; } -constexpr auto megabytes(std::uint32_t value) -> std::uint32_t -{ +constexpr auto megabytes(std::uint32_t value) -> std::uint32_t { return kilobytes(value) * 1024; } constexpr uint16_t XCP_PAYLOAD_MAX = 0xFFFF; -/* -byte-order is, where applicable little ending (LSB first). -*/ -#pragma pack(push) -#pragma pack(1) -struct FileHeaderType -{ + /* + byte-order is, where applicable little ending (LSB first). + */ + #pragma pack(push) + #pragma pack(1) + +struct FileHeaderType { uint16_t hdr_size; uint16_t version; uint16_t options; @@ -92,8 +84,7 @@ using HeaderTuple = std::tuple; -#else - using payload_t = py::array_t; -#endif /* STANDALONE_REKORDER */ - - -struct frame_header_t -{ - uint8_t category {0}; - uint16_t counter {0}; - double timestamp {0.0}; - uint16_t length {0}; + #if STANDALONE_REKORDER == 1 +using payload_t = std::shared_ptr; + #else +using payload_t = py::array_t; + #endif /* STANDALONE_REKORDER */ + +struct frame_header_t { + uint8_t category{ 0 }; + uint16_t counter{ 0 }; + double timestamp{ 0.0 }; + uint16_t length{ 0 }; }; -#pragma pack(pop) -using FrameTuple = std::tuple; -using FrameVector = std::vector; -using FrameTupleWriter = std::tuple; + #pragma pack(pop) + +using FrameTuple = std::tuple; +using FrameVector = std::vector; +using FrameTupleWriter = std::tuple; enum class FrameCategory : std::uint8_t { META, @@ -132,16 +122,14 @@ enum class FrameCategory : std::uint8_t { STIM, }; -namespace detail -{ - const std::string FILE_EXTENSION(".xmraw"); - const std::string MAGIC{"ASAMINT::XCP_RAW"}; - constexpr auto MAGIC_SIZE = 16; - constexpr auto VERSION = 0x0100; - constexpr auto FILE_HEADER_SIZE = sizeof(FileHeaderType); - constexpr auto CONTAINER_SIZE = sizeof(ContainerHeaderType); -} - +namespace detail { +const std::string FILE_EXTENSION(".xmraw"); +const std::string MAGIC{ "ASAMINT::XCP_RAW" }; +constexpr auto MAGIC_SIZE = 16; +constexpr auto VERSION = 0x0100; +constexpr auto FILE_HEADER_SIZE = sizeof(FileHeaderType); +constexpr auto CONTAINER_SIZE = sizeof(ContainerHeaderType); +} // namespace detail constexpr auto file_header_size() -> std::uint32_t { return (detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE); @@ -149,56 +137,53 @@ constexpr auto file_header_size() -> std::uint32_t { using rounding_func_t = std::function; -inline rounding_func_t create_rounding_func(std::uint32_t multiple) { +inline rounding_func_t create_rounding_func(std::uint32_t multiple) { return [multiple](std::uint32_t value) { - return (value + (multiple - 1)) & ~(multiple -1 ); + return (value + (multiple - 1)) & ~(multiple - 1); }; } const auto round_to_alignment = create_rounding_func(__ALIGNMENT_REQUIREMENT); - -inline void _fcopy(char * dest, char const * src, std::uint32_t n) noexcept -{ +inline void _fcopy(char* dest, char const * src, std::uint32_t n) noexcept { for (std::uint32_t i = 0; i < n; ++i) { dest[i] = src[i]; } } -#if STANDALONE_REKORDER == 1 - inline blob_t * get_payload_ptr(const payload_t& payload) noexcept { - return payload.get(); - } + #if STANDALONE_REKORDER == 1 +inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { + return payload.get(); +} - inline payload_t create_payload(std::uint32_t size, blob_t const * data) noexcept { - auto pl = std::make_shared(size); - _fcopy(reinterpret_cast(pl.get()), reinterpret_cast(data), size); - return pl; - } -#else - inline payload_t create_payload(std::uint32_t size, blob_t const * data) { - return py::array_t(size, data); - } +inline payload_t create_payload(std::uint32_t size, blob_t const * data) noexcept { + auto pl = std::make_shared(size); + _fcopy(reinterpret_cast(pl.get()), reinterpret_cast(data), size); + return pl; +} + #else +inline payload_t create_payload(std::uint32_t size, blob_t const * data) { + return py::array_t(size, data); +} - inline blob_t * get_payload_ptr(const payload_t& payload) noexcept { - py::buffer_info buf = payload.request(); +inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { + py::buffer_info buf = payload.request(); - return static_cast(buf.ptr); - } -#endif /* STANDALONE_REKORDER */ + return static_cast(buf.ptr); +} + #endif /* STANDALONE_REKORDER */ inline void hexdump(blob_t const * buf, std::uint16_t sz) { - for (std::uint16_t idx = 0; idx < sz; ++idx) - { + for (std::uint16_t idx = 0; idx < sz; ++idx) { printf("%02X ", buf[idx]); } printf("\n\r"); } - -template +template class TsQueue { -public: + public: + TsQueue() = default; TsQueue(const TsQueue& other) noexcept { @@ -214,7 +199,7 @@ class TsQueue { std::shared_ptr get() noexcept { std::unique_lock lock(m_mtx); - m_cond.wait(lock, [this]{return !m_queue.empty();}); + m_cond.wait(lock, [this] { return !m_queue.empty(); }); std::shared_ptr result(std::make_shared(m_queue.front())); m_queue.pop(); return result; @@ -225,15 +210,15 @@ class TsQueue { return m_queue.empty(); } -private: - mutable std::mutex m_mtx; - std::queue m_queue; + private: + + mutable std::mutex m_mtx; + std::queue m_queue; std::condition_variable m_cond; }; - class Event { -public: + public: Event(const Event& other) noexcept { std::scoped_lock lock(other.m_mtx); @@ -241,7 +226,7 @@ class Event { } ~Event() = default; - Event() = default; + Event() = default; void signal() noexcept { std::scoped_lock lock(m_mtx); @@ -251,7 +236,7 @@ class Event { void wait() noexcept { std::unique_lock lock(m_mtx); - m_cond.wait(lock, [this]{return m_flag;}); + m_cond.wait(lock, [this] { return m_flag; }); m_flag = false; } @@ -260,26 +245,25 @@ class Event { return m_flag; } -private: - mutable std::mutex m_mtx {}; - bool m_flag {false}; - std::condition_variable m_cond {}; -}; + private: + mutable std::mutex m_mtx{}; + bool m_flag{ false }; + std::condition_variable m_cond{}; +}; /* * * Super simplicistic block memory manager. * */ -template +template class BlockMemory { - -public: + public: using mem_block_t = std::array; - explicit BlockMemory() noexcept : m_memory{nullptr}, m_allocation_count{0} { + explicit BlockMemory() noexcept : m_memory{ nullptr }, m_allocation_count{ 0 } { m_memory = new T[_IS * _NB]; } @@ -288,15 +272,16 @@ class BlockMemory { delete[] m_memory; } } + BlockMemory(const BlockMemory&) = delete; - T * acquire() noexcept { + T* acquire() noexcept { const std::scoped_lock lock(m_mtx); if (m_allocation_count >= _NB) { return nullptr; } - T * ptr = reinterpret_cast(m_memory + (m_allocation_count * _IS)); + T* ptr = reinterpret_cast(m_memory + (m_allocation_count * _IS)); m_allocation_count++; return ptr; } @@ -309,353 +294,15 @@ class BlockMemory { m_allocation_count--; } -private: + private: - T * m_memory; + T* m_memory; std::uint32_t m_allocation_count; - std::mutex m_mtx; - + std::mutex m_mtx; }; + #include "reader.hpp" + #include "unfolder.hpp" + #include "writer.hpp" -/** - */ -class XcpLogFileWriter -{ -public: - explicit XcpLogFileWriter(const std::string& file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1) noexcept - { - if (!file_name.ends_with(detail::FILE_EXTENSION)) { - m_file_name = file_name + detail::FILE_EXTENSION; - } else { - m_file_name = file_name; - } - -#if defined(_WIN32) - m_fd = CreateFileA( - m_file_name.c_str(), - GENERIC_READ | GENERIC_WRITE, - 0, - (LPSECURITY_ATTRIBUTES)nullptr, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, - nullptr - ); -#else - m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666); -#endif - truncate(megabytes(prealloc)); - m_mmap = new mio::mmap_sink(m_fd); - m_chunk_size = megabytes(chunk_size); - m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)]; - m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; - - start_thread(); - } - - ~XcpLogFileWriter() noexcept { - finalize(); - #ifdef __APPLE__ - if (collector_thread.joinable()) { - collector_thread.join(); - } - #endif - } - - void finalize() { - if (!m_finalized) { - m_finalized = true; - stop_thread(); - if (m_container_record_count) { - compress_frames(); - } - write_header(detail::VERSION, 0x0000, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed); - m_mmap->unmap(); - truncate(m_offset); -#if defined(_WIN32) - CloseHandle(m_fd); -#else - close(m_fd); -#endif - delete m_mmap; - delete[] m_intermediate_storage; - } - } - - void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const * data) noexcept { - auto payload= new char[length]; - //auto payload = mem.acquire(); - - _fcopy(payload, data, length); - my_queue.put( - std::make_tuple(category, counter, timestamp, length, payload) - ); - } - -protected: - void truncate(off_t size) const noexcept - { -#if defined(_WIN32) - if (SetFilePointer(m_fd, size, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { - // TODO: Errorhandling. - } - if (SetEndOfFile(m_fd) == 0) { - // TODO: Errorhandling. - } -#else - ftruncate(m_fd, size); -#endif - } - - blob_t * ptr(std::uint32_t pos = 0) const noexcept - { - return (blob_t *)(m_mmap->data() + pos); - } - - template - void store_im(T const * data, std::uint32_t length) noexcept { - _fcopy(reinterpret_cast(m_intermediate_storage + m_intermediate_storage_offset), reinterpret_cast(data), length); - m_intermediate_storage_offset += length; - } - - void compress_frames() { - auto container = ContainerHeaderType{}; - //printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); - const int cp_size = ::LZ4_compress_default( - reinterpret_cast(m_intermediate_storage), reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), - m_intermediate_storage_offset, LZ4_COMPRESSBOUND(m_intermediate_storage_offset) - ); - if (cp_size < 0) { - throw std::runtime_error("LZ4 compression failed."); - } - //printf("comp: %d %d [%f]\n", m_intermediate_storage_offset, cp_size, double(m_intermediate_storage_offset) / double(cp_size)); - container.record_count = m_container_record_count; - container.size_compressed = cp_size; - container.size_uncompressed = m_container_size_uncompressed; - _fcopy(reinterpret_cast(ptr(m_offset)), reinterpret_cast(&container), detail::CONTAINER_SIZE); - m_offset += (detail::CONTAINER_SIZE + cp_size); - m_total_size_uncompressed += m_container_size_uncompressed; - m_total_size_compressed += cp_size; - m_record_count += m_container_record_count; - m_container_size_uncompressed = 0; - m_container_size_compressed = 0; - m_container_record_count = 0; - m_intermediate_storage_offset = 0; - m_num_containers += 1; - } - - void write_bytes(std::uint32_t pos, std::uint32_t count, char const * buf) const noexcept - { - auto addr = reinterpret_cast(ptr(pos)); - - _fcopy(addr, buf, count); - } - - void write_header(uint16_t version, uint16_t options, uint32_t num_containers, - uint32_t record_count, uint32_t size_compressed, uint32_t size_uncompressed) noexcept { - auto header = FileHeaderType{}; - write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); - header.hdr_size = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; - header.version = version; - header.options = options; - header.num_containers = num_containers; - header.record_count = record_count; - header.size_compressed = size_compressed; - header.size_uncompressed = size_uncompressed; - write_bytes(0x00000000UL + detail::MAGIC_SIZE, detail::FILE_HEADER_SIZE, reinterpret_cast(&header)); - } - - bool start_thread() noexcept { - if (collector_thread.joinable()) { - return false; - } - stop_collector_thread_flag = false; - #ifdef __APPLE__ - collector_thread = std::thread([this]() { - #else - collector_thread = std::jthread([this]() { - #endif - while (!stop_collector_thread_flag) { - auto item = my_queue.get(); - const auto content = item.get(); - if (stop_collector_thread_flag == true) - { - break; - } - const auto [category, counter, timestamp, length, payload] = *content; - const frame_header_t frame{ category, counter, timestamp, length }; - store_im(&frame, sizeof(frame)); - store_im(payload, length); - delete[] payload; - m_container_record_count += 1; - m_container_size_uncompressed += (sizeof(frame) + length); - if (m_container_size_uncompressed > m_chunk_size) { - compress_frames(); - } - } - }); - return true; - } - - bool stop_thread() noexcept { - if (!collector_thread.joinable()) { - return false; - } - stop_collector_thread_flag = true; - my_queue.put(FrameTupleWriter{}); // Put something into the queue, otherwise the thread will hang forever. - collector_thread.join(); - return true; - } - -private: - std::string m_file_name; - std::uint32_t m_offset{0}; - std::uint32_t m_chunk_size{0}; - std::uint32_t m_num_containers{0}; - std::uint32_t m_record_count{0UL}; - std::uint32_t m_container_record_count{0UL}; - std::uint32_t m_total_size_uncompressed{0UL}; - std::uint32_t m_total_size_compressed{0UL}; - std::uint32_t m_container_size_uncompressed{0UL}; - std::uint32_t m_container_size_compressed{0UL}; - __ALIGN blob_t * m_intermediate_storage{nullptr}; - std::uint32_t m_intermediate_storage_offset{0}; - mio::file_handle_type m_fd{INVALID_HANDLE_VALUE}; - mio::mmap_sink * m_mmap{nullptr}; - bool m_finalized{false}; - #ifdef __APPLE__ - std::thread collector_thread{}; - #else - std::jthread collector_thread{}; - #endif - std::mutex mtx; - TsQueue my_queue; - BlockMemory mem{}; - std::atomic_bool stop_collector_thread_flag{false}; -}; - - -/** - */ -class XcpLogFileReader -{ -public: - explicit XcpLogFileReader(const std::string& file_name) - { - if (!file_name.ends_with(detail::FILE_EXTENSION)) { - m_file_name = file_name + detail::FILE_EXTENSION; - } else { - m_file_name = file_name; - } - - m_mmap = new mio::mmap_source(m_file_name); - blob_t magic[detail::MAGIC_SIZE + 1]; - - read_bytes(0UL, detail::MAGIC_SIZE, magic); - if (memcmp(detail::MAGIC.c_str(), magic, detail::MAGIC_SIZE) != 0) { - throw std::runtime_error("Invalid file magic."); - } - m_offset = detail::MAGIC_SIZE; - - read_bytes(m_offset, detail::FILE_HEADER_SIZE, reinterpret_cast(&m_header)); - //printf("Sizes: %u %u %.3f\n", m_header.size_uncompressed, - // m_header.size_compressed, - // float(m_header.size_uncompressed) / float(m_header.size_compressed)); - if (m_header.hdr_size != detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE) - { - throw std::runtime_error("File header size does not match."); - } - if (detail::VERSION != m_header.version) - { - throw std::runtime_error("File version mismatch."); - } - - if (m_header.num_containers < 1) { - throw std::runtime_error("At least one container required."); - } - - m_offset += detail::FILE_HEADER_SIZE; - } - - [[nodiscard]] - FileHeaderType get_header() const noexcept { - return m_header; - } - - [[nodiscard]] - auto get_header_as_tuple() const noexcept -> HeaderTuple { - auto hdr = get_header(); - - return std::make_tuple( - hdr.num_containers, - hdr.record_count, - hdr.size_uncompressed, - hdr.size_compressed, - (double)((std::uint64_t)(((double)hdr.size_uncompressed / (double)hdr.size_compressed * 100.0) + 0.5)) / 100.0 - ); - } - - void reset() noexcept { - m_current_container = 0; - m_offset = file_header_size(); - } - - std::optional next_block() { - auto container = ContainerHeaderType{}; - auto frame = frame_header_t{}; - std::uint32_t boffs = 0; - auto result = FrameVector{}; - payload_t payload; - - if (m_current_container >= m_header.num_containers) { - return std::nullopt; - } - read_bytes(m_offset, detail::CONTAINER_SIZE, reinterpret_cast(&container)); - __ALIGN auto buffer = new blob_t[container.size_uncompressed]; - m_offset += detail::CONTAINER_SIZE; - result.reserve(container.record_count); - const int uc_size = ::LZ4_decompress_safe(reinterpret_cast(ptr(m_offset)), reinterpret_cast(buffer), container.size_compressed, container.size_uncompressed); - if (uc_size < 0) { - throw std::runtime_error("LZ4 decompression failed."); - } - boffs = 0; - for (std::uint32_t idx = 0; idx < container.record_count; ++idx) { - _fcopy(reinterpret_cast(&frame), reinterpret_cast(&(buffer[boffs])), sizeof(frame_header_t)); - boffs += sizeof(frame_header_t); - result.emplace_back(frame.category, frame.counter, frame.timestamp, frame.length, create_payload(frame.length, &buffer[boffs])); - boffs += frame.length; - } - m_offset += container.size_compressed; - m_current_container += 1; - delete[] buffer; - - return std::optional{result}; - } - - ~XcpLogFileReader() noexcept - { - delete m_mmap; - } - -protected: - [[nodiscard]] - blob_t const *ptr(std::uint32_t pos = 0) const - { - return reinterpret_cast(m_mmap->data() + pos); - } - - void read_bytes(std::uint32_t pos, std::uint32_t count, blob_t * buf) const - { - auto addr = reinterpret_cast(ptr(pos)); - _fcopy(reinterpret_cast(buf), addr, count); - } - -private: - std::string m_file_name; - std::uint32_t m_offset{0}; - std::uint32_t m_current_container{0}; - mio::mmap_source * m_mmap{nullptr}; - FileHeaderType m_header; -}; - -#endif // __REKORDER_HPP +#endif // __REKORDER_HPP diff --git a/pyxcp/recorder/setup.py b/pyxcp/recorder/setup.py index 8dbc8c5..99b6916 100644 --- a/pyxcp/recorder/setup.py +++ b/pyxcp/recorder/setup.py @@ -23,7 +23,7 @@ Pybind11Extension( EXT_NAMES[0], include_dirs=[INCLUDE_DIRS], - sources=["lz4.cpp", "wrap.cpp"], + sources=["lz4.c", "wrap.cpp"], define_macros=[("EXTENSION_NAME", EXT_NAMES[0])], cxx_std=20, # Extension will use C++20 generators/coroutines. ), diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp new file mode 100644 index 0000000..03cd442 --- /dev/null +++ b/pyxcp/recorder/unfolder.hpp @@ -0,0 +1,362 @@ + +#ifndef RECORDER_UNFOLDER_HPP +#define RECORDER_UNFOLDER_HPP + +#include +#include + +#include "daqlist.hpp" +#include "mcobject.hpp" + +// NOTE: C++23 has std::byteswap() +constexpr auto _bswap(std::uint64_t v) noexcept { + return ((v & UINT64_C(0x0000'0000'0000'00FF)) << 56) | ((v & UINT64_C(0x0000'0000'0000'FF00)) << 40) | + ((v & UINT64_C(0x0000'0000'00FF'0000)) << 24) | ((v & UINT64_C(0x0000'0000'FF00'0000)) << 8) | + ((v & UINT64_C(0x0000'00FF'0000'0000)) >> 8) | ((v & UINT64_C(0x0000'FF00'0000'0000)) >> 24) | + ((v & UINT64_C(0x00FF'0000'0000'0000)) >> 40) | ((v & UINT64_C(0xFF00'0000'0000'0000)) >> 56); +} + +constexpr auto _bswap(std::uint32_t v) noexcept { + return ((v & UINT32_C(0x0000'00FF)) << 24) | ((v & UINT32_C(0x0000'FF00)) << 8) | ((v & UINT32_C(0x00FF'0000)) >> 8) | + ((v & UINT32_C(0xFF00'0000)) >> 24); +} + +constexpr auto _bswap(std::uint16_t v) noexcept { + return ((v & UINT16_C(0x00FF)) << 8) | ((v & UINT16_C(0xFF00)) >> 8); +} + +using measurement_value_t = std::variant; +using measurement_value_vector_t = std::vector; + +template +auto get_value(blob_t const * buf, std::uint32_t offset) -> Ty { + return *reinterpret_cast(&buf[offset]); +} + +template +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> Ty { + return _bswap(get_value(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> float { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> double { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> float { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> double { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> std::int16_t { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::int16_t { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> std::int32_t { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::int32_t { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> std::int64_t { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::int64_t { + return static_cast(get_value_swapped(buf, offset)); +} + +/* +** Get primitive datatypes, consider byte-order. +*/ +struct Getter { + Getter() = default; + + explicit Getter(bool swap, std::uint8_t id_size, std::uint8_t ts_size) : m_id_size(id_size), m_ts_size(ts_size) { + int8 = get_value; + uint8 = get_value; + + if (swap) { + int16 = get_value_swapped; + int32 = get_value_swapped; + int64 = get_value_swapped; + uint16 = get_value_swapped; + uint32 = get_value_swapped; + uint64 = get_value_swapped; + float_ = get_value_swapped; + double_ = get_value_swapped; + } else { + int16 = get_value; + int32 = get_value; + int64 = get_value; + uint16 = get_value; + uint32 = get_value; + uint64 = get_value; + float_ = get_value; + double_ = get_value; + } + // ts_size=0; + std::cout << "TS-SIZE: " << static_cast(ts_size) << " : " << static_cast(m_id_size) << std::endl; + } + + std::uint32_t get_timestamp(blob_t const * buf) { + switch (m_ts_size) { + case 0: + return 0; + case 1: + return uint8(buf, m_id_size); + case 2: + return uint16(buf, m_id_size); + case 4: + return uint32(buf, m_id_size); + default: + throw std::runtime_error("Unsupported timestamp size: " + std::to_string(m_ts_size)); + } + } + + measurement_value_t reader(std::uint16_t tp, blob_t const * buf, std::uint16_t offset) const { + switch (tp) { + case 0: + return static_cast(uint8(buf, offset)); + case 1: + return int8(buf, offset); + case 2: + return static_cast(uint16(buf, offset)); + case 3: + return int16(buf, offset); + case 4: + return static_cast(uint32(buf, offset)); + case 5: + return int32(buf, offset); + case 6: + return uint64(buf, offset); + case 7: + return int64(buf, offset); + case 8: + return float_(buf, offset); + case 9: + return double_(buf, offset); + default: + throw std::runtime_error("Unsupported data type: " + std::to_string(tp)); + } + } + + void set_first_pids(const std::vector& daq_lists, const std::vector& first_pids) { + m_first_pids = first_pids; + + if (m_id_size == 1) { + // In case of 1-byte ID field (absolute ODT number) we need a mapping. + + std::uint16_t daq_list_num = 0; + for (const auto& daq_list : daq_lists) { + auto first_pid = m_first_pids[daq_list_num]; + + for (std::uint16_t idx = first_pid; idx < daq_list.get_odt_count() + first_pid; ++idx) { + m_odt_to_daq_map[idx] = { daq_list_num, (idx - first_pid) }; + } + daq_list_num++; + } + } + } + + std::tuple get_id(blob_t const * buf) { + std::uint16_t odt_num = 0; + + switch (m_id_size) { + case 1: + odt_num = uint8(buf, 0); // Get 1-byte ODT number... + return m_odt_to_daq_map[odt_num]; // ...and return mapped values. + case 2: + return { uint8(buf, 1), uint8(buf, 0) }; + case 3: + return { uint16(buf, 1), uint8(buf, 0) }; + case 4: + return { uint16(buf, 2), uint8(buf, 0) }; + default: + throw std::runtime_error("Unsupported ID size: " + std::to_string(m_id_size)); + } + } + + std::uint8_t m_id_size; + std::uint8_t m_ts_size; + std::function int8; + std::function uint8; + std::function int16; + std::function int32; + std::function int64; + std::function uint16; + std::function uint32; + std::function uint64; + std::function float_; + std::function double_; + std::vector m_first_pids; + std::map> m_odt_to_daq_map; +}; + +struct UnfoldingParameters { + UnfoldingParameters() = delete; + + explicit UnfoldingParameters( + std::uint8_t byte_order, std::uint8_t id_field_size, double scale_factor, bool enable_timestamps, std::uint8_t ts_size, + const std::vector& daq_lists + ) : + m_byte_order(byte_order), + m_id_field_size(id_field_size), + m_scale_factor(scale_factor), + m_enable_timestamps(enable_timestamps), + m_ts_size(ts_size), + m_daq_lists(daq_lists) { + } + + std::uint8_t m_byte_order; // INTEL(LITTLE)=0, MOTOROLA(BIG)=1 + std::uint8_t m_id_field_size; + double m_scale_factor; + std::uint8_t m_ts_size; + bool m_enable_timestamps; + std::vector m_daq_lists; + // timestampSupported + // prescalerSupported + // timestampMode.fixed + // minDAQ +}; + +class MeasurementBuffer { + public: + + MeasurementBuffer(std::size_t num_elements) : m_buffer(num_elements) { + } + + private: + + std::vector m_buffer; + std::uint16_t m_current_odt = 0; +}; + +class XcpLogFileUnfolder { + public: + + explicit XcpLogFileUnfolder(const std::string& file_name, const UnfoldingParameters& params) : + m_reader(file_name), m_byte_order(std::endian::native), m_params(params) { + std::endian target_byte_order; + bool requires_swap; + + // std::vector<; + + if (m_params.m_byte_order == 0) { + target_byte_order = std::endian::little; + } else if (m_params.m_byte_order == 1) { + target_byte_order = std::endian::big; + } + if (target_byte_order != m_byte_order) { + requires_swap = true; + } else { + requires_swap = false; + } + + auto ts_size = m_params.m_ts_size; + std::cout << "ENA-TS: " << m_params.m_enable_timestamps << std::endl; + if (params.m_enable_timestamps) { + ts_size = 0; + } + + std::cout << "ID-size: " << static_cast(params.m_id_field_size) << std::endl; + m_getter = Getter(requires_swap, params.m_id_field_size, ts_size); + } + + void start(const std::vector& first_pids) { + m_getter.set_first_pids(m_params.m_daq_lists, first_pids); + } + + std::optional next_block() { + std::uint16_t offset = 0; + + while (true) { + const auto& block = m_reader.next_block(); + + if (!block) { + break; + } + + for (const auto& frame : block.value()) { + const auto& [category, counter, timestamp, frame_length, payload] = frame; + auto payload_data = payload.data(); + + if (category != static_cast(FrameCategory::DAQ)) { + continue; + } + //////////////////////////////// + offset = 0; + auto [daq_num, odt_num] = m_getter.get_id(payload_data); + offset += m_params.m_id_field_size; + std::cout << "CTR: " << counter << " ID: " << daq_num << ": " << odt_num << std::endl; + + if (odt_num == 0) { + auto ts = m_getter.get_timestamp(payload_data); + // auto ts = 0; + std::cout << "\tSTART DAQ-LIST: " << ts << std::endl; + if (m_params.m_enable_timestamps) { + offset += m_params.m_ts_size; + } + } + for (const auto& param : m_params.m_daq_lists[daq_num].get_flatten_odts()[odt_num]) { + const auto& [name, address, ext, size, type_index] = param; + + std::cout << "\t" << name << " " << offset << " : " << size << " ==> "; + + auto length = payload.size(); + if (offset >= length) { + throw std::runtime_error( + "Offset is out of range! " + std::to_string(offset) + " >= " + std::to_string(length) + ); + } + auto data = m_getter.reader(type_index, payload_data, offset); + + if (std::holds_alternative(data)) { + std::cout << std::get(data) << " " << std::endl; + } else if (std::holds_alternative(data)) { + std::cout << std::get(data) << "(+/-) " << std::endl; + } else if (std::holds_alternative(data)) { + std::cout << std::get(data) << " (double) " << std::endl; + } + offset += size; + } + //////////////////////////////// + } + return std::nullopt; + } + return std::nullopt; + } + + private: + + XcpLogFileReader m_reader; + std::endian m_byte_order; + UnfoldingParameters m_params; + Getter m_getter; + std::map m_first_pids; + // std::vector> m_measurement_buffers; +}; + +#endif // RECORDER_UNFOLDER_HPP diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 80999ea..44714d4 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -1,27 +1,32 @@ -#include - -#include #include +#include #include +#include + #include "rekorder.hpp" namespace py = pybind11; using namespace pybind11::literals; - PYBIND11_MODULE(rekorder, m) { m.doc() = "XCP raw frame recorder."; py::class_(m, "_PyXcpLogFileReader") .def(py::init()) .def("next_block", &XcpLogFileReader::next_block) .def("reset", &XcpLogFileReader::reset) - .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple) - ; + .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple); py::class_(m, "_PyXcpLogFileWriter") - .def(py::init()) - .def("finalize", &XcpLogFileWriter::finalize) - .def("add_frame", &XcpLogFileWriter::add_frame) - ; + .def(py::init()) + .def("finalize", &XcpLogFileWriter::finalize) + .def("add_frame", &XcpLogFileWriter::add_frame); + + py::class_(m, "_UnfoldingParameters") + .def(py::init&>()); + + py::class_(m, "_XcpLogFileUnfolder") + .def(py::init()) + .def("next_block", &XcpLogFileUnfolder::next_block) + .def("start", &XcpLogFileUnfolder::start); } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp new file mode 100644 index 0000000..d5080f5 --- /dev/null +++ b/pyxcp/recorder/writer.hpp @@ -0,0 +1,218 @@ + +#ifndef RECORDER_WRITER_HPP +#define RECORDER_WRITER_HPP + +class XcpLogFileWriter { + public: + + explicit XcpLogFileWriter(const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1) noexcept { + if (!file_name.ends_with(detail::FILE_EXTENSION)) { + m_file_name = file_name + detail::FILE_EXTENSION; + } else { + m_file_name = file_name; + } + +#if defined(_WIN32) + m_fd = CreateFileA( + m_file_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr + ); +#else + m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666); +#endif + truncate(megabytes(prealloc)); + m_mmap = new mio::mmap_sink(m_fd); + m_chunk_size = megabytes(chunk_size); + m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)]; + m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; + + start_thread(); + } + + ~XcpLogFileWriter() noexcept { + finalize(); +#ifdef __APPLE__ + if (collector_thread.joinable()) { + collector_thread.join(); + } +#endif + } + + void finalize() { + if (!m_finalized) { + m_finalized = true; + stop_thread(); + if (m_container_record_count) { + compress_frames(); + } + write_header( + detail::VERSION, 0x0000, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed + ); + m_mmap->unmap(); + truncate(m_offset); +#if defined(_WIN32) + CloseHandle(m_fd); +#else + close(m_fd); +#endif + delete m_mmap; + delete[] m_intermediate_storage; + } + } + + void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const *data) noexcept { + auto payload = new char[length]; + // auto payload = mem.acquire(); + + _fcopy(payload, data, length); + my_queue.put(std::make_tuple(category, counter, timestamp, length, payload)); + } + + protected: + + void truncate(off_t size) const noexcept { +#if defined(_WIN32) + if (SetFilePointer(m_fd, size, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + // TODO: Errorhandling. + } + if (SetEndOfFile(m_fd) == 0) { + // TODO: Errorhandling. + } +#else + ftruncate(m_fd, size); +#endif + } + + blob_t *ptr(std::uint32_t pos = 0) const noexcept { + return (blob_t *)(m_mmap->data() + pos); + } + + template + void store_im(T const *data, std::uint32_t length) noexcept { + _fcopy( + reinterpret_cast(m_intermediate_storage + m_intermediate_storage_offset), reinterpret_cast(data), + length + ); + m_intermediate_storage_offset += length; + } + + void compress_frames() { + auto container = ContainerHeaderType{}; + // printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); + const int cp_size = ::LZ4_compress_default( + reinterpret_cast(m_intermediate_storage), + reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), m_intermediate_storage_offset, + LZ4_COMPRESSBOUND(m_intermediate_storage_offset) + ); + if (cp_size < 0) { + throw std::runtime_error("LZ4 compression failed."); + } + // printf("comp: %d %d [%f]\n", m_intermediate_storage_offset, cp_size, double(m_intermediate_storage_offset) / + // double(cp_size)); + container.record_count = m_container_record_count; + container.size_compressed = cp_size; + container.size_uncompressed = m_container_size_uncompressed; + _fcopy(reinterpret_cast(ptr(m_offset)), reinterpret_cast(&container), detail::CONTAINER_SIZE); + m_offset += (detail::CONTAINER_SIZE + cp_size); + m_total_size_uncompressed += m_container_size_uncompressed; + m_total_size_compressed += cp_size; + m_record_count += m_container_record_count; + m_container_size_uncompressed = 0; + m_container_size_compressed = 0; + m_container_record_count = 0; + m_intermediate_storage_offset = 0; + m_num_containers += 1; + } + + void write_bytes(std::uint32_t pos, std::uint32_t count, char const *buf) const noexcept { + auto addr = reinterpret_cast(ptr(pos)); + + _fcopy(addr, buf, count); + } + + void write_header( + uint16_t version, uint16_t options, uint32_t num_containers, uint32_t record_count, uint32_t size_compressed, + uint32_t size_uncompressed + ) noexcept { + auto header = FileHeaderType{}; + write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); + header.hdr_size = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; + header.version = version; + header.options = options; + header.num_containers = num_containers; + header.record_count = record_count; + header.size_compressed = size_compressed; + header.size_uncompressed = size_uncompressed; + write_bytes(0x00000000UL + detail::MAGIC_SIZE, detail::FILE_HEADER_SIZE, reinterpret_cast(&header)); + } + + bool start_thread() noexcept { + if (collector_thread.joinable()) { + return false; + } + stop_collector_thread_flag = false; +#ifdef __APPLE__ + collector_thread = std::thread([this]() { +#else + collector_thread = std::jthread([this]() { +#endif + while (!stop_collector_thread_flag) { + auto item = my_queue.get(); + const auto content = item.get(); + if (stop_collector_thread_flag == true) { + break; + } + const auto [category, counter, timestamp, length, payload] = *content; + const frame_header_t frame{ category, counter, timestamp, length }; + store_im(&frame, sizeof(frame)); + store_im(payload, length); + delete[] payload; + m_container_record_count += 1; + m_container_size_uncompressed += (sizeof(frame) + length); + if (m_container_size_uncompressed > m_chunk_size) { + compress_frames(); + } + } + }); + return true; + } + + bool stop_thread() noexcept { + if (!collector_thread.joinable()) { + return false; + } + stop_collector_thread_flag = true; + my_queue.put(FrameTupleWriter{}); // Put something into the queue, otherwise the thread will hang forever. + collector_thread.join(); + return true; + } + + private: + + std::string m_file_name; + std::uint32_t m_offset{ 0 }; + std::uint32_t m_chunk_size{ 0 }; + std::uint32_t m_num_containers{ 0 }; + std::uint32_t m_record_count{ 0UL }; + std::uint32_t m_container_record_count{ 0UL }; + std::uint32_t m_total_size_uncompressed{ 0UL }; + std::uint32_t m_total_size_compressed{ 0UL }; + std::uint32_t m_container_size_uncompressed{ 0UL }; + std::uint32_t m_container_size_compressed{ 0UL }; + __ALIGN blob_t *m_intermediate_storage{ nullptr }; + std::uint32_t m_intermediate_storage_offset{ 0 }; + mio::file_handle_type m_fd{ INVALID_HANDLE_VALUE }; + mio::mmap_sink *m_mmap{ nullptr }; + bool m_finalized{ false }; +#ifdef __APPLE__ + std::thread collector_thread{}; +#else + std::jthread collector_thread{}; +#endif + std::mutex mtx; + TsQueue my_queue; + BlockMemory mem{}; + std::atomic_bool stop_collector_thread_flag{ false }; +}; + +#endif // RECORDER_WRITER_HPP diff --git a/pyxcp/tests/test_binpacking.py b/pyxcp/tests/test_binpacking.py new file mode 100644 index 0000000..abd669a --- /dev/null +++ b/pyxcp/tests/test_binpacking.py @@ -0,0 +1,186 @@ +import pytest + +from pyxcp.daq_stim.optimize import make_continuous_blocks +from pyxcp.daq_stim.optimize import McObject +from pyxcp.daq_stim.optimize.binpacking import Bin +from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing + + +@pytest.fixture +def blocks(): + return [ + McObject(name="", address=0x000E10BA, length=2), + McObject(name="", address=0x000E10BE, length=2), + McObject(name="", address=0x000E41F4, length=4), + McObject(name="", address=0x000E51FC, length=4), + McObject(name="", address=0x00125288, length=4), + McObject(name="", address=0x00125294, length=4), + McObject(name="", address=0x001252A1, length=1), + McObject(name="", address=0x001252A4, length=4), + McObject(name="", address=0x00125438, length=3), + McObject(name="", address=0x0012543C, length=1), + ] + + +def test_pack_to_single_bin(blocks): + BIN_SIZE = 253 + bins = first_fit_decreasing(items=blocks, bin_size=BIN_SIZE) + + assert len(bins) == 1 + bin0 = bins[0] + assert bin0.residual_capacity == BIN_SIZE - 29 + assert bin0.entries == [ + McObject(name="", address=0x000E41F4, length=4), + McObject(name="", address=0x000E51FC, length=4), + McObject(name="", address=0x00125288, length=4), + McObject(name="", address=0x00125294, length=4), + McObject(name="", address=0x001252A4, length=4), + McObject(name="", address=0x00125438, length=3), + McObject(name="", address=0x000E10BA, length=2), + McObject(name="", address=0x000E10BE, length=2), + McObject(name="", address=0x001252A1, length=1), + McObject(name="", address=0x0012543C, length=1), + ] + + +def test_pack_empty_block_set(): + BIN_SIZE = 253 + bins = first_fit_decreasing(items=[], bin_size=BIN_SIZE) + assert bins == [Bin(size=BIN_SIZE)] + + +def test_pack_to_multiple_bins1(blocks): + BIN_SIZE = 6 + bins = first_fit_decreasing(items=blocks, bin_size=BIN_SIZE) + assert len(bins) == 6 + bin0, bin1, bin2, bin3, bin4, bin5 = bins + assert bin0.residual_capacity == 0 + assert bin0.entries == [ + McObject(name="", address=0x000E41F4, length=4), + McObject(name="", address=0x000E10BA, length=2), + ] + assert bin1.residual_capacity == 0 + assert bin1.entries == [ + McObject(name="", address=0x000E51FC, length=4), + McObject(name="", address=0x000E10BE, length=2), + ] + assert bin2.residual_capacity == 0 + assert bin2.entries == [ + McObject(name="", address=0x00125288, length=4), + McObject(name="", address=0x001252A1, length=1), + McObject(name="", address=0x0012543C, length=1), + ] + assert bin3.residual_capacity == 2 + assert bin3.entries == [McObject(name="", address=0x00125294, length=4)] + assert bin4.residual_capacity == 2 + assert bin4.entries == [McObject(name="", address=0x001252A4, length=4)] + assert bin5.residual_capacity == 3 + assert bin5.entries == [McObject(name="", address=0x00125438, length=3)] + + +def test_binpacking_raises(blocks): + BIN_SIZE = 7 + with pytest.raises(ValueError): + first_fit_decreasing(items=[McObject(name="", address=0x1000, length=32)], bin_size=BIN_SIZE) + + +def test_binpacking_works(blocks): + BIN_SIZE = 7 + first_fit_decreasing(items=[McObject(name="", address=0x1000, length=7)], bin_size=BIN_SIZE) + + +def test_make_continuous_blocks1(): + BLOCKS = [ + McObject(name="", address=0x000E0002, length=2), + McObject(name="", address=0x000E0008, ext=23, length=4), + McObject(name="", address=0x000E0004, length=4), + McObject(name="", address=0x000E000C, ext=23, length=4), + McObject(name="", address=0x000E0000, length=2), + ] + bins = make_continuous_blocks(chunks=BLOCKS) + assert bins == [ + McObject( + name="", + address=917504, + ext=0, + length=8, + components=[ + McObject(name="", address=917504, ext=0, length=2, components=[]), + McObject(name="", address=917506, ext=0, length=2, components=[]), + McObject(name="", address=917508, ext=0, length=4, components=[]), + ], + ), + McObject( + name="", + address=917512, + ext=23, + length=8, + components=[ + McObject(name="", address=917512, ext=23, length=4, components=[]), + McObject(name="", address=917516, ext=23, length=4, components=[]), + ], + ), + ] + + +def test_make_continuous_blocks2(): + BLOCKS = [ + McObject(name="", address=0x000E0002, length=2), + McObject(name="", address=0x000E0008, length=4), + McObject(name="", address=0x000E0004, length=4), + McObject(name="", address=0x000E000C, length=4), + McObject(name="", address=0x000E0000, length=2), + ] + bins = make_continuous_blocks(chunks=BLOCKS) + assert bins == [ + McObject( + name="", + address=917504, + ext=0, + length=16, + components=[ + McObject(name="", address=917504, ext=0, length=2, components=[]), + McObject(name="", address=917506, ext=0, length=2, components=[]), + McObject(name="", address=917508, ext=0, length=4, components=[]), + McObject(name="", address=917512, ext=0, length=4, components=[]), + McObject(name="", address=917516, ext=0, length=4, components=[]), + ], + ) + ] + + +def test_make_continuous_blocks3(): + BLOCKS = [ + McObject(name="", address=0x000E0002, ext=0x01, length=2), + McObject(name="", address=0x000E0008, ext=0x03, length=4), + McObject(name="", address=0x000E0004, ext=0x02, length=4), + McObject(name="", address=0x000E000C, ext=0x04, length=4), + McObject(name="", address=0x000E0000, ext=0x00, length=2), + ] + bins = make_continuous_blocks(chunks=BLOCKS) + assert bins == [ + McObject( + name="", address=917504, ext=0, length=2, components=[McObject(name="", address=917504, ext=0, length=2, components=[])] + ), + McObject( + name="", address=917506, ext=1, length=2, components=[McObject(name="", address=917506, ext=1, length=2, components=[])] + ), + McObject( + name="", address=917508, ext=2, length=4, components=[McObject(name="", address=917508, ext=2, length=4, components=[])] + ), + McObject( + name="", address=917512, ext=3, length=4, components=[McObject(name="", address=917512, ext=3, length=4, components=[])] + ), + McObject( + name="", address=917516, ext=4, length=4, components=[McObject(name="", address=917516, ext=4, length=4, components=[])] + ), + ] + + +def test_mc_object_len_zero(): + with pytest.raises(ValueError): + McObject(name="", address=0, ext=0, length=0) + + +def test_mc_object_ok(): + McObject(name="", address=0, ext=0, length=1) diff --git a/pyxcp/tests/test_daq.py b/pyxcp/tests/test_daq.py new file mode 100644 index 0000000..9700445 --- /dev/null +++ b/pyxcp/tests/test_daq.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from unittest.mock import Mock +from unittest.mock import patch + +from pyxcp.daq_stim import Daq +from pyxcp.daq_stim import DaqList + +# import pytest + + +DAQ_INFO = { + "channels": [ + { + "cycle": 0, + "maxDaqList": 1, + "name": "Key T", + "priority": 0, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": False}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 10, + "maxDaqList": 1, + "name": "10 ms", + "priority": 1, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 100, + "maxDaqList": 1, + "name": "100ms", + "priority": 2, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 1, + "maxDaqList": 1, + "name": "1ms", + "priority": 3, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 10, + "maxDaqList": 1, + "name": "FilterBypassDaq", + "priority": 4, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": True, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + { + "cycle": 10, + "maxDaqList": 1, + "name": "FilterBypassStim", + "priority": 5, + "properties": {"consistency": "CONSISTENCY_ODT", "daq": False, "packed": False, "stim": True}, + "unit": "EVENT_CHANNEL_TIME_UNIT_1MS", + }, + ], + "processor": { + "keyByte": { + "addressExtension": "AE_DIFFERENT_WITHIN_ODT", + "identificationField": "IDF_REL_ODT_NUMBER_ABS_DAQ_LIST_NUMBER_BYTE", + "optimisationType": "OM_DEFAULT", + }, + "maxDaq": 0, + "minDaq": 0, + "properties": { + "bitStimSupported": False, + "configType": "DYNAMIC", + "overloadEvent": False, + "overloadMsb": True, + "pidOffSupported": False, + "prescalerSupported": True, + "resumeSupported": True, + "timestampSupported": True, + }, + }, + "resolution": { + "granularityOdtEntrySizeDaq": 1, + "granularityOdtEntrySizeStim": 1, + "maxOdtEntrySizeDaq": 218, + "maxOdtEntrySizeStim": 218, + "timestampMode": {"fixed": False, "size": "S4", "unit": "DAQ_TIMESTAMP_UNIT_10US"}, + "timestampTicks": 10, + }, +} + +SLAVE_INFO = { + "addressGranularity": 0, + "byteOrder": 0, + "interleavedMode": False, + "masterBlockMode": True, + "maxBs": 2, + "maxCto": 255, + "maxDto": 1500, + "maxWriteDaqMultipleElements": 31, + "minSt": 0, + "optionalCommMode": True, + "pgmProcessor": {}, + "protocolLayerVersion": 1, + "queueSize": 0, + "slaveBlockMode": True, + "supportsCalpag": True, + "supportsDaq": True, + "supportsPgm": True, + "supportsStim": True, + "transportLayerVersion": 1, + "xcpDriverVersionNumber": 25, +} + + +class AttrDict(dict): + def __getattr__(self, name): + return self[name] + + +class MockMaster: + def __init__(self): + self.slaveProperties = AttrDict( + { + "maxDto": 1500, + "supportsDaq": True, + } + ) + + def getDaqInfo(self): + return DAQ_INFO + + def freeDaq(self): + pass + + def allocDaq(self, daq_count): + self.daq_count = daq_count + + def allocOdt(self, daq_num, odt_count): + pass + + def allocOdtEntry(self, daq_num, odt_num, entry_count): + pass + + def setDaqPtr(self, daqListNumber, odtNumber, odtEntryNumber): + pass + + def writeDaq(self, bitOffset, entrySize, addressExt, address): + pass + + def setDaqListMode(self, mode, daqListNumber, eventChannelNumber, prescaler, priority): + pass + + def startStopDaqList(self, mode, daqListNumber): + pass + + def startStopSynch(self, mode): + pass + + +DAQ_LISTS = [ + DaqList( + 1, + [ + ("channel1", 0x1BD004, 0, 4, "U32"), + ("channel2", 0x1BD008, 0, 4, "U32"), + ("PWMFiltered", 0x1BDDE2, 0, 1, "U8"), + ("PWM", 0x1BDDDF, 0, 1, "U8"), + ("Triangle", 0x1BDDDE, 0, 1, "U8"), + ], + ), + DaqList( + 3, + [ + ("TestWord_001", 0x1BE120, 0, 2, "U16"), + ("TestWord_003", 0x1BE128, 0, 2, "U16"), + ("TestWord_004", 0x1BE12C, 0, 2, "U16"), + ("TestWord_005", 0x1BE134, 0, 2, "U16"), + ("TestWord_006", 0x1BE134, 0, 2, "U16"), + ("TestWord_007", 0x1BE138, 0, 2, "U16"), + ("TestWord_008", 0x1BE13C, 0, 2, "U16"), + ("TestWord_009", 0x1BE140, 0, 2, "U16"), + ("TestWord_011", 0x1BE148, 0, 2, "U16"), + # ("", ), + ], + ), +] + +daq = Daq() +daq.set_master(MockMaster()) + +daq.add_daq_lists(DAQ_LISTS) +daq.setup() +daq.start() From 2cae8e30f74f81db93a7d9183e9e7cb7d1441f9d Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Fri, 20 Oct 2023 08:16:23 +0200 Subject: [PATCH 16/99] Code cleanup by pre-commit. --- .clang-format | 24 +-- .codeclimate.yml | 4 +- .darglint | 2 + .github/PULL_REQUEST_TEMPLATE.md | 2 + .github/workflows/pythonapp.yml | 84 ++++---- .github/workflows/testing.yml | 36 ++-- .pre-commit-config.yaml | 231 +++++++++++---------- .readthedocs.yml | 18 +- CODE_OF_CONDUCT.md | 20 +- README.md | 7 +- appveyor.yml | 16 +- bandit.yml | 2 + build_ext.py | 17 +- docs/Makefile | 2 +- docs/conf.py | 2 +- docs/configuration.rst | 1 - docs/howto_cli_tools.rst | 2 - docs/installation.rst | 1 - docs/tutorial.rst | 2 - pyproject.toml | 102 +++++---- pyxcp/__init__.py | 16 +- pyxcp/asam/types.py | 8 +- pyxcp/checksum.py | 1 - pyxcp/cmdline.py | 2 +- pyxcp/config.py | 2 +- pyxcp/config/__init__.py | 71 ++----- pyxcp/config/legacy.py | 1 + pyxcp/constants.py | 21 +- pyxcp/cpp_ext/__init__.py | 4 +- pyxcp/daq_stim/__init__.py | 17 +- pyxcp/daq_stim/optimize/__init__.py | 9 +- pyxcp/daq_stim/optimize/binpacking.py | 4 +- pyxcp/dllif.py | 7 +- pyxcp/errormatrix.py | 5 +- pyxcp/examples/xcp_policy.py | 7 +- pyxcp/examples/xcp_read_benchmark.py | 10 +- pyxcp/examples/xcp_skel.py | 2 - pyxcp/examples/xcp_unlock.py | 2 +- pyxcp/examples/xcp_user_supplied_driver.py | 1 - pyxcp/examples/xcphello.py | 2 +- pyxcp/examples/xcphello_recorder.py | 2 +- pyxcp/master/__init__.py | 3 +- pyxcp/master/errorhandler.py | 19 +- pyxcp/master/master.py | 64 +++--- pyxcp/recorder/__init__.py | 4 +- pyxcp/recorder/reco.py | 13 +- pyxcp/recorder/rekorder.cpp | 40 ++-- pyxcp/recorder/setup.py | 19 +- pyxcp/recorder/test_reko.py | 5 +- pyxcp/recorder/unfolder.hpp | 4 +- pyxcp/recorder/wrap.cpp | 1 - pyxcp/scripts/pyxcp_probe_can_drivers.py | 2 - pyxcp/scripts/xcp_fetch_a2l.py | 1 - pyxcp/scripts/xcp_id_scanner.py | 1 - pyxcp/scripts/xcp_info.py | 2 +- pyxcp/stim/setup.py | 17 +- pyxcp/tests/test_binpacking.py | 6 +- pyxcp/tests/test_can.py | 24 ++- pyxcp/tests/test_checksum.py | 1 + pyxcp/tests/test_config.py | 1 + pyxcp/tests/test_daq.py | 8 +- pyxcp/tests/test_master.py | 1 - pyxcp/tests/test_utils.py | 6 +- pyxcp/timing.py | 1 - pyxcp/transport/__init__.py | 19 +- pyxcp/transport/base.py | 11 +- pyxcp/transport/can.py | 26 +-- pyxcp/transport/candriver/__init__.py | 1 - pyxcp/transport/candriver/pc_canalystii.py | 3 +- pyxcp/transport/candriver/pc_etas.py | 3 +- pyxcp/transport/candriver/pc_gsusb.py | 3 +- pyxcp/transport/candriver/pc_iscan.py | 3 +- pyxcp/transport/candriver/pc_ixxat.py | 3 +- pyxcp/transport/candriver/pc_kvaser.py | 3 +- pyxcp/transport/candriver/pc_neovi.py | 3 +- pyxcp/transport/candriver/pc_nican.py | 3 +- pyxcp/transport/candriver/pc_nixnet.py | 3 +- pyxcp/transport/candriver/pc_pcan.py | 3 +- pyxcp/transport/candriver/pc_seeed.py | 3 +- pyxcp/transport/candriver/pc_serial.py | 3 +- pyxcp/transport/candriver/pc_slcan.py | 3 +- pyxcp/transport/candriver/pc_socketcan.py | 3 +- pyxcp/transport/candriver/pc_systec.py | 3 +- pyxcp/transport/candriver/pc_usb2can.py | 3 +- pyxcp/transport/candriver/pc_vector.py | 3 +- pyxcp/transport/candriver/python_can.py | 6 +- pyxcp/transport/cxx_ext/CMakeLists.txt | 1 - pyxcp/transport/cxx_ext/setup.py | 15 +- pyxcp/transport/eth.py | 8 +- pyxcp/transport/sxi.py | 8 +- pyxcp/transport/usb_transport.py | 10 +- pyxcp/types.py | 41 ++-- pyxcp/utils.py | 8 +- pyxcp/vector/map.py | 4 +- requirements.txt | 23 -- selective_tests.py | 3 +- setup.py | 44 +++- 97 files changed, 584 insertions(+), 707 deletions(-) create mode 100644 .darglint create mode 100644 bandit.yml delete mode 100644 requirements.txt diff --git a/.clang-format b/.clang-format index 8ed8ff6..32f9bb5 100644 --- a/.clang-format +++ b/.clang-format @@ -73,7 +73,7 @@ BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: AfterColon BreakStringLiterals: true ColumnLimit: 132 -CommentPragmas: '^ IWYU pragma:' +CommentPragmas: "^ IWYU pragma:" CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 @@ -98,14 +98,14 @@ IncludeCategories: - Regex: '^<.*\.h>' Priority: 1 SortPriority: 0 - - Regex: '^<.*' + - Regex: "^<.*" Priority: 2 SortPriority: 0 - - Regex: '.*' + - Regex: ".*" Priority: 3 SortPriority: 0 -IncludeIsMainRegex: '([-_](test|unittest))?$' -IncludeIsMainSourceRegex: '' +IncludeIsMainRegex: "([-_](test|unittest))?$" +IncludeIsMainSourceRegex: "" IndentCaseLabels: true IndentCaseBlocks: true IndentExternBlock: Indent @@ -119,8 +119,8 @@ IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: false -MacroBlockBegin: '' -MacroBlockEnd: '' +MacroBlockBegin: "" +MacroBlockEnd: "" MaxEmptyLinesToKeep: 1 NamespaceIndentation: Inner ObjCBinPackProtocolList: Never @@ -145,9 +145,9 @@ RawStringFormats: - cpp - Cpp - CPP - - 'c++' - - 'C++' - CanonicalDelimiter: '' + - "c++" + - "C++" + CanonicalDelimiter: "" BasedOnStyle: google - Language: TextProto Delimiters: @@ -163,7 +163,7 @@ RawStringFormats: - PARSE_TEXT_PROTO - ParseTextOrDie - ParseTextProtoOrDie - CanonicalDelimiter: '' + CanonicalDelimiter: "" BasedOnStyle: google ReferenceAlignment: Left ReflowComments: true @@ -204,5 +204,3 @@ StatementMacros: TabWidth: 4 UseCRLF: false UseTab: Never -... - diff --git a/.codeclimate.yml b/.codeclimate.yml index 059d224..f159a80 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -7,7 +7,7 @@ engines: enabled: true ratings: paths: - - "**.py" + - "**.py" checks: argument-count: @@ -42,4 +42,4 @@ checks: threshold: 50 # language-specific defaults. an override will affect all languages. exclude_patterns: -- "pyxcp/tests/**" + - "pyxcp/tests/**" diff --git a/.darglint b/.darglint new file mode 100644 index 0000000..72ccc6c --- /dev/null +++ b/.darglint @@ -0,0 +1,2 @@ +[darglint] +strictness = long diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b86bc43..de1986a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,7 @@ ## Types of changes + + - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 123da4b..65cd59b 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -14,46 +14,42 @@ jobs: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: - fail-fast: false - matrix: - os: [ - macos-13, - windows-latest, - ubuntu-latest - ] - cibw_archs: ["auto"] - include: - - os: ubuntu-latest - cibw_archs: "aarch64" + fail-fast: false + matrix: + os: [macos-13, windows-latest, ubuntu-latest] + cibw_archs: ["auto"] + include: + - os: ubuntu-latest + cibw_archs: "aarch64" steps: - uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: nightly - override: true - components: rustfmt, clippy + toolchain: nightly + override: true + components: rustfmt, clippy - name: Build wheel uses: pypa/cibuildwheel@v2.16 - uses: actions/upload-artifact@v2 with: - path: ./wheelhouse/*.whl + path: ./wheelhouse/*.whl build_sdist: name: Build source distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2 - - name: Build sdist - run: | - pip install -U build - python -m build --sdist + - name: Build sdist + run: | + pip install -U build + python -m build --sdist - - uses: actions/upload-artifact@v2 - with: - path: dist/*.tar.gz + - uses: actions/upload-artifact@v2 + with: + path: dist/*.tar.gz upload_pypi: needs: [build_wheels, build_sdist] runs-on: ubuntu-latest @@ -61,27 +57,27 @@ jobs: # alternatively, to publish when a GitHub Release is created, use the following rule: # if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v2 - with: - name: artifact - path: dist + - uses: actions/download-artifact@v2 + with: + name: artifact + path: dist - - uses: pypa/gh-action-pypi-publish@v1.4.2 - with: - user: __token__ - password: ${{ secrets.PYPI_PASSWORD }} + - uses: pypa/gh-action-pypi-publish@v1.4.2 + with: + user: __token__ + password: ${{ secrets.PYPI_PASSWORD }} # To test: repository_url: https://test.pypi.org/legacy/ # - # - name: Upload to PyPI - #uses: pypa/gh-action-pypi-publish@v1.4.2 - #with: - # user: ${{ secrets.PYPI_USER_NAME }} - # password: ${{ secrets.PYPI_PASSWORD }} - #- name: Build and publish - # env: - # TWINE_USERNAME: ${{ secrets.PYPI_USER_NAME }} - # TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - # if: env.TWINE_USERNAME != null - # run: | - # python setup.py bdist_wheel - # twine upload dist/* + # - name: Upload to PyPI + #uses: pypa/gh-action-pypi-publish@v1.4.2 + #with: + # user: ${{ secrets.PYPI_USER_NAME }} + # password: ${{ secrets.PYPI_PASSWORD }} + #- name: Build and publish + # env: + # TWINE_USERNAME: ${{ secrets.PYPI_USER_NAME }} + # TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + # if: env.TWINE_USERNAME != null + # run: | + # python setup.py bdist_wheel + # twine upload dist/* diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 6648c3f..2bca4ca 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -5,9 +5,9 @@ name: Run tests. on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] jobs: build: @@ -15,20 +15,20 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] # macos-latest - python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] + os: [ubuntu-latest, windows-latest] # macos-latest + python: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip setuptools - pip install -r requirements.txt - python setup.py install - - name: Test with pytest - run: | - pip install pytest - pytest + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools + pip install -r requirements.txt + python setup.py install + - name: Test with pytest + run: | + pip install pytest + pytest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e663ef5..fe230c8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,107 +1,126 @@ repos: -- repo: local - hooks: - - id: check-yaml - name: check-yaml - entry: check-yaml - language: python - types: [yaml] - stages: [commit] - - id: check-json - name: check-json - entry: check-json - language: python - types: [json] - stages: [commit] - - id: check-toml - name: check-toml - entry: check-toml - language: python - types: [toml] - stages: [commit] - - id: check-ast - name: check-ast - entry: check-ast - language: python - types: [python] - stages: [commit] - #- id: autopep8 - #name: autopep8 - #entry: autopep8 - #language: python - #types: [python] - #args: [-a, -i] - #stages: [commit] - - id: check-builtin-literals - name: check-builtin-literals - entry: check-builtin-literals - language: python - types: [python] - stages: [commit] - - id: check-case-conflict - name: check-case-conflict - entry: check-case-conflict - language: python - types: [python] - stages: [commit] - - id: check-merge-conflict - name: check-merge-conflict - entry: check-merge-conflict - language: python - types: [text] - stages: [commit] - - id: fix-byte-order-marker - name: fix-byte-order-marker - entry: fix-byte-order-marker - language: python - types: [python] - stages: [commit] - - id: mixed-line-ending - name: mixed-line-ending - entry: mixed-line-ending - language: python - types_or: [c, c++, python] - stages: [commit] - - id: end-of-file-fixer - name: end-of-file-fixer - entry: end-of-file-fixer - language: python - types_or: [c, c++, python] - stages: [commit] - - id: trailing-whitespace-fixer - name: trailing-whitespace-fixer - entry: trailing-whitespace-fixer - language: python - types_or: [c, c++, python] - stages: [commit] - - id: reorder-python-imports - name: reorder-python-imports - entry: reorder-python-imports - language: python - types: [python] - stages: [commit] - - id: black - name: black - entry: black - language: python - types: [python] - stages: [commit] - - id: selective_tests - name: selective_tests - entry: ./selective_tests.py - language: python - types: [python] - stages: [commit] - - id: flake8 - name: flake8 - entry: flake8 - language: python - types: [python] - stages: [commit] - - id: pytest-check - name: pytest-check - entry: pytest - language: system - pass_filenames: false - always_run: true - stages: [push] + - repo: local + hooks: + - id: bandit + name: bandit + entry: bandit + language: system + types: [python] + require_serial: true + args: ["-c", "bandit.yml"] + - id: black + name: black + entry: black + language: system + types: [python] + require_serial: true + - id: ruff + name: ruff + entry: ruff + language: system + types: [python] + require_serial: true + - id: check-added-large-files + name: Check for added large files + entry: check-added-large-files + language: system + - id: check-toml + name: Check Toml + entry: check-toml + language: system + types: [toml] + - id: check-json + name: check-json + entry: check-json + language: python + types: [json] + - id: check-yaml + name: Check Yaml + entry: check-yaml + language: system + types: [yaml] + - id: check-ast + name: check-ast + entry: check-ast + language: python + types: [python] + stages: [commit] + - id: check-builtin-literals + name: check-builtin-literals + entry: check-builtin-literals + language: python + types: [python] + stages: [commit] + - id: check-case-conflict + name: check-case-conflict + entry: check-case-conflict + language: python + types: [python] + stages: [commit] + - id: check-merge-conflict + name: check-merge-conflict + entry: check-merge-conflict + language: python + types: [text] + stages: [commit] + - id: fix-byte-order-marker + name: fix-byte-order-marker + entry: fix-byte-order-marker + language: python + types: [python] + stages: [commit] + - id: mixed-line-ending + name: mixed-line-ending + entry: mixed-line-ending + language: python + types_or: [c, c++, python] + stages: [commit] + - id: end-of-file-fixer + name: end-of-file-fixer + entry: end-of-file-fixer + language: python + types_or: [python] + stages: [commit] + - id: darglint + name: darglint + entry: darglint + language: system + types: [python] + stages: [manual] + - id: end-of-file-fixer + name: Fix End of Files + entry: end-of-file-fixer + language: system + types: [text] + stages: [commit, push, manual] + - id: flake8 + name: flake8 + entry: flake8 + language: system + types: [python] + require_serial: true + args: [--darglint-ignore-regex, .*] + - id: isort + name: isort + entry: isort + require_serial: true + language: system + types_or: [cython, pyi, python] + args: ["--filter-files"] + - id: pyupgrade + name: pyupgrade + description: Automatically upgrade syntax for newer versions. + entry: pyupgrade + language: system + types: [python] + args: [--py37-plus] + - id: trailing-whitespace + name: Trim Trailing Whitespace + entry: trailing-whitespace-fixer + language: system + types: [text] + stages: [commit, push, manual] + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.6.0 + hooks: + - id: prettier diff --git a/.readthedocs.yml b/.readthedocs.yml index ac448af..5f125f5 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,17 +1,15 @@ - version: 2 sphinx: - configuration: docs/conf.py + configuration: docs/conf.py formats: all python: - version: 3.7 - install: - - requirements: docs/requirements.txt - - method: pip - path: . - - method: setuptools - path: . - + version: 3.7 + install: + - requirements: docs/requirements.txt + - method: pip + path: . + - method: setuptools + path: . diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 56f2728..0260460 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities diff --git a/README.md b/README.md index 6a93fd6..00654fe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ - -pyXCP -===== +# pyXCP [![Codacy Badge](https://api.codacy.com/project/badge/Grade/85f774708b2542d98d02df55c743d24a)](https://app.codacy.com/app/christoph2/pyxcp?utm_source=github.com&utm_medium=referral&utm_content=christoph2/pyxcp&utm_campaign=Badge_Grade_Settings) [![Maintainability](https://api.codeclimate.com/v1/badges/4c639f3695f2725e392a/maintainability)](https://codeclimate.com/github/christoph2/pyxcp/maintainability) @@ -24,17 +22,20 @@ XCP also replaces the older CCP (CAN Calibration Protocol). pyXCP is hosted on Github, get the latest release: [https://github.com/christoph2/pyxcp](https://github.com/christoph2/pyxcp) You can install pyxcp from source: + ``` pip install -r requirements.txt python setup.py install ``` Alternatively, you can install pyxcp from source with pip: + ``` pip install git+https://github.com/christoph2/pyxcp.git ``` Alternatively, get pyxcp from [PyPI](https://pypi.org/project/pyxcp/): + ``` pip install pyxcp ``` diff --git a/appveyor.yml b/appveyor.yml index af542ea..560feab 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,7 @@ image: Visual Studio 2022 environment: - matrix: - # For Python versions available on Appveyor, see # https://www.appveyor.com/docs/windows-images-software/#python # The list here is complete (excluding Python 2.6, which @@ -63,11 +61,11 @@ on_finish: $wc.UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\result.xml)) deploy: -- provider: GitHub - artifact: '*.*' - on: + - provider: GitHub + artifact: "*.*" + on: APPVEYOR_REPO_TAG: true - description: Test release -- do not use - tag: $(APPVEYOR_REPO_TAG_NAME) - draft: false - prerelease: true + description: Test release -- do not use + tag: $(APPVEYOR_REPO_TAG_NAME) + draft: false + prerelease: true diff --git a/bandit.yml b/bandit.yml new file mode 100644 index 0000000..9b2cfb0 --- /dev/null +++ b/bandit.yml @@ -0,0 +1,2 @@ +assert_used: + skips: ["*/test_*.py"] diff --git a/build_ext.py b/build_ext.py index 8495d8d..1fc2256 100644 --- a/build_ext.py +++ b/build_ext.py @@ -1,21 +1,12 @@ -import subprocess -from pathlib import Path -from typing import Any -from typing import Dict +import subprocess # nosec +from typing import Any, Dict -from pybind11.setup_helpers import build_ext -from pybind11.setup_helpers import naive_recompile -from pybind11.setup_helpers import ParallelCompile -from pybind11.setup_helpers import Pybind11Extension +from pybind11.setup_helpers import ParallelCompile, Pybind11Extension, naive_recompile -# from setuptools_cpp import CMakeExtension, ExtensionBuilder, Pybind11Extension -# ext_modules = [ -# CMakeExtension("pyxcp.recorder", sourcedir="pyxcp/recorder") -# ] print("Running 'build.py'...") -PYB11_INCLUDE_DIRS = subprocess.check_output(["pybind11-config", "--includes"]) +PYB11_INCLUDE_DIRS = subprocess.check_output(["pybind11-config", "--includes"]) # nosec EXT_NAMES = ["rekorder"] ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() diff --git a/docs/Makefile b/docs/Makefile index 44d90df..13451fd 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -17,4 +17,4 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py index cdbf163..9547b92 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # @@ -13,6 +12,7 @@ import os import sys + sys.path.insert(0, os.path.abspath("../pyxcp")) diff --git a/docs/configuration.rst b/docs/configuration.rst index e0e3768..e083834 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -155,4 +155,3 @@ vector * `RX_QUEUE_SIZE`: int, 16384 * `FD`: bool, False * `DATA_BITRATE`: int, None - diff --git a/docs/howto_cli_tools.rst b/docs/howto_cli_tools.rst index b1f24a8..5c68aed 100644 --- a/docs/howto_cli_tools.rst +++ b/docs/howto_cli_tools.rst @@ -1,5 +1,3 @@ How-to write your own command-line tools ======================================== - - diff --git a/docs/installation.rst b/docs/installation.rst index 6d66f2b..9724db3 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -10,4 +10,3 @@ Installation and Getting Started Prerequisites ------------- - diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 82dd4f8..b23b9e5 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -1,4 +1,2 @@ Tutorial ======== - - diff --git a/pyproject.toml b/pyproject.toml index 1e87fbc..03ae8dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,44 +1,45 @@ + [build-system] -requires = ["pdm", "pdm-pep517", "pybind11<3.0.0,>=2.9.0", "wheel", "build", "twine", "setuptools"] -build-backend = "pdm.pep517.api" +requires = ["poetry-core>=1.0.0", "pybind11<3.0.0,>=2.9.0", "wheel", "build", "twine", "setuptools"] +build-backend = "poetry.core.masonry.api" -[tool.pdm] -includes = [] -build = "build_ext.py" -license-expression = "LGPL-3.0-or-later" -license-files.paths = ["LICENSE"] -[tool.pdm.dev-dependencies] -dev = [ - "pytest<7.0.0,>=6.2.5", - "pytest-runner<6.0.0,>=5.3.1", - "pytest-cov<4.0.0,>=3.0.0", +[tool.poetry.dev-dependencies] +Pygments = ">=2.10.0" +bandit = ">=1.7.4" +black = ">=21.10b0" +coverage = {extras = ["toml"], version = ">=6.2"} +darglint = ">=1.8.1" +flake8 = ">=4.0.1" +flake8-bugbear = ">=21.9.2" +flake8-docstrings = ">=1.6.0" +flake8-rst-docstrings = ">=0.2.5" +furo = ">=2021.11.12" +isort = ">=5.10.1" +mypy = ">=0.930" +pep8-naming = ">=0.12.1" +pre-commit = ">=2.16.0" +pre-commit-hooks = ">=4.1.0" +pytest = ">=6.2.5" +pyupgrade = ">=2.29.1" +safety = ">=1.10.3" +sphinx = ">=4.3.2" +sphinx-autobuild = ">=2021.3.14" +sphinx-click = ">=3.0.2" +typeguard = ">=2.13.3" +xdoctest = {extras = ["colors"], version = ">=0.15.10"} +myst-parser = {version = ">=0.16.1"} -] -[project] -authors = [ - {name = "Christoph Schueler"}, - {email = "cpu12.gems@googlemail.com"} -] -requires-python = ">=3.7" -dependencies = [ - "setuptools-cpp<1.0.0,>=0.1.0", - "Mako<2.0.0,>=1.1.6", - "construct<3.0.0,>=2.10.67", - "pyserial<4.0,>=3.5", - "pyusb<2.0.0,>=1.2.1", - "toml<1.0.0,>=0.10.2", - "python-can>=4.0.0", - "uptime>=3.0.1", - "chardet>=5.2.0", - "traitlets>=5.9.0", -] + +[tool.poetry] +authors = ["Christoph Schueler "] name = "pyxcp" version = "0.21.6" readme = "README.md" description = "Universal Calibration Protocol for Python" keywords = ["automotive", "ecu", "xcp", "asam", "autosar"] -license = {file = "LICENSE"} +homepage = "https://github.com/christoph2/pyxcp" +license = "LGPLv3" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development", @@ -51,10 +52,23 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12" ] -dynamic = ["entry-points"] +build = "build_ext.py" +# dynamic = ["entry-points"] -[project.urls] -homepage = "https://github.com/christoph2/pyxcp" +[tool.poetry.dependencies] +python = "^3.7" +construct = "^2.10.68" +mako = "^1.2.4" +pyserial = "^3.5" +pyusb = "^1.2.1" +python-can = "^4.2.2" +uptime = "^3.0.1" +rich = "^13.6.0" +chardet = "^5.2.0" +traitlets = "<=5.11.2" + +[tool.poetry.group.dev.dependencies] +ruff = "^0.1.0" [project.optional-dependencies] doc = [ @@ -62,7 +76,7 @@ doc = [ "sphinxcontrib-napoleon" ] -[project.scripts] +[tool.poetry.scripts] pyxcp-probe-can-drivers = "pyxcp.scripts.pyxcp_probe_can_drivers:main" xcp-id-scanner = "pyxcp.scripts.xcp_id_scanner:main" xcp-fetch-a2l = "pyxcp.scripts.xcp_fetch_a2l:main" @@ -72,6 +86,18 @@ xcp-info = "pyxcp.scripts.xcp_info:main" addopts = "--verbose --tb=short --junitxml=result.xml -o junit_family=xunit2" testpaths = "pyxcp/tests" +[tool.isort] +profile = "black" +force_single_line = false +lines_after_imports = 2 + +[tool.mypy] +strict = false +warn_unreachable = true +pretty = true +show_column_numbers = true +show_error_context = true + [tool.flake8] ignore = ["D203", "E203", "E266", "E501", "W503", "F403", "F401", "BLK100"] exclude = ''' @@ -97,6 +123,9 @@ show-source = true max-line-length = 132 select = ["B","C","E","F","W","T4","B9"] +[tool.ruff] +line-length = 132 + [tool.black] line-length=132 include = '\.pyi?$' @@ -123,4 +152,3 @@ build-verbosity = 3 build = "cp3{7,8,9,10,11,12}-*" skip = ["*-manylinux_i686", "*-musllinux_x86_64", "*-musllinux_i686"] # Skip Linux 32bit and MUSL builds. build-frontend = "build" - diff --git a/pyxcp/__init__.py b/pyxcp/__init__.py index 4ecf37b..580bbea 100644 --- a/pyxcp/__init__.py +++ b/pyxcp/__init__.py @@ -1,32 +1,28 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Universal Calibration Protocol for Python""" import sys from rich.traceback import install +from .master import Master # noqa: F401 +from .transport import Can, Eth, SxI, Usb # noqa: F401 + + install(show_locals=True, max_frames=3) # Install custom exception handler. if sys.platform == "win32" and sys.version_info[:2] < (3, 11): # patch the time module with the high resolution alternatives try: + import time + from win_precise_time import sleep as wpsleep from win_precise_time import time as wptime - import time - time.sleep = wpsleep time.time = wptime except ImportError: pass - -from .master import Master -from .transport import Can -from .transport import Eth -from .transport import SxI -from .transport import Usb - # if you update this manually, do not forget to update .bumpversion.cfg __version__ = "0.21.6" diff --git a/pyxcp/asam/types.py b/pyxcp/asam/types.py index 6e24022..5584536 100644 --- a/pyxcp/asam/types.py +++ b/pyxcp/asam/types.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import struct + INTEL = "<" MOTOROLA = ">" @@ -15,7 +15,7 @@ """ -class AsamBaseType(object): +class AsamBaseType: """Base class for ASAM codecs. Note @@ -51,7 +51,7 @@ def encode(self, value): bytes Encoded value. """ - return struct.pack("{}{}".format(self.byteorder, self.FMT), value) + return struct.pack(f"{self.byteorder}{self.FMT}", value) def decode(self, value): """Decode a value. @@ -68,7 +68,7 @@ def decode(self, value): data-type data-type is determined by derived class. """ - return struct.unpack("{}{}".format(self.byteorder, self.FMT), bytes(value))[0] + return struct.unpack(f"{self.byteorder}{self.FMT}", bytes(value))[0] class A_Uint8(AsamBaseType): diff --git a/pyxcp/checksum.py b/pyxcp/checksum.py index a1d4b72..63a758c 100644 --- a/pyxcp/checksum.py +++ b/pyxcp/checksum.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Checksum calculation for memory ranges .. [1] XCP Specification, BUILD_CHECKSUM service. diff --git a/pyxcp/cmdline.py b/pyxcp/cmdline.py index dd1981c..9567465 100644 --- a/pyxcp/cmdline.py +++ b/pyxcp/cmdline.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Parse (transport-layer specific) command line parameters and create a XCP master instance. @@ -9,6 +8,7 @@ from pyxcp.config import application from pyxcp.master import Master + warnings.simplefilter("always") diff --git a/pyxcp/config.py b/pyxcp/config.py index 32d856e..a597113 100644 --- a/pyxcp/config.py +++ b/pyxcp/config.py @@ -1,8 +1,8 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import json import pathlib + try: import toml except ImportError: diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 996148d..8a9a7d4 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import io import json import sys @@ -9,27 +8,24 @@ import can import toml -from traitlets import Any -from traitlets import Bool -from traitlets import Callable -from traitlets import Dict -from traitlets import Enum -from traitlets import Float -from traitlets import Int -from traitlets import Integer -from traitlets import List -from traitlets import TraitError -from traitlets import Unicode -from traitlets import Union -from traitlets.config import Application -from traitlets.config import Configurable -from traitlets.config import Instance -from traitlets.config import SingletonConfigurable -from traitlets.config.loader import Config -from traitlets.config.loader import load_pyconfig_files +from traitlets import ( + Any, + Bool, + Callable, + Dict, + Enum, + Float, + Integer, + List, + TraitError, + Unicode, + Union, +) +from traitlets.config import Application, Instance, SingletonConfigurable from pyxcp.config import legacy + warnings.simplefilter("always") @@ -520,9 +516,7 @@ class Virtual(SingletonConfigurable, CanBase): will keep the timestamp set in the :class:`~can.Message` instance. Otherwise, the timestamp value will be replaced with the current system time.""", - ).tag( - config=True - ) # protocol = Type(default_value=None, allow_none=True, help = """ """).tag(config=True) + ).tag(config=True) CAN_INTERFACE_MAP = { @@ -592,7 +586,7 @@ class Can(SingletonConfigurable): tseg1_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg1 (data).").tag(config=True) tseg2_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg2 (data).").tag(config=True) timing = Union( - [Instance(can.BitTiming), Instance(can.BitTimingFd)], + [Instance(can.BitTiming)], # , Instance(can.BitTimingFd) default_value=None, allow_none=True, help="""Custom bit timing settings. @@ -633,12 +627,11 @@ class Can(SingletonConfigurable): def __init__(self, **kws): super().__init__(**kws) - tos = self.class_own_traits() - if self.parent.layer == "CAN": if self.interface is None or self.interface not in self.VALID_INTERFACES: raise TraitError( - f"CAN interface must be one of {sorted(list(self.VALID_INTERFACES))} not the {type(self.interface).__name__} {self.interface}." + f"CAN interface must be one of {sorted(list(self.VALID_INTERFACES))} not the" + " {type(self.interface).__name__} {self.interface}." ) self.canalystii = CanAlystii.instance(config=self.config, parent=self) self.cantact = CanTact.instance(config=self.config, parent=self) @@ -676,9 +669,6 @@ class Eth(SingletonConfigurable): bind_to_address = Unicode(default_value=None, allow_none=True, help="Specific local address.").tag(config=True) bind_to_port = Integer(default_value=None, allow_none=True, help="Specific local port.").tag(config=True) - def __str__(self): - return f"Eth(host='{self.host}', port={self.port}, protocol='{self.protocol}', ipv6={self.ipv6}, tcp_nodelay={self.tcp_nodelay})" - class SxI(SingletonConfigurable): """SPI and SCI connections.""" @@ -694,9 +684,6 @@ class SxI(SingletonConfigurable): -cs Set the SxI checksum type LEN+CTR+PACKETS = 1, ONLY PACKETS = 2 (Default 0 no checksum) """ - def __str__(self): - return f"SxI(port='{self.port}', bitrate={self.bitrate}, bytesize={self.bytesize}, parity='{self.parity}', stopbits={self.stopbits})" - class USB(SingletonConfigurable): """ """ @@ -709,9 +696,6 @@ class USB(SingletonConfigurable): vendor_id = Integer(0).tag(config=True) product_id = Integer(0).tag(config=True) - def __str__(self): - return f"USB(serial_number='{self.serial_number}', configuration_number={self.configuration_number}, interface_number={self.interface_number}, command_endpoint_number={self.command_endpoint_number}, reply_endpoint_number={self.reply_endpoint_number})" - class Transport(SingletonConfigurable): """ """ @@ -743,11 +727,6 @@ def __init__(self, **kws): self.sxi = SxI.instance(config=self.config, parent=self) self.usb = USB.instance(config=self.config, parent=self) - def __str__(self): - return str( - f"Transport(layer='{self.layer}', create_daq_timestamps={self.create_daq_timestamps}, timeout={self.timeout}, alignment={self.alignment}), Can={self.can}, Eth={self.eth}, SxI={self.sxi}, USB={self.usb}" - ) - class General(SingletonConfigurable): """ """ @@ -759,11 +738,6 @@ class General(SingletonConfigurable): seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) seed_n_key_function = Callable(default_value=None, allow_none=True).tag(config=True) - def __str__(self): - return str( - f"General(loglevel: '{self.loglevel}', disable_error_handling: {self.disable_error_handling}, seed_n_key_dll: '{self.seed_n_key_dll}', seed_n_key_dll_same_bit_width: {self.seed_n_key_dll_same_bit_width})" - ) - class PyXCP(Application): config_file = Unicode(default_value="pyxcp_conf.py", help="base name of config file").tag(config=True) @@ -856,9 +830,6 @@ def generate_config_file(self, file_like: io.IOBase, config=None) -> None: for klass in self._classes_with_config_traits(): self._iterate_config_class(klass, [klass.__name__]) - def __str__(self): - return f"PyXCP: {self.config.general}" - class Configuration: pass @@ -872,6 +843,4 @@ class Configuration: # print(application.generate_config_file()) # print("*" * 80) -import sys - -application.generate_config_file(sys.stdout) +# application.generate_config_file(sys.stdout) diff --git a/pyxcp/config/legacy.py b/pyxcp/config/legacy.py index 26e201e..0e5c1fd 100644 --- a/pyxcp/config/legacy.py +++ b/pyxcp/config/legacy.py @@ -3,6 +3,7 @@ from traitlets.config.loader import Config + LEGACY_KEYWORDS = { # General "LOGLEVEL": "General.loglevel", diff --git a/pyxcp/constants.py b/pyxcp/constants.py index 8f3d8e5..f2945c0 100644 --- a/pyxcp/constants.py +++ b/pyxcp/constants.py @@ -1,8 +1,7 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import struct -from typing import Callable -from typing import NewType +from typing import Callable, NewType + PackerType = NewType("PackerType", Callable[[int], bytes]) UnpackerType = NewType("UnpackerType", Callable[[bytes], int]) @@ -10,39 +9,39 @@ def makeBytePacker(byteorder: str = "@") -> PackerType: """""" - return struct.Struct("{}B".format(byteorder)).pack + return struct.Struct(f"{byteorder}B").pack def makeByteUnpacker(byteorder: str = "@") -> UnpackerType: """""" - return struct.Struct("{}B".format(byteorder)).unpack + return struct.Struct(f"{byteorder}B").unpack def makeWordPacker(byteorder: str = "@") -> PackerType: """""" - return struct.Struct("{}H".format(byteorder)).pack + return struct.Struct(f"{byteorder}H").pack def makeWordUnpacker(byteorder: str = "@") -> UnpackerType: """""" - return struct.Struct("{}H".format(byteorder)).unpack + return struct.Struct(f"{byteorder}H").unpack def makeDWordPacker(byteorder: str = "@") -> PackerType: """""" - return struct.Struct("{}I".format(byteorder)).pack + return struct.Struct(f"{byteorder}I").pack def makeDWordUnpacker(byteorder: str = "@") -> UnpackerType: """""" - return struct.Struct("{}I".format(byteorder)).unpack + return struct.Struct(f"{byteorder}I").unpack def makeDLongPacker(byteorder: str = "@") -> PackerType: """""" - return struct.Struct("{}Q".format(byteorder)).pack + return struct.Struct(f"{byteorder}Q").pack def makeDLongUnpacker(byteorder: str = "@") -> UnpackerType: """""" - return struct.Struct("{}Q".format(byteorder)).unpack + return struct.Struct(f"{byteorder}Q").unpack diff --git a/pyxcp/cpp_ext/__init__.py b/pyxcp/cpp_ext/__init__.py index b49713c..22473f2 100644 --- a/pyxcp/cpp_ext/__init__.py +++ b/pyxcp/cpp_ext/__init__.py @@ -1,3 +1 @@ -from .cpp_ext import Bin -from .cpp_ext import DaqList -from .cpp_ext import McObject +from .cpp_ext import Bin, DaqList, McObject # noqa: F401 diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index fc07ddc..203e190 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -1,26 +1,15 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -import struct -from collections import defaultdict -from dataclasses import dataclass -from dataclasses import field + + from pprint import pprint -from typing import Any -from typing import Callable -from typing import Dict -from typing import List -from typing import Optional -from typing import Tuple +from typing import Callable, List, Optional, Tuple from pyxcp import types from pyxcp.cpp_ext import DaqList from pyxcp.daq_stim.optimize import make_continuous_blocks -from pyxcp.daq_stim.optimize import McObject from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing from pyxcp.recorder import DAQParser as _DAQParser from pyxcp.recorder import MeasurementParameters -from pyxcp.recorder import XcpLogFileReader -from pyxcp.types import FrameCategory DAQ_ID_FIELD_SIZE = { diff --git a/pyxcp/daq_stim/optimize/__init__.py b/pyxcp/daq_stim/optimize/__init__.py index ddee368..e0ebf40 100644 --- a/pyxcp/daq_stim/optimize/__init__.py +++ b/pyxcp/daq_stim/optimize/__init__.py @@ -1,16 +1,9 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Optimize data-structures like memory sections.""" -from collections import defaultdict -from dataclasses import dataclass -from dataclasses import field + from itertools import groupby from operator import attrgetter -from typing import Any -from typing import Dict from typing import List -from typing import Tuple -from typing import Union from pyxcp.cpp_ext import McObject diff --git a/pyxcp/daq_stim/optimize/binpacking.py b/pyxcp/daq_stim/optimize/binpacking.py index 9bed51c..6dea443 100644 --- a/pyxcp/daq_stim/optimize/binpacking.py +++ b/pyxcp/daq_stim/optimize/binpacking.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Bin-packing algorithms. """ -from typing import List -from typing import Optional +from typing import List, Optional from pyxcp.cpp_ext import Bin diff --git a/pyxcp/dllif.py b/pyxcp/dllif.py index 3191535..bec6bc2 100644 --- a/pyxcp/dllif.py +++ b/pyxcp/dllif.py @@ -1,11 +1,10 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import binascii import ctypes import enum import platform import re -import subprocess +import subprocess # nosec import sys from pathlib import Path @@ -34,7 +33,7 @@ class SeedNKeyError(Exception): elif bwidth == "32bit": use_ctypes = True else: - raise RuntimeError("Platform '{}' currently not supported.".format(sys.platform)) + raise RuntimeError(f"Platform '{sys.platform}' currently not supported.") def getKey(logger, dllName: str, privilege: int, seed: str, assume_same_bit_width: bool): @@ -74,7 +73,7 @@ def getKey(logger, dllName: str, privilege: int, seed: str, assume_same_bit_widt [LOADER, dllName, str(privilege), binascii.hexlify(seed).decode("ascii")], stdout=subprocess.PIPE, shell=False, - ) + ) # nosec except FileNotFoundError as exc: logger.error(f"Could not find executable '{LOADER}' -- {exc}") return (SeedNKeyResult.ERR_COULD_NOT_LOAD_DLL, None) diff --git a/pyxcp/errormatrix.py b/pyxcp/errormatrix.py index aa52345..c58ce47 100644 --- a/pyxcp/errormatrix.py +++ b/pyxcp/errormatrix.py @@ -1,12 +1,11 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Types and structures to support error-handling as specified by XCP. """ import enum from collections import namedtuple -from pyxcp.types import Command -from pyxcp.types import XcpError +from pyxcp.types import Command, XcpError + Handler = namedtuple("Handler", "preAction action") diff --git a/pyxcp/examples/xcp_policy.py b/pyxcp/examples/xcp_policy.py index 85d1f04..edcb497 100644 --- a/pyxcp/examples/xcp_policy.py +++ b/pyxcp/examples/xcp_policy.py @@ -1,12 +1,11 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Demostrates how to use frame recording policies including recorder extension. """ from pprint import pprint from pyxcp.cmdline import ArgumentParser -from pyxcp.transport.base import FrameRecorderPolicy -from pyxcp.transport.base import StdoutPolicy +from pyxcp.transport.base import FrameRecorderPolicy, StdoutPolicy # noqa: F401 + ap = ArgumentParser(description="pyXCP frame recording policy example.") @@ -34,7 +33,7 @@ from pyxcp.utils import hexDump try: - import pandas + import pandas # noqa: F401 except ImportError: has_pandas = False else: diff --git a/pyxcp/examples/xcp_read_benchmark.py b/pyxcp/examples/xcp_read_benchmark.py index 6ed04aa..fc08ce5 100644 --- a/pyxcp/examples/xcp_read_benchmark.py +++ b/pyxcp/examples/xcp_read_benchmark.py @@ -1,14 +1,14 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Very basic hello-world example. """ import time -from pyxcp.cmdline import ArgumentParser import matplotlib.pyplot as plt -import numpy as np import seaborn as sns +from pyxcp.cmdline import ArgumentParser + + sns.set() @@ -22,7 +22,7 @@ ys = [] x.connect() for ctoSize in range(8, 64 + 4, 4): - print("CTO-Size: {}".format(ctoSize)) + print(f"CTO-Size: {ctoSize}") xs.append(ctoSize) start = time.perf_counter() for _ in range(ITERATIONS): @@ -30,7 +30,7 @@ data = x.fetch(LENGTH, ctoSize) et = time.perf_counter() - start ys.append(et) - print("CTO size: {:-3} -- elapsed time {:-3.04}".format(ctoSize, et)) + print(f"CTO size: {ctoSize:-3} -- elapsed time {et:-3.04}") x.disconnect() plt.plot(xs, ys) plt.show() diff --git a/pyxcp/examples/xcp_skel.py b/pyxcp/examples/xcp_skel.py index 10f9cf4..8297889 100644 --- a/pyxcp/examples/xcp_skel.py +++ b/pyxcp/examples/xcp_skel.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Use this as a copy-and-paste template for your own scripts. """ -from pprint import pprint from pyxcp.cmdline import ArgumentParser diff --git a/pyxcp/examples/xcp_unlock.py b/pyxcp/examples/xcp_unlock.py index 080d82b..c603265 100644 --- a/pyxcp/examples/xcp_unlock.py +++ b/pyxcp/examples/xcp_unlock.py @@ -1,9 +1,9 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Very basic hello-world example. """ from pyxcp.cmdline import ArgumentParser + """ """ diff --git a/pyxcp/examples/xcp_user_supplied_driver.py b/pyxcp/examples/xcp_user_supplied_driver.py index b8a6521..d81e637 100644 --- a/pyxcp/examples/xcp_user_supplied_driver.py +++ b/pyxcp/examples/xcp_user_supplied_driver.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """User supplied CAN driver. Run as: diff --git a/pyxcp/examples/xcphello.py b/pyxcp/examples/xcphello.py index e370fe3..8b49c5a 100644 --- a/pyxcp/examples/xcphello.py +++ b/pyxcp/examples/xcphello.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Very basic hello-world example. """ from pprint import pprint @@ -7,6 +6,7 @@ from pyxcp.cmdline import ArgumentParser from pyxcp.utils import decode_bytes + daq_info = False diff --git a/pyxcp/examples/xcphello_recorder.py b/pyxcp/examples/xcphello_recorder.py index 2bc1774..647f7bf 100644 --- a/pyxcp/examples/xcphello_recorder.py +++ b/pyxcp/examples/xcphello_recorder.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Very basic hello-world example. """ from pprint import pprint @@ -9,6 +8,7 @@ from pyxcp.transport import FrameRecorderPolicy from pyxcp.utils import decode_bytes + daq_info = False diff --git a/pyxcp/master/__init__.py b/pyxcp/master/__init__.py index b9ffe22..eee88b4 100644 --- a/pyxcp/master/__init__.py +++ b/pyxcp/master/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Lowlevel API reflecting available XCP services .. note:: For technical reasons the API is split into two parts; @@ -7,4 +6,4 @@ .. [1] XCP Specification, Part 2 - Protocol Layer Specification """ -from .master import Master +from .master import Master # noqa: F401 diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index 2c2a82c..b90c4ce 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Implements error-handling according to XCP spec. """ import functools -import os import threading import time import types @@ -12,18 +10,14 @@ import can from pyxcp.config import application -from pyxcp.errormatrix import Action -from pyxcp.errormatrix import ERROR_MATRIX -from pyxcp.errormatrix import PreAction -from pyxcp.types import COMMAND_CATEGORIES -from pyxcp.types import XcpError -from pyxcp.types import XcpResponseError -from pyxcp.types import XcpTimeoutError +from pyxcp.errormatrix import ERROR_MATRIX, Action, PreAction +from pyxcp.types import COMMAND_CATEGORIES, XcpError, XcpResponseError, XcpTimeoutError + handle_errors = True # enable/disable XCP error-handling. -class SingletonBase(object): +class SingletonBase: _lock = threading.Lock() def __new__(cls, *args, **kws): @@ -32,7 +26,7 @@ def __new__(cls, *args, **kws): try: cls._lock.acquire() if not hasattr(cls, "_instance"): - cls._instance = super(SingletonBase, cls).__new__(cls) + cls._instance = super().__new__(cls) finally: cls._lock.release() return cls._instance @@ -90,8 +84,7 @@ def getActions(service, error_code): def actionIter(actions): """Iterate over action from :file:`errormatrix.py`""" if isinstance(actions, (tuple, list)): - for item in actions: - yield item + yield from actions else: yield actions diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 7cdf6c2..e7364b1 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Lowlevel API reflecting available XCP services. .. note:: For technical reasons the API is split into two parts; @@ -8,40 +7,28 @@ .. [1] XCP Specification, Part 2 - Protocol Layer Specification """ import functools -import logging import struct import traceback import warnings from time import sleep -from typing import Callable -from typing import Collection -from typing import Dict -from typing import List -from typing import Optional -from typing import Union - -from .stim import DaqEventInfo -from .stim import get_policy_lock -from .stim import get_writer_lock -from .stim import Stim -from pyxcp import checksum -from pyxcp import types -from pyxcp.constants import makeBytePacker -from pyxcp.constants import makeByteUnpacker -from pyxcp.constants import makeDLongPacker -from pyxcp.constants import makeDLongUnpacker -from pyxcp.constants import makeDWordPacker -from pyxcp.constants import makeDWordUnpacker -from pyxcp.constants import makeWordPacker -from pyxcp.constants import makeWordUnpacker -from pyxcp.constants import PackerType -from pyxcp.constants import UnpackerType -from pyxcp.master.errorhandler import disable_error_handling -from pyxcp.master.errorhandler import wrapped +from typing import Callable, Collection, Dict, List, Optional + +from pyxcp import checksum, types +from pyxcp.constants import ( + makeBytePacker, + makeByteUnpacker, + makeDLongPacker, + makeDLongUnpacker, + makeDWordPacker, + makeDWordUnpacker, + makeWordPacker, + makeWordUnpacker, +) +from pyxcp.master.errorhandler import disable_error_handling, wrapped from pyxcp.transport.base import createTransport -from pyxcp.utils import decode_bytes -from pyxcp.utils import delay -from pyxcp.utils import SHORT_SLEEP +from pyxcp.utils import SHORT_SLEEP, decode_bytes, delay + +from .stim import DaqEventInfo, Stim, get_policy_lock, get_writer_lock def broadcasted(func: Callable): @@ -53,7 +40,7 @@ class SlaveProperties(dict): """Container class for fixed parameters, like byte-order, maxCTO, ...""" def __init__(self, *args, **kws): - super(SlaveProperties, self).__init__(*args, **kws) + super().__init__(*args, **kws) def __getattr__(self, name): return self[name] @@ -571,7 +558,7 @@ def fetch(self, length: int, limitPayload: int = None): # TODO: pull address is not included because of services implicitly setting address information like :meth:`getID` . """ if limitPayload and limitPayload < 8: - raise ValueError("Payload must be at least 8 bytes - given: {}".format(limitPayload)) + raise ValueError(f"Payload must be at least 8 bytes - given: {limitPayload}") slaveBlockMode = self.slaveProperties.slaveBlockMode if slaveBlockMode: @@ -1072,7 +1059,7 @@ def writeDaqMultiple(self, daqElements): daqElements : list of `dict` containing the following keys: *bitOffset*, *size*, *address*, *addressExt*. """ if len(daqElements) > self.slaveProperties.maxWriteDaqMultipleElements: - raise ValueError("At most {} daqElements are permitted.".format(self.slaveProperties.maxWriteDaqMultipleElements)) + raise ValueError(f"At most {self.slaveProperties.maxWriteDaqMultipleElements} daqElements are permitted.") data = bytearray() data.append(len(daqElements)) @@ -1660,11 +1647,11 @@ def verify(self, addr, length): """ self.setMta(addr) cs = self.buildChecksum(length) - self.logger.debug("BuildChecksum return'd: 0x{:08X} [{}]".format(cs.checksum, cs.checksumType)) + self.logger.debug(f"BuildChecksum return'd: 0x{cs.checksum:08X} [{cs.checksumType}]") self.setMta(addr) data = self.fetch(length) cc = checksum.check(data, cs.checksumType) - self.logger.debug("Our checksum : 0x{:08X}".format(cc)) + self.logger.debug(f"Our checksum : 0x{cc:08X}") return cs.checksum == cc def getDaqInfo(self): @@ -1791,7 +1778,8 @@ def cond_unlock(self, resources=None): In case of DLL related issues. """ import re - from pyxcp.dllif import getKey, SeedNKeyResult, SeedNKeyError + + from pyxcp.dllif import SeedNKeyError, SeedNKeyResult, getKey MAX_PAYLOAD = self.slaveProperties["maxCto"] - 2 @@ -1812,7 +1800,7 @@ def cond_unlock(self, resources=None): resource_names = [r.lower() for r in re.split(r"[ ,]", resources) if r] for name in resource_names: if name not in types.RESOURCE_VALUES: - raise ValueError("Invalid resource name '{}'.".format(name)) + raise ValueError(f"Invalid resource name '{name}'.") if not protection_status[name]: continue resource_value = types.RESOURCE_VALUES[name] @@ -1852,7 +1840,7 @@ def cond_unlock(self, resources=None): offset += key_length self.unlock(key_length, data) else: - raise SeedNKeyError("SeedAndKey DLL returned: {}".format(SeedNKeyResult(result).name)) + raise SeedNKeyError(f"SeedAndKey DLL returned: {SeedNKeyResult(result).name}") def identifier(self, id_value: int) -> str: """Return the identifier for the given value. diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index ddf8c97..95485a3 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -1,13 +1,12 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """XCP Frame Recording Facility. """ from dataclasses import dataclass -from enum import IntEnum from typing import Union from pyxcp.types import FrameCategory + try: import pandas as pd except ImportError: @@ -17,6 +16,7 @@ import pyxcp.recorder.rekorder as rec + MeasurementParameters = rec._MeasurementParameters DAQParser = rec.DAQParser diff --git a/pyxcp/recorder/reco.py b/pyxcp/recorder/reco.py index 8404a6f..1e58ebf 100644 --- a/pyxcp/recorder/reco.py +++ b/pyxcp/recorder/reco.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Raw XCP traffic recorder. Data is stored in LZ4 compressed containers. @@ -15,8 +14,6 @@ """ import enum import mmap -import os -import pathlib import struct from collections import namedtuple @@ -27,7 +24,7 @@ MAGIC = b"ASAMINT::XCP_RAW" -FILE_HEADER_STRUCT = struct.Struct("<{:d}sHHHLLLL".format(len(MAGIC))) +FILE_HEADER_STRUCT = struct.Struct(f"<{len(MAGIC):d}sHHHLLLL") FileHeader = namedtuple( "FileHeader", "magic hdr_size version options num_containers record_count size_compressed size_uncompressed", @@ -82,7 +79,7 @@ def __init__( ): self._is_closed = True try: - self._of = open("{}{}".format(file_name, FILE_EXTENSION), "w+b") + self._of = open(f"{file_name}{FILE_EXTENSION}", "w+b") except Exception: raise else: @@ -161,7 +158,7 @@ def set(self, address: int, data: bytes): try: self._mapping[address : address + length] = data except IndexError: - raise XcpLogFileCapacityExceededError("Maximum file size of {} MBytes exceeded.".format(self.prealloc)) from None + raise XcpLogFileCapacityExceededError(f"Maximum file size of {self.prealloc} MBytes exceeded.") from None def _write_header( self, @@ -201,7 +198,7 @@ class XcpLogFileReader: def __init__(self, file_name): self._is_closed = True try: - self._log_file = open("{}{}".format(file_name, FILE_EXTENSION), "r+b") + self._log_file = open(f"{file_name}{FILE_EXTENSION}", "r+b") except Exception: raise else: @@ -218,7 +215,7 @@ def __init__(self, file_name): self.total_size_uncompressed, ) = FILE_HEADER_STRUCT.unpack(self.get(0, FILE_HEADER_STRUCT.size)) if magic != MAGIC: - raise XcpLogFileParseError("Invalid file magic: '{}'.".format(magic)) + raise XcpLogFileParseError(f"Invalid file magic: '{magic}'.") def __del__(self): if not self._is_closed: diff --git a/pyxcp/recorder/rekorder.cpp b/pyxcp/recorder/rekorder.cpp index 2828985..6e9a665 100644 --- a/pyxcp/recorder/rekorder.cpp +++ b/pyxcp/recorder/rekorder.cpp @@ -3,28 +3,24 @@ #include "rekorder.hpp" - -void some_records(XcpLogFileWriter& writer) -{ - const auto COUNT = 1024 * 100 * 5; - unsigned filler = 0x00; - char buffer[1024]; +void some_records(XcpLogFileWriter& writer) { + const auto COUNT = 1024 * 100 * 5; + unsigned filler = 0x00; + char buffer[1024]; for (auto idx = 0; idx < COUNT; ++idx) { - auto fr = frame_header_t{}; - fr.category = 1; - fr.counter = idx; + auto fr = frame_header_t{}; + fr.category = 1; + fr.counter = idx; fr.timestamp = std::clock(); - fr.length = 10 + (rand() % 240); - filler = (filler + 1) % 16; + fr.length = 10 + (rand() % 240); + filler = (filler + 1) % 16; memset(buffer, filler, fr.length); - writer.add_frame(fr.category, fr.counter, fr.timestamp, fr.length, std::bit_cast(&buffer)); + writer.add_frame(fr.category, fr.counter, fr.timestamp, fr.length, std::bit_cast(&buffer)); } } - -int main() -{ +int main() { srand(42); printf("\nWRITER\n"); @@ -49,15 +45,15 @@ int main() printf("size/uncompressed: %u\n", header.size_uncompressed); printf("compression ratio: %.2f\n", static_cast(header.size_uncompressed) / static_cast(header.size_compressed)); - while (true) { - const auto& frames = reader.next_block(); - if (!frames) { - break; - } - for (const auto& frame: frames.value()) { + while (true) { + const auto& frames = reader.next_block(); + if (!frames) { + break; + } + for (const auto& frame : frames.value()) { auto const& [category, counter, timestamp, length, payload] = frame; } - } + } printf("---\n"); printf("Finished.\n"); } diff --git a/pyxcp/recorder/setup.py b/pyxcp/recorder/setup.py index 99b6916..d60938b 100644 --- a/pyxcp/recorder/setup.py +++ b/pyxcp/recorder/setup.py @@ -1,17 +1,18 @@ -import os -import subprocess - -from distutils.core import Extension +import subprocess # nosec from distutils.core import setup -from pybind11.setup_helpers import build_ext -from pybind11.setup_helpers import naive_recompile -from pybind11.setup_helpers import ParallelCompile -from pybind11.setup_helpers import Pybind11Extension + +from pybind11.setup_helpers import ( + ParallelCompile, + Pybind11Extension, + build_ext, + naive_recompile, +) + # ParallelCompile("NPY_NUM_BUILD_JOBS").install() ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() -INCLUDE_DIRS = subprocess.getoutput("pybind11-config --include") +INCLUDE_DIRS = subprocess.getoutput("pybind11-config --include") # nosec # os.environ ["CFLAGS"] = '' diff --git a/pyxcp/recorder/test_reko.py b/pyxcp/recorder/test_reko.py index 5da321d..31cb746 100644 --- a/pyxcp/recorder/test_reko.py +++ b/pyxcp/recorder/test_reko.py @@ -1,8 +1,7 @@ from time import perf_counter -from pyxcp.recorder import FrameCategory -from pyxcp.recorder import XcpLogFileReader -from pyxcp.recorder import XcpLogFileWriter +from pyxcp.recorder import FrameCategory, XcpLogFileReader, XcpLogFileWriter + # Pre-allocate a 100MB file -- Note: due to the nature of memory-mapped files, this is a HARD limit. # Chunk size is 1MB (i.e. compression granularity). diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index dc6d6be..60c050d 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -393,7 +393,7 @@ auto requires_swap(std::uint8_t byte_order) { class UnfolderBase { public: - explicit UnfolderBase(const MeasurementParameters& params) : m_params(params) noexcept { + explicit UnfolderBase(const MeasurementParameters& params) : m_params(params) { create_state_vars(params); } @@ -552,7 +552,7 @@ class DAQParser { m_unfolder->feed(timestamp, payload); } - virtual void post_setup() noexcept { + virtual void post_setup() { } void finalize() noexcept { diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index cde22dd..e3ec324 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -10,7 +10,6 @@ namespace py = pybind11; using namespace pybind11::literals; - class PyDAQParser : public DAQParser { public: diff --git a/pyxcp/scripts/pyxcp_probe_can_drivers.py b/pyxcp/scripts/pyxcp_probe_can_drivers.py index e293472..143779c 100644 --- a/pyxcp/scripts/pyxcp_probe_can_drivers.py +++ b/pyxcp/scripts/pyxcp_probe_can_drivers.py @@ -1,6 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -from pprint import pprint import can diff --git a/pyxcp/scripts/xcp_fetch_a2l.py b/pyxcp/scripts/xcp_fetch_a2l.py index fd53649..3aa196a 100644 --- a/pyxcp/scripts/xcp_fetch_a2l.py +++ b/pyxcp/scripts/xcp_fetch_a2l.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Fetch A2L file from XCP slave (if supported). """ import sys diff --git a/pyxcp/scripts/xcp_id_scanner.py b/pyxcp/scripts/xcp_id_scanner.py index e6f91cd..9f365ce 100644 --- a/pyxcp/scripts/xcp_id_scanner.py +++ b/pyxcp/scripts/xcp_id_scanner.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Scan for available IDs. """ from pprint import pprint diff --git a/pyxcp/scripts/xcp_info.py b/pyxcp/scripts/xcp_info.py index 7373240..87b12e0 100644 --- a/pyxcp/scripts/xcp_info.py +++ b/pyxcp/scripts/xcp_info.py @@ -1,11 +1,11 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Very basic hello-world example. """ from pprint import pprint from pyxcp.cmdline import ArgumentParser + daq_info = False diff --git a/pyxcp/stim/setup.py b/pyxcp/stim/setup.py index 3d3078d..23caab7 100644 --- a/pyxcp/stim/setup.py +++ b/pyxcp/stim/setup.py @@ -1,12 +1,13 @@ -import os -import subprocess - -from distutils.core import Extension +import subprocess # nosec from distutils.core import setup -from pybind11.setup_helpers import build_ext -from pybind11.setup_helpers import naive_recompile -from pybind11.setup_helpers import ParallelCompile -from pybind11.setup_helpers import Pybind11Extension + +from pybind11.setup_helpers import ( + ParallelCompile, + Pybind11Extension, + build_ext, + naive_recompile, +) + # ParallelCompile("NPY_NUM_BUILD_JOBS").install() ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() diff --git a/pyxcp/tests/test_binpacking.py b/pyxcp/tests/test_binpacking.py index abd669a..201393e 100644 --- a/pyxcp/tests/test_binpacking.py +++ b/pyxcp/tests/test_binpacking.py @@ -1,9 +1,7 @@ import pytest -from pyxcp.daq_stim.optimize import make_continuous_blocks -from pyxcp.daq_stim.optimize import McObject -from pyxcp.daq_stim.optimize.binpacking import Bin -from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing +from pyxcp.daq_stim.optimize import McObject, make_continuous_blocks +from pyxcp.daq_stim.optimize.binpacking import Bin, first_fit_decreasing @pytest.fixture diff --git a/pyxcp/tests/test_can.py b/pyxcp/tests/test_can.py index 687bb6f..568ab71 100644 --- a/pyxcp/tests/test_can.py +++ b/pyxcp/tests/test_can.py @@ -1,14 +1,16 @@ import pytest -from pyxcp.transport.can import calculateFilter -from pyxcp.transport.can import CAN_EXTENDED_ID -from pyxcp.transport.can import Identifier -from pyxcp.transport.can import IdentifierOutOfRangeError -from pyxcp.transport.can import isExtendedIdentifier -from pyxcp.transport.can import MAX_11_BIT_IDENTIFIER -from pyxcp.transport.can import MAX_29_BIT_IDENTIFIER -from pyxcp.transport.can import setDLC -from pyxcp.transport.can import stripIdentifier +from pyxcp.transport.can import ( + CAN_EXTENDED_ID, + MAX_11_BIT_IDENTIFIER, + MAX_29_BIT_IDENTIFIER, + Identifier, + IdentifierOutOfRangeError, + calculateFilter, + isExtendedIdentifier, + setDLC, + stripIdentifier, +) def testSet0(): @@ -223,9 +225,9 @@ def test_identifier_repr_works2(capsys): def test_identifier_repr_does_the_trick1(capsys): i = Identifier(101) - assert eval(repr(i)) == Identifier(101) + assert eval(repr(i)) == Identifier(101) # nosec def test_identifier_repr_does_the_trick2(capsys): i = Identifier(101 | CAN_EXTENDED_ID) - assert eval(repr(i)) == Identifier(101 | CAN_EXTENDED_ID) + assert eval(repr(i)) == Identifier(101 | CAN_EXTENDED_ID) # nosec diff --git a/pyxcp/tests/test_checksum.py b/pyxcp/tests/test_checksum.py index 2a258d7..eb34c50 100644 --- a/pyxcp/tests/test_checksum.py +++ b/pyxcp/tests/test_checksum.py @@ -2,6 +2,7 @@ from pyxcp import checksum + """ XCP_ADD_11 0x10 0x10 XCP_ADD_12 0x0F10 0x0F10 diff --git a/pyxcp/tests/test_config.py b/pyxcp/tests/test_config.py index b1c4951..59421aa 100644 --- a/pyxcp/tests/test_config.py +++ b/pyxcp/tests/test_config.py @@ -3,6 +3,7 @@ from pyxcp.config import readConfiguration + JSON = """{ "PORT": "COM10", "BITRATE": 38400, diff --git a/pyxcp/tests/test_daq.py b/pyxcp/tests/test_daq.py index 9700445..ec6619e 100644 --- a/pyxcp/tests/test_daq.py +++ b/pyxcp/tests/test_daq.py @@ -1,12 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -from unittest.mock import Mock -from unittest.mock import patch -from pyxcp.daq_stim import Daq -from pyxcp.daq_stim import DaqList - -# import pytest +from pyxcp.daq_stim import Daq, DaqList DAQ_INFO = { diff --git a/pyxcp/tests/test_master.py b/pyxcp/tests/test_master.py index d3d4d85..c951245 100644 --- a/pyxcp/tests/test_master.py +++ b/pyxcp/tests/test_master.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import struct import time from collections import deque diff --git a/pyxcp/tests/test_utils.py b/pyxcp/tests/test_utils.py index cf4ddbc..1e181f6 100644 --- a/pyxcp/tests/test_utils.py +++ b/pyxcp/tests/test_utils.py @@ -1,10 +1,6 @@ from sys import version_info -from pyxcp.utils import flatten -from pyxcp.utils import getPythonVersion -from pyxcp.utils import hexDump -from pyxcp.utils import PYTHON_VERSION -from pyxcp.utils import slicer +from pyxcp.utils import PYTHON_VERSION, flatten, getPythonVersion, hexDump, slicer def test_hexdump(capsys): diff --git a/pyxcp/timing.py b/pyxcp/timing.py index 0938d24..ff91270 100644 --- a/pyxcp/timing.py +++ b/pyxcp/timing.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import time diff --git a/pyxcp/transport/__init__.py b/pyxcp/transport/__init__.py index 36b5015..e652ca7 100644 --- a/pyxcp/transport/__init__.py +++ b/pyxcp/transport/__init__.py @@ -1,11 +1,10 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -from .base import FrameAcquisitionPolicy -from .base import FrameRecorderPolicy -from .base import LegacyFrameAcquisitionPolicy -from .base import NoOpPolicy -from .base import StdoutPolicy -from .can import Can -from .eth import Eth -from .sxi import SxI -from .usb_transport import Usb +from .base import FrameAcquisitionPolicy # noqa: F401 +from .base import FrameRecorderPolicy # noqa: F401 +from .base import LegacyFrameAcquisitionPolicy # noqa: F401 +from .base import NoOpPolicy # noqa: F401 +from .base import StdoutPolicy # noqa: F401 +from .can import Can # noqa: F401 +from .eth import Eth # noqa: F401 +from .sxi import SxI # noqa: F401 +from .usb_transport import Usb # noqa: F401 diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 9ad1cf0..5da6c9e 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -1,21 +1,16 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import abc import threading import typing from collections import deque from datetime import datetime -from enum import IntEnum -from time import perf_counter -from time import sleep -from time import time +from time import perf_counter, sleep, time import pyxcp.types as types + from ..recorder import XcpLogFileWriter from ..timing import Timing -from ..utils import flatten -from ..utils import hexDump -from ..utils import SHORT_SLEEP +from ..utils import SHORT_SLEEP, flatten, hexDump class FrameAcquisitionPolicy: diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index 3d949ba..2118376 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -1,22 +1,14 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ """ -import abc + import functools import operator from bisect import bisect_left from time import time -from typing import Any -from typing import Callable -from typing import Dict -from typing import List -from typing import Optional -from typing import Tuple -from typing import Union - -from can import CanError -from can import Message +from typing import Any, Dict, Optional + +from can import CanError, Message from can.interface import _get_class_for_interface from pyxcp.config import CAN_INTERFACE_MAP @@ -124,10 +116,10 @@ def __init__(self, raw_id: int): self._is_extended = isExtendedIdentifier(raw_id) if self._is_extended: if self._id > MAX_29_BIT_IDENTIFIER: - raise IdentifierOutOfRangeError("29-bit identifier '{}' is out of range".format(self._id)) + raise IdentifierOutOfRangeError(f"29-bit identifier '{self._id}' is out of range") else: if self._id > MAX_11_BIT_IDENTIFIER: - raise IdentifierOutOfRangeError("11-bit identifier '{}' is out of range".format(self._id)) + raise IdentifierOutOfRangeError(f"11-bit identifier '{self._id}' is out of range") @property def id(self) -> int: @@ -187,10 +179,10 @@ def __eq__(self, other): return (self.id == other.id) and (self.is_extended == other.is_extended) def __str__(self): - return "Identifier(id = 0x{:08x}, is_extended = {})".format(self.id, self.is_extended) + return f"Identifier(id = 0x{self.id:08x}, is_extended = {self.is_extended})" def __repr__(self): - return "Identifier(0x{:08x})".format(self.raw_id) + return f"Identifier(0x{self.raw_id:08x})" class Frame: @@ -229,7 +221,7 @@ def connect(self): } self.can_interface = self.can_interface_class(interface=self.interface_name, **self.parameters) self.can_interface.set_filters([can_filter]) - self.parent.logger.debug("Python-CAN driver: {} - {}]".format(self.interface_name, self.can_interface)) + self.parent.logger.debug(f"Python-CAN driver: {self.interface_name} - {self.can_interface}]") self.connected = True def close(self): diff --git a/pyxcp/transport/candriver/__init__.py b/pyxcp/transport/candriver/__init__.py index faa18be..4265cc3 100644 --- a/pyxcp/transport/candriver/__init__.py +++ b/pyxcp/transport/candriver/__init__.py @@ -1,2 +1 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- diff --git a/pyxcp/transport/candriver/pc_canalystii.py b/pyxcp/transport/candriver/pc_canalystii.py index 96be4a1..8d211f3 100644 --- a/pyxcp/transport/candriver/pc_canalystii.py +++ b/pyxcp/transport/candriver/pc_canalystii.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for CANalyst-II(+) by ZLG ZHIYUAN Electronics interfaces. """ @@ -24,4 +23,4 @@ class Canalystii(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(Canalystii, self).__init__(bustype="canalystii") + super().__init__(bustype="canalystii") diff --git a/pyxcp/transport/candriver/pc_etas.py b/pyxcp/transport/candriver/pc_etas.py index 3057cd6..96ee5e1 100644 --- a/pyxcp/transport/candriver/pc_etas.py +++ b/pyxcp/transport/candriver/pc_etas.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for ETAS BOA interfaces. """ @@ -22,4 +21,4 @@ class Etas(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(Etas, self).__init__(bustype="etas") + super().__init__(bustype="etas") diff --git a/pyxcp/transport/candriver/pc_gsusb.py b/pyxcp/transport/candriver/pc_gsusb.py index aa8e0bd..fb3c711 100644 --- a/pyxcp/transport/candriver/pc_gsusb.py +++ b/pyxcp/transport/candriver/pc_gsusb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for CAN driver for Geschwister Schneider USB/CAN devices and bytewerk.org candleLight USB CAN interfaces. """ @@ -20,4 +19,4 @@ class GsUsb(python_can.PythonCAN, can.CanInterfaceBase): """ def __init__(self): - super(GsUsb, self).__init__(bustype="gs_usb") + super().__init__(bustype="gs_usb") diff --git a/pyxcp/transport/candriver/pc_iscan.py b/pyxcp/transport/candriver/pc_iscan.py index 5a1734b..aa33b40 100644 --- a/pyxcp/transport/candriver/pc_iscan.py +++ b/pyxcp/transport/candriver/pc_iscan.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for isCAN from Thorsis Technologies GmbH. """ @@ -20,4 +19,4 @@ class IsCAN(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(IsCAN, self).__init__(bustype="iscan") + super().__init__(bustype="iscan") diff --git a/pyxcp/transport/candriver/pc_ixxat.py b/pyxcp/transport/candriver/pc_ixxat.py index 5522c16..e0bc0fa 100644 --- a/pyxcp/transport/candriver/pc_ixxat.py +++ b/pyxcp/transport/candriver/pc_ixxat.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for Ixxat interfaces. """ @@ -24,4 +23,4 @@ class Ixxat(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(Ixxat, self).__init__(bustype="ixxat") + super().__init__(bustype="ixxat") diff --git a/pyxcp/transport/candriver/pc_kvaser.py b/pyxcp/transport/candriver/pc_kvaser.py index 78104d1..721a981 100644 --- a/pyxcp/transport/candriver/pc_kvaser.py +++ b/pyxcp/transport/candriver/pc_kvaser.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for Kvaser interfaces. """ @@ -36,4 +35,4 @@ class Kvaser(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(Kvaser, self).__init__(bustype="kvaser") + super().__init__(bustype="kvaser") diff --git a/pyxcp/transport/candriver/pc_neovi.py b/pyxcp/transport/candriver/pc_neovi.py index 80da5a0..bce8d43 100644 --- a/pyxcp/transport/candriver/pc_neovi.py +++ b/pyxcp/transport/candriver/pc_neovi.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for ICS NeoVi interfaces. """ @@ -28,4 +27,4 @@ class Neovi(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(Neovi, self).__init__(bustype="neovi") + super().__init__(bustype="neovi") diff --git a/pyxcp/transport/candriver/pc_nican.py b/pyxcp/transport/candriver/pc_nican.py index 8ee8a65..4a526bd 100644 --- a/pyxcp/transport/candriver/pc_nican.py +++ b/pyxcp/transport/candriver/pc_nican.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for National Instruments interfaces. """ @@ -20,4 +19,4 @@ class NiCan(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(NiCan, self).__init__(bustype="nican") + super().__init__(bustype="nican") diff --git a/pyxcp/transport/candriver/pc_nixnet.py b/pyxcp/transport/candriver/pc_nixnet.py index 580ca3e..2e07e2c 100644 --- a/pyxcp/transport/candriver/pc_nixnet.py +++ b/pyxcp/transport/candriver/pc_nixnet.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for National Instruments xnet interfaces. """ @@ -20,4 +19,4 @@ class NiXnet(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(NiXnet, self).__init__(bustype="nixnet") + super().__init__(bustype="nixnet") diff --git a/pyxcp/transport/candriver/pc_pcan.py b/pyxcp/transport/candriver/pc_pcan.py index fd5f109..21e23b0 100644 --- a/pyxcp/transport/candriver/pc_pcan.py +++ b/pyxcp/transport/candriver/pc_pcan.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for Peak System interfaces. """ @@ -22,4 +21,4 @@ class PCan(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(PCan, self).__init__(bustype="pcan") + super().__init__(bustype="pcan") diff --git a/pyxcp/transport/candriver/pc_seeed.py b/pyxcp/transport/candriver/pc_seeed.py index 3d7e29f..0611ffd 100644 --- a/pyxcp/transport/candriver/pc_seeed.py +++ b/pyxcp/transport/candriver/pc_seeed.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for USB-CAN Analyzer by Seeed Studio interfaces. """ @@ -22,7 +21,7 @@ class Seeed(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(Seeed, self).__init__(bustype="seeedstudio") + super().__init__(bustype="seeedstudio") # from can.interfaces.seeedstudio import SeeedBus diff --git a/pyxcp/transport/candriver/pc_serial.py b/pyxcp/transport/candriver/pc_serial.py index cdeb655..7f23dfc 100644 --- a/pyxcp/transport/candriver/pc_serial.py +++ b/pyxcp/transport/candriver/pc_serial.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver serial port connected interfaces. """ @@ -24,4 +23,4 @@ class Serial(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(Serial, self).__init__(bustype="serial") + super().__init__(bustype="serial") diff --git a/pyxcp/transport/candriver/pc_slcan.py b/pyxcp/transport/candriver/pc_slcan.py index 828c83f..91b8521 100644 --- a/pyxcp/transport/candriver/pc_slcan.py +++ b/pyxcp/transport/candriver/pc_slcan.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for CAN over Serial (like Lawicel) interfaces. """ @@ -26,4 +25,4 @@ class SlCan(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(SlCan, self).__init__(bustype="slcan") + super().__init__(bustype="slcan") diff --git a/pyxcp/transport/candriver/pc_socketcan.py b/pyxcp/transport/candriver/pc_socketcan.py index 8ad8be1..58baf6e 100644 --- a/pyxcp/transport/candriver/pc_socketcan.py +++ b/pyxcp/transport/candriver/pc_socketcan.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for Linux SocketCAN interfaces. """ @@ -20,4 +19,4 @@ class SocketCAN(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(SocketCAN, self).__init__(bustype="socketcan") + super().__init__(bustype="socketcan") diff --git a/pyxcp/transport/candriver/pc_systec.py b/pyxcp/transport/candriver/pc_systec.py index 5cb262a..3fcf7c4 100644 --- a/pyxcp/transport/candriver/pc_systec.py +++ b/pyxcp/transport/candriver/pc_systec.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for Systec interfaces. """ @@ -26,4 +25,4 @@ class Systec(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(Systec, self).__init__(bustype="systec") + super().__init__(bustype="systec") diff --git a/pyxcp/transport/candriver/pc_usb2can.py b/pyxcp/transport/candriver/pc_usb2can.py index 1c1a910..ba73b60 100644 --- a/pyxcp/transport/candriver/pc_usb2can.py +++ b/pyxcp/transport/candriver/pc_usb2can.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for 8devices USB2CAN interfaces. """ @@ -27,4 +26,4 @@ class Usb2Can(python_can.PythonCAN, can.CanInterfaceBase): """ def __init__(self): - super(Usb2Can, self).__init__(bustype="usb2can") + super().__init__(bustype="usb2can") diff --git a/pyxcp/transport/candriver/pc_vector.py b/pyxcp/transport/candriver/pc_vector.py index cb0f631..ef795c4 100644 --- a/pyxcp/transport/candriver/pc_vector.py +++ b/pyxcp/transport/candriver/pc_vector.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ python-can driver for Vector Informatik interfaces. """ @@ -31,4 +30,4 @@ class Vector(python_can.PythonCAN, can.CanInterfaceBase): } def __init__(self): - super(Vector, self).__init__(bustype="vector") + super().__init__(bustype="vector") diff --git a/pyxcp/transport/candriver/python_can.py b/pyxcp/transport/candriver/python_can.py index eb0094d..9e6264a 100644 --- a/pyxcp/transport/candriver/python_can.py +++ b/pyxcp/transport/candriver/python_can.py @@ -1,10 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Support for python-can - github.com/hardbyte/python-can """ -from typing import Optional -from can.interface import _get_class_for_interface - -# import pyxcp.transport.can as can +from can.interface import _get_class_for_interface # noqa: F401 diff --git a/pyxcp/transport/cxx_ext/CMakeLists.txt b/pyxcp/transport/cxx_ext/CMakeLists.txt index 422cce1..1872ed8 100644 --- a/pyxcp/transport/cxx_ext/CMakeLists.txt +++ b/pyxcp/transport/cxx_ext/CMakeLists.txt @@ -48,4 +48,3 @@ target_include_directories( # ${eth_booster_SOURCE_DIR}/${_PS} ) target_link_libraries(blocking_client eth ${ADD_LIBS}) - diff --git a/pyxcp/transport/cxx_ext/setup.py b/pyxcp/transport/cxx_ext/setup.py index cf16c9c..199796b 100644 --- a/pyxcp/transport/cxx_ext/setup.py +++ b/pyxcp/transport/cxx_ext/setup.py @@ -1,16 +1,15 @@ import os -import subprocess +import subprocess # nosec import sys - -from distutils.core import Extension from distutils.core import setup -from pybind11.setup_helpers import build_ext -from pybind11.setup_helpers import Pybind11Extension + +from pybind11.setup_helpers import Pybind11Extension, build_ext + try: - INCLUDE_DIRS = subprocess.getoutput("pybind11-config --include") + INCLUDE_DIRS = subprocess.getoutput("pybind11-config --include") # nosec except Exception as e: - print("Error while executing pybind11-config ('{}').\npybind11 probably not installed?".format(str(e))) + print(f"Error while executing pybind11-config ('{str(e)}').\npybind11 probably not installed?") sys.exit(1) pf = sys.platform @@ -19,7 +18,7 @@ elif pf.startswith("linux"): LIBS = ["pthread", "rt"] else: - raise RuntimeError("Platform '{}' currently not supported.".format(pf)) + raise RuntimeError(f"Platform '{pf}' currently not supported.") os.environ["CFLAGS"] = "" diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 8ddb753..8f06e83 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -1,17 +1,15 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import selectors import socket import struct import threading from collections import deque -from time import perf_counter -from time import sleep -from time import time +from time import sleep, time from pyxcp.transport.base import BaseTransport from pyxcp.utils import SHORT_SLEEP + DEFAULT_XCP_PORT = 5555 RECV_SIZE = 8196 @@ -24,7 +22,7 @@ class Eth(BaseTransport): HEADER_SIZE = HEADER.size def __init__(self, config=None, policy=None): - super(Eth, self).__init__(config, policy) + super().__init__(config, policy) self.load_config(config) self.host = self.config.host self.port = self.config.port diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index 5333fff..c63d9c3 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import struct -from time import perf_counter from time import time import serial @@ -18,7 +16,7 @@ class SxI(BaseTransport): HEADER_SIZE = HEADER.size def __init__(self, config=None, policy=None): - super(SxI, self).__init__(config, policy) + super().__init__(config, policy) self.load_config(config) self.port_name = self.config.port self.baudrate = self.config.bitrate @@ -30,7 +28,7 @@ def __del__(self): self.closeConnection() def connect(self): - self.logger.debug("Trying to open serial comm_port {}.".format(self.port_name)) + self.logger.debug(f"Trying to open serial comm_port {self.port_name}.") try: self.comm_port = serial.Serial() self.comm_port.port = self.port_name @@ -43,7 +41,7 @@ def connect(self): except serial.SerialException as e: self.logger.critical(f"{e}") raise - self.logger.info("Serial comm_port openend as '{}' @ {} Bits/Sec.".format(self.comm_port.portstr, self.baudrate)) + self.logger.info(f"Serial comm_port openend as '{self.comm_port.portstr}' @ {self.baudrate} Bits/Sec.") self.startListener() def output(self, enable): diff --git a/pyxcp/transport/usb_transport.py b/pyxcp/transport/usb_transport.py index faefd85..00b2d80 100644 --- a/pyxcp/transport/usb_transport.py +++ b/pyxcp/transport/usb_transport.py @@ -1,12 +1,9 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import struct import threading from array import array from collections import deque -from time import perf_counter -from time import sleep -from time import time +from time import perf_counter, sleep, time import usb.core import usb.util @@ -14,6 +11,7 @@ from pyxcp.transport.base import BaseTransport from pyxcp.utils import SHORT_SLEEP + RECV_SIZE = 16384 @@ -34,7 +32,7 @@ class Usb(BaseTransport): HEADER_SIZE = HEADER.size def __init__(self, config=None, policy=None): - super(Usb, self).__init__(config, policy) + super().__init__(config, policy) self.load_config(config) self.serial_number = self.config.serial_number self.vendor_id = self.config.vendor_id @@ -74,7 +72,7 @@ def connect(self): except BaseException: continue else: - raise Exception("Device with serial {} not found".format(self.serial_number)) + raise Exception(f"Device with serial {self.serial_number} not found") current_configuration = self.device.get_active_configuration() if current_configuration.bConfigurationValue != self.configuration_number: diff --git a/pyxcp/types.py b/pyxcp/types.py index 7cd1f63..df2c54a 100644 --- a/pyxcp/types.py +++ b/pyxcp/types.py @@ -1,28 +1,29 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import enum from collections import namedtuple import construct -from construct import BitsInteger -from construct import BitStruct -from construct import Enum -from construct import Flag -from construct import GreedyBytes -from construct import GreedyRange -from construct import If -from construct import IfThenElse -from construct import Int16ub -from construct import Int16ul -from construct import Int32ub -from construct import Int32ul -from construct import Int64ub -from construct import Int64ul -from construct import Int8ul -from construct import Padding -from construct import Struct -from construct import Switch -from construct import this +from construct import ( + BitsInteger, + BitStruct, + Enum, + Flag, + GreedyBytes, + GreedyRange, + If, + IfThenElse, + Int8ul, + Int16ub, + Int16ul, + Int32ub, + Int32ul, + Int64ub, + Int64ul, + Padding, + Struct, + Switch, + this, +) if construct.version < (2, 8): diff --git a/pyxcp/utils.py b/pyxcp/utils.py index 069e675..2747f03 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -1,10 +1,7 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import sys from binascii import hexlify -from time import get_clock_info from time import perf_counter -from time import time import chardet @@ -26,14 +23,14 @@ def hexDump(arr): arr = hexlify(arr).decode("ascii") return "[{}]".format(" ".join([arr[i * 2 : (i + 1) * 2] for i in range(size)])) else: - return "[{}]".format(" ".join(["{:02x}".format(x) for x in arr])) + return "[{}]".format(" ".join([f"{x:02x}" for x in arr])) def slicer(iterable, sliceLength, converter=None): if converter is None: converter = type(iterable) length = len(iterable) - return [converter((iterable[item : item + sliceLength])) for item in range(0, length, sliceLength)] + return [converter(iterable[item : item + sliceLength]) for item in range(0, length, sliceLength)] def flatten(*args): @@ -58,6 +55,7 @@ def decode_bytes(byte_str: bytes) -> str: else: return byte_str.decode(encoding) + PYTHON_VERSION = getPythonVersion() SHORT_SLEEP = 0.0005 diff --git a/pyxcp/vector/map.py b/pyxcp/vector/map.py index d03f133..e9356f7 100644 --- a/pyxcp/vector/map.py +++ b/pyxcp/vector/map.py @@ -1,6 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -from enum import IntEnum MAP_NAMES = { 1: "BorlandC 16 Bit", @@ -81,4 +79,4 @@ def mapfile_name(name, counter, fmt): - return "{:2d}{:d}{:s}.map".format(fmt, counter, name) + return f"{fmt:2d}{counter:d}{name:s}.map" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index dc9e007..0000000 --- a/requirements.txt +++ /dev/null @@ -1,23 +0,0 @@ -pybind11~=2.10.4 -pytest~=7.4.2 -pytest-runner -construct>=2.9 -mako -traitlets -chardet -rich -pyserial -numpydoc -sphinxcontrib-napoleon -toml~=0.10.2 -pyusb -win-precise-time; sys_platform == 'win32' - -numpy~=1.25.0 -setuptools~=65.5.0 -packaging~=23.1 -Babel~=2.12.1 -Pygments~=2.16.1 -MarkupSafe~=2.1.3 -Jinja2~=3.1.2 -docutils~=0.20.1 \ No newline at end of file diff --git a/selective_tests.py b/selective_tests.py index 461ebd7..156c912 100644 --- a/selective_tests.py +++ b/selective_tests.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import sys from pathlib import Path @@ -16,7 +15,7 @@ def main(args): print("SELECTIVE_TESTS called with:", args) dirs = set() args = args[1:] - with open("st.txt", "wt") as of: + with open("st.txt", "w") as of: for arg in args: parent = str(Path(arg).parent) dirs.add(parent) diff --git a/setup.py b/setup.py index b1a8f0a..ae3443f 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,21 @@ #!/bin/env python import os import platform -import subprocess +import subprocess # nosec import sys import setuptools.command.build_py import setuptools.command.develop + if sys.platform == "darwin": os.environ["CC"] = "clang++" os.environ["CXX"] = "clang++" try: from pybind11.setup_helpers import ( - Pybind11Extension, - build_ext, ParallelCompile, + Pybind11Extension, naive_recompile, ) except ImportError: @@ -26,23 +26,39 @@ ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() try: - PYB11_INCLUDE_DIRS = subprocess.check_output(["pybind11-config", "--includes"]) + PYB11_INCLUDE_DIRS = subprocess.check_output(["pybind11-config", "--includes"]) # nosec except Exception as e: print(str(e), end=" -- ") has_pybind11 = False print("'pybind11-config' not properly working, could not build recorder extension module.") -with open(os.path.join("pyxcp", "__init__.py"), "r") as f: +with open(os.path.join("pyxcp", "__init__.py")) as f: for line in f: if line.startswith("__version__"): version = line.split("=")[-1].strip().strip('"') break -with open("README.md", "r") as fh: +with open("README.md") as fh: long_description = fh.read() +""" +PKG_NAME = "stim" +EXT_NAMES = ["stim"] +__version__ = "0.0.1" + +ext_modules = [ + Pybind11Extension( + EXT_NAMES[0], + include_dirs=[INCLUDE_DIRS], + sources=["stim.cpp", "stim_wrapper.cpp", "scheduler.cpp"], + define_macros=[("EXTENSION_NAME", EXT_NAMES[0])], + cxx_std=20, # Extension will use C++20 generators/coroutines. + ), +] + +""" -EXT_NAMES = ["pyxcp.recorder.rekorder", "pyxcp.cpp_ext.cpp_ext"] +EXT_NAMES = ["pyxcp.recorder.rekorder", "pyxcp.cpp_ext.cpp_ext", "pyxcp.stim.stim"] if has_pybind11: ext_modules = [ @@ -62,6 +78,14 @@ optional=False, cxx_std=20, ), + Pybind11Extension( + EXT_NAMES[2], + include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/stim"], + sources=["pyxcp/stim/stim.cpp", "pyxcp/stim/stim_wrapper.cpp", "pyxcp/stim/scheduler.cpp"], + define_macros=[("EXTENSION_NAME", EXT_NAMES[2]), ("NDEBUG", 1)], + optional=False, + cxx_std=20, # Extension will use C++20 generators/coroutines. + ), ] else: ext_modules = [] @@ -93,7 +117,7 @@ def finalize_options(self): """Post-process options.""" asamkeydll = os.path.join("pyxcp", "asamkeydll.c") target = os.path.join("pyxcp", "asamkeydll.exe") - self.arguments = [asamkeydll, "-o{}".format(target)] + self.arguments = [asamkeydll, f"-o{target}"] def run(self): """Run gcc""" @@ -102,9 +126,9 @@ def run(self): gccCmd = ["gcc", "-m32", "-O3", "-Wall"] self.announce(" ".join(gccCmd + self.arguments)) try: - subprocess.check_call(gccCmd + self.arguments) + subprocess.check_call(gccCmd + self.arguments) # nosec except Exception as e: - print("Building pyxcp/asamkeydll.exe failed: '{}'".format(str(e))) + print(f"Building pyxcp/asamkeydll.exe failed: '{str(e)}'") else: print("Successfully build pyxcp/asamkeydll.exe") From a347de71af353817aaa6596109f9f7bdb185eb34 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Fri, 20 Oct 2023 10:56:37 +0200 Subject: [PATCH 17/99] Restructure --- pyproject.toml | 14 ++++---- pyxcp/{stim => daq_stim}/scheduler.cpp | 0 pyxcp/{stim => daq_stim}/scheduler.hpp | 0 pyxcp/{stim => daq_stim}/stim.cpp | 0 pyxcp/{stim => daq_stim}/stim.hpp | 0 pyxcp/{stim => daq_stim}/stim_wrapper.cpp | 0 pyxcp/master/master.py | 3 +- pyxcp/stim/do.cmd | 3 -- pyxcp/stim/main.cpp | 7 ---- pyxcp/stim/run.cmd | 2 -- pyxcp/stim/setup.py | 41 ----------------------- setup.cfg | 3 ++ setup.py | 23 ++----------- 13 files changed, 13 insertions(+), 83 deletions(-) rename pyxcp/{stim => daq_stim}/scheduler.cpp (100%) rename pyxcp/{stim => daq_stim}/scheduler.hpp (100%) rename pyxcp/{stim => daq_stim}/stim.cpp (100%) rename pyxcp/{stim => daq_stim}/stim.hpp (100%) rename pyxcp/{stim => daq_stim}/stim_wrapper.cpp (100%) delete mode 100644 pyxcp/stim/do.cmd delete mode 100644 pyxcp/stim/main.cpp delete mode 100644 pyxcp/stim/run.cmd delete mode 100644 pyxcp/stim/setup.py diff --git a/pyproject.toml b/pyproject.toml index 03ae8dd..c707c7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["poetry-core>=1.0.0", "pybind11<3.0.0,>=2.9.0", "wheel", "build", "twine", "setuptools"] +requires = ["poetry-core>=1.0.0", "setuptools>=68.0.0", ] # , "pybind11<3.0.0,>=2.9.0", "wheel", "build", "twine" build-backend = "poetry.core.masonry.api" @@ -30,6 +30,11 @@ typeguard = ">=2.13.3" xdoctest = {extras = ["colors"], version = ">=0.15.10"} myst-parser = {version = ">=0.16.1"} +[project] +name = "pyxcp" +version = "0.21.6" +dynamic = ["license", "readme", "authors", "requires-python", "description", "classifiers", "scripts", "dependencies", "optional-dependencies"] + [tool.poetry] authors = ["Christoph Schueler "] @@ -53,7 +58,6 @@ classifiers = [ "Programming Language :: Python :: 3.12" ] build = "build_ext.py" -# dynamic = ["entry-points"] [tool.poetry.dependencies] python = "^3.7" @@ -70,12 +74,6 @@ traitlets = "<=5.11.2" [tool.poetry.group.dev.dependencies] ruff = "^0.1.0" -[project.optional-dependencies] -doc = [ - "sphinx", - "sphinxcontrib-napoleon" -] - [tool.poetry.scripts] pyxcp-probe-can-drivers = "pyxcp.scripts.pyxcp_probe_can_drivers:main" xcp-id-scanner = "pyxcp.scripts.xcp_id_scanner:main" diff --git a/pyxcp/stim/scheduler.cpp b/pyxcp/daq_stim/scheduler.cpp similarity index 100% rename from pyxcp/stim/scheduler.cpp rename to pyxcp/daq_stim/scheduler.cpp diff --git a/pyxcp/stim/scheduler.hpp b/pyxcp/daq_stim/scheduler.hpp similarity index 100% rename from pyxcp/stim/scheduler.hpp rename to pyxcp/daq_stim/scheduler.hpp diff --git a/pyxcp/stim/stim.cpp b/pyxcp/daq_stim/stim.cpp similarity index 100% rename from pyxcp/stim/stim.cpp rename to pyxcp/daq_stim/stim.cpp diff --git a/pyxcp/stim/stim.hpp b/pyxcp/daq_stim/stim.hpp similarity index 100% rename from pyxcp/stim/stim.hpp rename to pyxcp/daq_stim/stim.hpp diff --git a/pyxcp/stim/stim_wrapper.cpp b/pyxcp/daq_stim/stim_wrapper.cpp similarity index 100% rename from pyxcp/stim/stim_wrapper.cpp rename to pyxcp/daq_stim/stim_wrapper.cpp diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index e7364b1..2dcccbe 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -24,12 +24,11 @@ makeWordPacker, makeWordUnpacker, ) +from pyxcp.daq_stim.stim import DaqEventInfo, Stim, get_policy_lock, get_writer_lock from pyxcp.master.errorhandler import disable_error_handling, wrapped from pyxcp.transport.base import createTransport from pyxcp.utils import SHORT_SLEEP, decode_bytes, delay -from .stim import DaqEventInfo, Stim, get_policy_lock, get_writer_lock - def broadcasted(func: Callable): """""" diff --git a/pyxcp/stim/do.cmd b/pyxcp/stim/do.cmd deleted file mode 100644 index cff15a3..0000000 --- a/pyxcp/stim/do.cmd +++ /dev/null @@ -1,3 +0,0 @@ -call build.cmd -cls -python .\xcphello_ext.py -c .\conf_eth_user.toml -d -l DEBUG diff --git a/pyxcp/stim/main.cpp b/pyxcp/stim/main.cpp deleted file mode 100644 index 3c5de18..0000000 --- a/pyxcp/stim/main.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include - -#include "stim_wrapper.hpp" - -int main() { - return 0; -} diff --git a/pyxcp/stim/run.cmd b/pyxcp/stim/run.cmd deleted file mode 100644 index 0bb777f..0000000 --- a/pyxcp/stim/run.cmd +++ /dev/null @@ -1,2 +0,0 @@ -cls -python .\xcphello_ext.py -c .\conf_eth_user.toml -d -l DEBUG diff --git a/pyxcp/stim/setup.py b/pyxcp/stim/setup.py deleted file mode 100644 index 23caab7..0000000 --- a/pyxcp/stim/setup.py +++ /dev/null @@ -1,41 +0,0 @@ -import subprocess # nosec -from distutils.core import setup - -from pybind11.setup_helpers import ( - ParallelCompile, - Pybind11Extension, - build_ext, - naive_recompile, -) - - -# ParallelCompile("NPY_NUM_BUILD_JOBS").install() -ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() - -INCLUDE_DIRS = subprocess.getoutput("pybind11-config --include") - -# os.environ ["CFLAGS"] = '' - -PKG_NAME = "stim" -EXT_NAMES = ["stim"] -__version__ = "0.0.1" - -ext_modules = [ - Pybind11Extension( - EXT_NAMES[0], - include_dirs=[INCLUDE_DIRS], - sources=["stim.cpp", "stim_wrapper.cpp", "scheduler.cpp"], - define_macros=[("EXTENSION_NAME", EXT_NAMES[0])], - cxx_std=20, # Extension will use C++20 generators/coroutines. - ), -] - -setup( - name=PKG_NAME, - version="0.0.1", - author="John Doe", - description="Example", - ext_modules=ext_modules, - cmdclass={"build_ext": build_ext}, - zip_save=False, -) diff --git a/setup.cfg b/setup.cfg index c0d459d..aa77c75 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,6 @@ +[metadata] +name=pyxcp + [aliases] test=pytest diff --git a/setup.py b/setup.py index ae3443f..348b3f5 100644 --- a/setup.py +++ b/setup.py @@ -41,24 +41,7 @@ with open("README.md") as fh: long_description = fh.read() -""" -PKG_NAME = "stim" -EXT_NAMES = ["stim"] -__version__ = "0.0.1" - -ext_modules = [ - Pybind11Extension( - EXT_NAMES[0], - include_dirs=[INCLUDE_DIRS], - sources=["stim.cpp", "stim_wrapper.cpp", "scheduler.cpp"], - define_macros=[("EXTENSION_NAME", EXT_NAMES[0])], - cxx_std=20, # Extension will use C++20 generators/coroutines. - ), -] - -""" - -EXT_NAMES = ["pyxcp.recorder.rekorder", "pyxcp.cpp_ext.cpp_ext", "pyxcp.stim.stim"] +EXT_NAMES = ["pyxcp.recorder.rekorder", "pyxcp.cpp_ext.cpp_ext", "pyxcp.daq_stim.stim"] if has_pybind11: ext_modules = [ @@ -80,8 +63,8 @@ ), Pybind11Extension( EXT_NAMES[2], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/stim"], - sources=["pyxcp/stim/stim.cpp", "pyxcp/stim/stim_wrapper.cpp", "pyxcp/stim/scheduler.cpp"], + include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/daq_stim"], + sources=["pyxcp/daq_stim/stim.cpp", "pyxcp/daq_stim/stim_wrapper.cpp", "pyxcp/daq_stim/scheduler.cpp"], define_macros=[("EXTENSION_NAME", EXT_NAMES[2]), ("NDEBUG", 1)], optional=False, cxx_std=20, # Extension will use C++20 generators/coroutines. From 5938ed93023b505e210d59b0245ae40b357469c5 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sat, 11 Nov 2023 12:44:53 +0100 Subject: [PATCH 18/99] today() --- pyxcp/cmdline.py | 3 +- pyxcp/config/__init__.py | 38 +++-- pyxcp/cpp_ext/blockmem.hpp | 59 +++++++ pyxcp/cpp_ext/daqlist.hpp | 12 +- pyxcp/cpp_ext/event.hpp | 67 ++++++++ pyxcp/cpp_ext/extension_wrapper.cpp | 5 +- pyxcp/cpp_ext/mcobject.hpp | 5 +- pyxcp/cpp_ext/tsqueue.hpp | 46 +++++ pyxcp/daq_stim/__init__.py | 19 ++- pyxcp/daq_stim/stim.cpp | 11 -- pyxcp/daq_stim/stim.hpp | 171 ++++++++++++------- pyxcp/daq_stim/stim_wrapper.cpp | 28 ++-- pyxcp/master/errorhandler.py | 40 ++--- pyxcp/master/master.py | 6 +- pyxcp/recorder/rekorder.hpp | 130 +------------- pyxcp/recorder/unfolder.hpp | 252 ++++++++++++++++++++++++++-- pyxcp/recorder/wrap.cpp | 7 +- pyxcp/transport/base.py | 24 +-- pyxcp/utils.py | 18 +- setup.py | 3 +- 20 files changed, 648 insertions(+), 296 deletions(-) create mode 100644 pyxcp/cpp_ext/blockmem.hpp create mode 100644 pyxcp/cpp_ext/event.hpp create mode 100644 pyxcp/cpp_ext/tsqueue.hpp diff --git a/pyxcp/cmdline.py b/pyxcp/cmdline.py index 9567465..27ddd4c 100644 --- a/pyxcp/cmdline.py +++ b/pyxcp/cmdline.py @@ -5,7 +5,7 @@ """ import warnings -from pyxcp.config import application +from pyxcp.config import create_application from pyxcp.master import Master @@ -26,6 +26,7 @@ def __init__(self, callout=None, *args, **kws): warnings.warn("callout argument is not supported anymore", DeprecationWarning) def run(self, policy=None): + application = create_application() transport = application.transport.layer master = Master(transport, config=application, policy=policy) return master diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 8a9a7d4..1bb8fd7 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -739,16 +739,22 @@ class General(SingletonConfigurable): seed_n_key_function = Callable(default_value=None, allow_none=True).tag(config=True) +class ProfileApp(Application): + def start(self): + print("Starting ProfileApp") + + class PyXCP(Application): config_file = Unicode(default_value="pyxcp_conf.py", help="base name of config file").tag(config=True) - classes = List([General, Transport]) + classes = List([General, Transport, ProfileApp]) def initialize(self, argv=None): self.parse_command_line(argv) self.read_configuration_file() self.general = General.instance(config=self.config, parent=self) self.transport = Transport.instance(parent=self) + self.profile_app = ProfileApp.instance(config=self.config, parent=self) def read_configuration_file(self): pth = Path(self.config_file) @@ -784,7 +790,7 @@ def read_configuration_file(self): ) ) - def _iterate_config_class(self, klass, class_names: typing.List[str]) -> None: + def _iterate_config_class(self, klass, class_names: typing.List[str], config) -> None: sub_classes = [] class_path = ".".join(class_names) print( @@ -814,12 +820,17 @@ def _iterate_config_class(self, klass, class_names: typing.List[str]) -> None: else: print(f"# Type: {tr.info()}") print(f"# Default: {value}") - - print(f"# c.{class_path}.{name} = {value}", end="\n\n") + if name in config: + cfg_value = config[name] + if isinstance(cfg_value, str): + cfg_value = f"'{cfg_value}'" + print(f"c.{class_path}.{name} = {cfg_value}", end="\n\n") + else: + print(f"# c.{class_path}.{name} = {value}", end="\n\n") if class_names is None: class_names = [] for sub_klass in sub_classes: - self._iterate_config_class(sub_klass, class_names + [sub_klass.__name__]) + self._iterate_config_class(sub_klass, class_names + [sub_klass.__name__], config=config.get(sub_klass.__name__, {})) def generate_config_file(self, file_like: io.IOBase, config=None) -> None: print("#") @@ -828,19 +839,18 @@ def generate_config_file(self, file_like: io.IOBase, config=None) -> None: print("c = get_config() # noqa", end="\n\n") for klass in self._classes_with_config_traits(): - self._iterate_config_class(klass, [klass.__name__]) + self._iterate_config_class(klass, [klass.__name__], config=self.config.get(klass.__name__, {})) + print(self.config) class Configuration: pass -application = PyXCP() - -application.initialize(sys.argv) -application.start() - -# print(application.generate_config_file()) -# print("*" * 80) +def create_application(): + application = PyXCP() + application.initialize(sys.argv) + application.start() + # application.generate_config_file(sys.stdout) -# application.generate_config_file(sys.stdout) + return application diff --git a/pyxcp/cpp_ext/blockmem.hpp b/pyxcp/cpp_ext/blockmem.hpp new file mode 100644 index 0000000..2200b39 --- /dev/null +++ b/pyxcp/cpp_ext/blockmem.hpp @@ -0,0 +1,59 @@ + +#ifndef __BLOCKMEM_HPP +#define __BLOCKMEM_HPP + +#include +#include +#include + +/* + * + * Super simplicistic block memory manager. + * + */ +template +class BlockMemory { + public: + + using mem_block_t = std::array; + + explicit BlockMemory() noexcept : m_memory{ nullptr }, m_allocation_count{ 0 } { + m_memory = new T[_IS * _NB]; + } + + ~BlockMemory() noexcept { + if (m_memory) { + delete[] m_memory; + } + } + + BlockMemory(const BlockMemory&) = delete; + + T* acquire() noexcept { + const std::scoped_lock lock(m_mtx); + + if (m_allocation_count >= _NB) { + return nullptr; + } + T* ptr = reinterpret_cast(m_memory + (m_allocation_count * _IS)); + m_allocation_count++; + return ptr; + } + + void release() noexcept { + const std::scoped_lock lock(m_mtx); + if (m_allocation_count == 0) { + return; + } + m_allocation_count--; + } + + private: + + T* m_memory; + std::uint32_t m_allocation_count; + std::mutex m_mtx; +}; + + +#endif // __BLOCKMEM_HPP diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index 0b1dc8c..1f8b497 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -13,10 +13,11 @@ class DaqList { std::vector>>; DaqList( - std::string_view meas_name, std::uint16_t event_num, bool enable_timestamps, + std::string_view meas_name, std::uint16_t event_num, bool stim, bool enable_timestamps, const std::vector& measurements ) : - m_name(meas_name), m_event_num(event_num), m_enable_timestamps(enable_timestamps) { + m_name(meas_name), m_event_num(event_num), m_stim(stim), m_enable_timestamps(enable_timestamps) { + std::cout << "DAQ-List: " << meas_name << " " << event_num << " " << stim << " " << enable_timestamps << std::endl; for (const auto& measurement : measurements) { auto const& [name, address, ext, dt_name] = measurement; m_measurements.emplace_back(McObject(name, address, ext, 0, dt_name)); @@ -31,10 +32,14 @@ class DaqList { return m_name; } - bool get_event_num() const { + std::uint16_t get_event_num() const { return m_event_num; } + bool get_stim() const { + return m_stim; + } + const std::vector& get_measurements() const { return m_measurements; } @@ -94,6 +99,7 @@ class DaqList { std::string m_name; std::uint16_t m_event_num; + bool m_stim; bool m_enable_timestamps; std::vector m_measurements; std::vector m_measurements_opt; diff --git a/pyxcp/cpp_ext/event.hpp b/pyxcp/cpp_ext/event.hpp new file mode 100644 index 0000000..b4e8fa5 --- /dev/null +++ b/pyxcp/cpp_ext/event.hpp @@ -0,0 +1,67 @@ + +#ifndef __EVENT_HPP +#define __EVENT_HPP + +#include +#include +#include + +class Event { + public: + + Event(const Event& other) noexcept { + std::scoped_lock lock(other.m_mtx); + m_flag = other.m_flag; + } + + ~Event() = default; + Event() = default; + + void signal() noexcept { + std::scoped_lock lock(m_mtx); + m_flag = true; + m_cond.notify_one(); + } + + void wait() noexcept { + std::unique_lock lock(m_mtx); + m_cond.wait(lock, [this] { return m_flag; }); + m_flag = false; + } + + bool state() const noexcept { + std::scoped_lock lock(m_mtx); + return m_flag; + } + + private: + + mutable std::mutex m_mtx{}; + bool m_flag{ false }; + std::condition_variable m_cond{}; +}; + +#if 0 +class Spinlock { + public: + + Spinlock() : m_flag(ATOMIC_FLAG_INIT) { + } + + ~Spinlock() = default; + + void lock() { + while (m_flag.test_and_set()) { + } + } + + void unlock() { + m_flag.clear(); + } + +private: + std::atomic_flag m_flag; +}; +#endif + +#endif // __EVENT_HPP diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index 88d127d..d34a544 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -47,11 +47,12 @@ PYBIND11_MODULE(cpp_ext, m) { py::class_(m, "DaqList") .def( - py::init&>(), "name"_a, - "event_num"_a, "enable_timestamps"_a, "measurements"_a + py::init&>(), "name"_a, + "event_num"_a, "stim"_a, "enable_timestamps"_a, "measurements"_a ) .def_property("name", &DaqList::get_name, nullptr) .def_property("event_num", &DaqList::get_event_num, nullptr) + .def_property("stim", &DaqList::get_stim, nullptr) .def_property("enable_timestamps", &DaqList::get_enable_timestamps, nullptr) .def_property("measurements", &DaqList::get_measurements, nullptr) .def_property("measurements_opt", &DaqList::get_measurements_opt, &DaqList::set_measurements_opt) diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index 620bf1e..b6b3097 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -6,6 +6,7 @@ #include #include #include + #include #include const std::map> TYPE_MAP = { @@ -36,7 +37,9 @@ class McObject { m_components(components), m_type_index(-1) { if (data_type != "") { - const auto [ti, len] = TYPE_MAP.at(data_type); + std::string dt_toupper; + std::transform(data_type.begin(), data_type.end(), dt_toupper.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c); }); + const auto [ti, len] = TYPE_MAP.at(dt_toupper); m_type_index = ti; m_length = len; } diff --git a/pyxcp/cpp_ext/tsqueue.hpp b/pyxcp/cpp_ext/tsqueue.hpp new file mode 100644 index 0000000..d53440f --- /dev/null +++ b/pyxcp/cpp_ext/tsqueue.hpp @@ -0,0 +1,46 @@ + +#ifndef __TSQUEUE_HPP +#define __TSQUEUE_HPP + +#include +#include +#include + +template +class TsQueue { + public: + + TsQueue() = default; + + TsQueue(const TsQueue& other) noexcept { + std::scoped_lock lock(other.m_mtx); + m_queue = other.m_queue; + } + + void put(T value) noexcept { + std::scoped_lock lock(m_mtx); + m_queue.push(value); + m_cond.notify_one(); + } + + std::shared_ptr get() noexcept { + std::unique_lock lock(m_mtx); + m_cond.wait(lock, [this] { return !m_queue.empty(); }); + std::shared_ptr result(std::make_shared(m_queue.front())); + m_queue.pop(); + return result; + } + + bool empty() const noexcept { + std::scoped_lock lock(m_mtx); + return m_queue.empty(); + } + + private: + + mutable std::mutex m_mtx; + std::queue m_queue; + std::condition_variable m_cond; +}; + +#endif // __TSQUEUE_HPP diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 203e190..7e5bfae 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -1,11 +1,10 @@ #!/usr/bin/env python - from pprint import pprint -from typing import Callable, List, Optional, Tuple +from typing import List from pyxcp import types -from pyxcp.cpp_ext import DaqList +from pyxcp.cpp_ext import DaqList # , StimList from pyxcp.daq_stim.optimize import make_continuous_blocks from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing from pyxcp.recorder import DAQParser as _DAQParser @@ -27,11 +26,11 @@ class DAQParser(_DAQParser): - def __init__(self, file_name: str, daq_lists: List[DaqList], callback: Optional[Callable[[int, Tuple], None]] = None): + def __init__(self, file_name: str, daq_lists: List[DaqList]): super().__init__() - self.callback = callback self.file_name = file_name self.daq_lists = daq_lists + self.setup_called = False def setup(self, write_multiple: bool = True): self.daq_info = self.xcp_master.getDaqInfo() @@ -68,10 +67,10 @@ def setup(self, write_multiple: bool = True): # print("NO TIMESTAMP SUPPORT") else: if self.ts_fixed: - # print("Fixed timestamp") + print("Fixed timestamp") max_payload_size_first = max_payload_size - self.ts_size else: - # print("timestamp variable.") + print("timestamp variable.") self.selectable_timestamps = True except Exception as e: @@ -121,12 +120,18 @@ def setup(self, write_multiple: bool = True): self.xcp_master.setDaqPtr(i, j, 0) for entry in measurement.entries: self.xcp_master.writeDaq(0xFF, entry.length, entry.ext, entry.address) + self.setup_called = True def start(self): + if not self.setup_called: + raise RuntimeError("please run setup() before start()") for i, daq_list in enumerate(self.daq_lists, self.min_daq): + print(daq_list.name, daq_list.event_num, daq_list.stim) mode = 0x00 if self.supports_timestampes and (self.ts_fixed or (self.selectable_timestamps and daq_list.enable_timestamps)): mode = 0x10 + if daq_list.stim: + mode |= 0x02 self.xcp_master.setDaqListMode( daqListNumber=i, mode=mode, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xFF # TODO: + MIN_DAQ ) diff --git a/pyxcp/daq_stim/stim.cpp b/pyxcp/daq_stim/stim.cpp index ace8954..b1dc6cd 100644 --- a/pyxcp/daq_stim/stim.cpp +++ b/pyxcp/daq_stim/stim.cpp @@ -9,14 +9,3 @@ void make_dto() { void init() { } - -static Mutex writer_lock{}; -static Mutex policy_lock{}; - -Mutex& get_writer_lock() { - return writer_lock; -} - -Mutex& get_policy_lock() { - return policy_lock; -} diff --git a/pyxcp/daq_stim/stim.hpp b/pyxcp/daq_stim/stim.hpp index 976b43a..abf6105 100644 --- a/pyxcp/daq_stim/stim.hpp +++ b/pyxcp/daq_stim/stim.hpp @@ -12,13 +12,19 @@ #include #include #include + #include #include #include + #include + + #include "scheduler.hpp" + #include "helper.hpp" + #include #include - #include "scheduler.hpp" +namespace py = pybind11; constexpr double TMR_RESOLUTION = 1.0 / 1000.0; // Timer resolution is one millisecond. @@ -88,22 +94,37 @@ class TsQueue { struct Mutex { ~Mutex() { + //std::cout << "Mutex destructor called" << std::endl; + } + + Mutex(std::string_view name) : m_name(name) { + // std::cout << "Mutex constructor called: " << name << std::endl; } void lock() { + // std::cout << "\tMutex lock: " << m_name << " : " << m_counter << std::endl; m_mtx.lock(); + m_counter++; + #if 0 + if (!m_mtx.try_lock()) { + ///std::cout << "\t\tMutex LOCK FAILED!!!" << std::endl; + } else { + m_counter++; + } + #endif } void unlock() { + //std::cout << "\tMutex un-lock: " << m_name << " : " << m_counter << std::endl; m_mtx.unlock(); + m_counter--; } - std::mutex m_mtx{}; + int m_counter{ 0 }; + std::string m_name{}; + std::recursive_mutex m_mtx{}; }; -Mutex& get_writer_lock(); -Mutex& get_policy_lock(); - constexpr std::uint8_t MIN_STIM_PID = 0x00; constexpr std::uint8_t MAX_STIM_PID = 0xBF; @@ -218,6 +239,41 @@ typedef struct tagXcpDaq_MessageType { ///// +class FakeEnum { + public: + + FakeEnum(std::uint8_t value) : m_value(value) { + } + + const std::string get_name() const { + return std::string("STIM"); + } + + const std::uint8_t get_value() const { + return m_value; + } + + operator int() const { + return m_value; + } + + int bit_length() const { + return 8; + } + + /*std::string*/py::bytes to_bytes(std::uint8_t length, std::string_view encoding) const { + std::stringstream ss; + + ss << m_value; + return py::bytes(ss.str()); + } + + + private: + + std::uint8_t m_value; +}; + struct StimParameters { std::byte max_dto; }; @@ -246,10 +302,8 @@ struct DaqEventInfo { cycle_time = TMR_RESOLUTION; } m_cycle_time = static_cast(cycle_time * 1000.0); - std::cout << "\tTMR_TMP: " << m_cycle_time << std::endl; } - - std::cout << "Event: " << m_name << " Zaikel: " << m_cycle_time << "ms - periodic? " << m_periodic << std::endl; + // DBG_PRINTN("Event:", m_name, " time:", m_cycle_time, "periodic?", m_periodic, std::endl); } public: @@ -305,17 +359,17 @@ void sched_init(); // TODO: Incl. class Stim { public: - const std::uint8_t RESUME = 0x80; - const std::uint8_t RUNNING = 0x40; - const std::uint8_t PID_OFF = 0x20; + const std::uint8_t RESUME = 0x80; + const std::uint8_t RUNNING = 0x40; + const std::uint8_t PID_OFF = 0x20; const std::uint8_t TIMESTAMP = 0x10; const std::uint8_t DIRECTION = 0x02; - const std::uint8_t SELECTED = 0x01; + const std::uint8_t SELECTED = 0x01; const std::uint8_t DIRECTION_STIM = 0x02; - using feed_function_t = std::function)>; - using send_function_t = std::function)>; + using feed_function_t = std::function)>; + using send_function_t = std::function)>; explicit Stim() { if (timeBeginPeriod(100) == TIMERR_NOERROR) { @@ -331,7 +385,7 @@ class Stim { auto start = timeGetTime(); - //m_scheduler.start_thread(); + // m_scheduler.start_thread(); } void setParameters(const StimParameters& params) { @@ -339,14 +393,14 @@ class Stim { } void setDaqEventInfo(const std::vector& daq_event_info) { - std::uint16_t idx = 0; + std::uint16_t idx = 0; - std::cout << "SET_DAQ_EVENT_INFO" << std::endl; + DBG_PRINTN("SET_DAQ_EVENT_INFO\n"); for (const auto& event : daq_event_info) { - //m_daq_event_info.try_emplace(std::make_pair(idx, event)); + // m_daq_event_info.try_emplace(std::make_pair(idx, event)); m_daq_event_info.try_emplace(idx, event); - //m_daq_event_info[idx] = event; + // m_daq_event_info[idx] = event; if (event.m_stim) { std::cout << "\tSTIM: " << event.m_name << ":" << idx << std::endl; } @@ -359,7 +413,7 @@ class Stim { return; } m_daq_ptr = { daqListNumber, odtNumber, odtEntryNumber }; - std::cout << "SET_DAQ_PTR " << daqListNumber << ":" << odtNumber << ":" << odtEntryNumber << std::endl; + //DBG_PRINTN("SET_DAQ_PTR -- daq:", daqListNumber, " odt:", odtNumber, " entry:", odtEntryNumber, std::endl); } void clearDaqList(std::uint16_t daqListNumber) { @@ -367,40 +421,41 @@ class Stim { return; } auto entry = m_daq_lists[daqListNumber]; - std::cout << "CLEAR_DAQ_LIST " << daqListNumber << std::endl; + //DBG_PRINTN("CLEAR_DAQ_LIST -- daq:", daqListNumber, std::endl); entry.clear(); } void writeDaq(std::uint16_t bitOffset, std::uint16_t entrySize, std::uint16_t addressExt, std::uint32_t address) { auto [d, o, e] = m_daq_ptr; - auto& entry = m_daq_lists[d].m_odts[o].m_entries[e]; + auto& entry = m_daq_lists[d].m_odts[o].m_entries[e]; - std::cout << "WRITE_DAQ " << bitOffset << ":" << entrySize << ":" << addressExt << ":" << address << std::endl; + //DBG_PRINTN("WRITE_DAQ -- bit-offset:", bitOffset, " size:", entrySize, " addr-ext:", addressExt," addr:", address, std::endl); entry.bitOffset = bitOffset; entry.address = address; entry.address_extension = addressExt; entry.entry_size = entrySize; - +#if 0 std::cout << "\tBO: " << entry.bitOffset << std::endl; std::cout << "\tES: " << entry.entry_size << std::endl; std::cout << "\tAD: " << entry.address << std::endl; std::cout << "\tAE: " << entry.address_extension << std::endl; +#endif } void setDaqListMode( std::uint16_t mode, std::uint16_t daqListNumber, std::uint16_t eventChannelNumber, std::uint16_t prescaler, std::uint16_t priority ) { - std::cout << "<<< Enter setDaqListMode() !!!\n"; + //std::cout << "<<< Enter setDaqListMode() !!!\n"; if (!validateEntryNumber(daqListNumber)) { - std::cout << "Invalid DAQ list number!!!\b\n"; + //std::cout << "Invalid DAQ list number!!!\b\n"; return; } - std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" - << priority << std::endl; + //std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" + // << priority << std::endl; - auto& entry = m_daq_lists.at(daqListNumber); + auto& entry = m_daq_lists.at(daqListNumber); entry.mode = mode; entry.prescaler = prescaler; // The use of a prescaler is only used for DAQ lists with DIRECTION = DAQ. @@ -408,7 +463,6 @@ class Stim { entry.priority = priority; entry.event_channel_number = eventChannelNumber; - std::cout << "\tAnzahl / Events: " << std::size(m_daq_event_info) << std::endl; auto& event = m_daq_event_info.at(eventChannelNumber); event.m_daq_lists.emplace(daqListNumber); @@ -420,14 +474,14 @@ class Stim { std::cout << "\t\tEvent: " << event.m_name << " ==> " << event.m_cycle_time << " - " << event.m_periodic << std::endl; calculate_scheduler_period(event.m_cycle_time); } - std::cout << ">>> Exit setDaqListMode() !!!\n"; + //std::cout << ">>> Exit setDaqListMode() !!!\n"; } void startStopDaqList(std::uint16_t mode, std::uint16_t daqListNumber) { if (!validateEntryNumber(daqListNumber)) { return; } - auto& entry = m_daq_lists.at(daqListNumber); + auto& entry = m_daq_lists.at(daqListNumber); /* * 00 = stop * 01 = start @@ -441,18 +495,20 @@ class Stim { entry.mode |= (SELECTED); } - std::cout << "START_STOP_DAQ_LIST " << mode << ":" << daqListNumber << std::endl; + //std::cout << "START_STOP_DAQ_LIST " << mode << ":" << daqListNumber << std::endl; } void startStopSynch(std::uint16_t mode) { - std::cout << "START_STOP_SYNCH " << mode << ":" << std::endl; + //std::cout << "START_STOP_SYNCH " << mode << ":" << std::endl; + + send(0x00, { 0x04, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }); - for (auto dl: m_stim_lists) { - std::cout << "\tRunning list: " << dl << std::endl; + for (auto dl : m_stim_lists) { + //std::cout << "\tRunning list: " << dl << std::endl; } auto idx = 0; - for (auto dl: m_daq_lists) { - std::cout << "DAQ-L: " << idx << " " << dl.mode << " - " << dl.event_channel_number << ":" << dl.prescaler << std::endl; + for (auto dl : m_daq_lists) { + //std::cout << "DAQ-L: " << idx << " " << dl.mode << " - " << dl.event_channel_number << ":" << dl.prescaler << std::endl; idx++; } } @@ -477,19 +533,19 @@ class Stim { void clear() { m_daq_lists.clear(); - //m_daq_event_info.clear(); + // m_daq_event_info.clear(); m_stim_lists.clear(); m_first_pids.clear(); } void freeDaq() { - std::cout << "FREE_DAQ\n"; + //std::cout << "FREE_DAQ\n"; clear(); } void allocDaq(std::uint16_t daqCount) { m_daq_lists.resize(daqCount); - std::cout << "ALLOC_DAQ " << daqCount << std::endl; + //std::cout << "ALLOC_DAQ " << daqCount << std::endl; std::for_each(m_daq_lists.cbegin(), m_daq_lists.cend(), [](auto elem) { elem.clear(); }); } @@ -497,7 +553,7 @@ class Stim { if (!validateEntryNumber(daqListNumber)) { return; } - std::cout << "ALLOC_ODT " << daqListNumber << ":" << odtCount << std::endl; + //std::cout << "ALLOC_ODT " << daqListNumber << ":" << odtCount << std::endl; m_daq_lists[daqListNumber].resize(odtCount); } @@ -505,12 +561,12 @@ class Stim { if (!validateEntryNumber(daqListNumber, odtNumber)) { return; } - std::cout << "ALLOC_ODT_ENTRY " << daqListNumber << ":" << odtNumber << ":" << odtEntriesCount << std::endl; + //std::cout << "ALLOC_ODT_ENTRY " << daqListNumber << ":" << odtNumber << ":" << odtEntriesCount << std::endl; m_daq_lists[daqListNumber].m_odts[odtNumber].resize(odtEntriesCount); } void set_first_pid(std::uint16_t daqListNumber, std::uint16_t firstPid) { - std::cout << "set_first_pid(" << daqListNumber << ", " << firstPid << ")\n"; + //std::cout << "set_first_pid(" << daqListNumber << ", " << firstPid << ")\n"; m_first_pids[daqListNumber] = firstPid; } @@ -522,10 +578,9 @@ class Stim { m_send_function = fun; } - void send(const std::vector frame) { + void send(std::uint8_t stim_list_num, const std::vector frame) { if (m_send_function) { - std::scoped_lock _{ get_writer_lock() }; - m_send_function.value()(frame); + m_send_function.value()(FakeEnum(stim_list_num), frame); } } @@ -534,15 +589,13 @@ class Stim { void calculate_scheduler_period(std::size_t value) { if (!m_scheduler_period) { m_scheduler_period = value; - std::cout << "\tSet Period\n"; + //std::cout << "\tSet Period\n"; } if (!m_scheduler_max_value) { m_scheduler_max_value = value; - std::cout << "\tSet Max\n"; - } - if (value==1) { - value=3; + //std::cout << "\tSet Max\n"; } + std::cout << "\tSCHED_Value: " << value << " - BEFORE: " << *m_scheduler_period << ":" << *m_scheduler_max_value << std::endl; m_scheduler_period = std::gcd(*m_scheduler_period, value); m_scheduler_max_value = std::lcm(*m_scheduler_max_value, value); @@ -578,13 +631,13 @@ class Stim { std::tuple m_daq_ptr; std::optional m_scheduler_period{ std::nullopt }; std::optional m_scheduler_max_value{ std::nullopt }; - std::map m_daq_event_info; - std::map m_first_pids{}; - std::set m_stim_lists{}; - std::optional m_feed_function{ std::nullopt }; - std::optional m_send_function{ std::nullopt }; - Scheduler m_scheduler{}; - bool m_daq_running{false}; + std::map m_daq_event_info; + std::map m_first_pids{}; + std::set m_stim_lists{}; + std::optional m_feed_function{ std::nullopt }; + std::optional m_send_function{ std::nullopt }; + Scheduler m_scheduler{}; + bool m_daq_running{ false }; }; #endif // __STIM_HPP diff --git a/pyxcp/daq_stim/stim_wrapper.cpp b/pyxcp/daq_stim/stim_wrapper.cpp index e3b7f72..57c726c 100644 --- a/pyxcp/daq_stim/stim_wrapper.cpp +++ b/pyxcp/daq_stim/stim_wrapper.cpp @@ -12,9 +12,6 @@ using namespace py::literals; #include "stim.hpp" PYBIND11_MODULE(stim, m) { - m.def("get_writer_lock", &get_writer_lock, py::return_value_policy::reference); - m.def("get_policy_lock", &get_policy_lock, py::return_value_policy::reference); - py::class_(m, "DaqEventInfo") .def(py::init() ); @@ -32,19 +29,18 @@ PYBIND11_MODULE(stim, m) { .def("writeDaq", &Stim::writeDaq) .def("setDaqListMode", &Stim::setDaqListMode) .def("startStopDaqList", &Stim::startStopDaqList) - .def("startStopSynch", &Stim::startStopSynch) + .def("startStopSynch", &Stim::startStopSynch) - .def("set_first_pid", &Stim::set_first_pid) + .def("set_first_pid", &Stim::set_first_pid) .def("set_policy_feeder", [](Stim& self, const py::function& callback) { self.set_policy_feeder(callback); }) - .def("set_frame_sender", [](Stim& self, const py::function& callback) { self.set_frame_sender(callback); }) - - ; - - py::class_(m, "Mutex") - .def("__enter__", [&](Mutex& self) { /*std::cout << "__enter__ Mutex()\n";*/ /*self.lock();*/ }) - .def( - "__exit__", - [&](Mutex& self, const std::optional& exc_type, const std::optional& exc_value, - const std::optional& traceback) { /*std::cout << "__exit____ Mutex()\n";*/ /*self.unlock();*/ } - ); + .def("set_frame_sender", [](Stim& self, const py::function& callback) { self.set_frame_sender(callback); }); + + py::class_(m, "FakeEnum") + .def(py::init()) + .def_property_readonly("name", &FakeEnum::get_name) + .def_property_readonly("value", &FakeEnum::get_value) + .def("bit_length", &FakeEnum::bit_length) + .def("to_bytes", &FakeEnum::to_bytes) + .def("__int__", [](const FakeEnum& self) { return self.get_value(); }); + ; } diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index b90c4ce..0862df0 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -2,6 +2,7 @@ """Implements error-handling according to XCP spec. """ import functools +import logging import threading import time import types @@ -9,7 +10,6 @@ import can -from pyxcp.config import application from pyxcp.errormatrix import ERROR_MATRIX, Action, PreAction from pyxcp.types import COMMAND_CATEGORIES, XcpError, XcpResponseError, XcpTimeoutError @@ -39,7 +39,7 @@ class InternalError(Exception): """Indicates an internal error, like invalid service.""" -class UnhandledError(Exception): +class SystemExit(Exception): """""" @@ -76,7 +76,7 @@ def getActions(service, error_code): print(f"Try to handle error -- Service: {service.name} Error-Code: {error_code}") handler = eh.get(error_str) if handler is None: - raise UnhandledError(f"Service '{service.name}' has no handler for '{error_code}'.") + raise SystemExit(f"Service '{service.name}' has no handler for '{error_code}'.") preActions, actions = handler return preActions, actions @@ -168,8 +168,6 @@ def display_error(): class Handler: """""" - logger = application.log - def __init__(self, instance, func, arguments, error_code=None): self.instance = instance if hasattr(func, "__closure__") and func.__closure__: @@ -181,6 +179,7 @@ def __init__(self, instance, func, arguments, error_code=None): self.service = self.instance.service self.error_code = error_code self._repeater = None + self.logger = logging.getLogger("PyXCP") def __str__(self): return f"Handler(func = {func_name(self.func)} arguments = {self.arguments} service = {self.service} error_code = {self.error_code})" @@ -246,15 +245,15 @@ def actions(self, preActions, actions): if item == Action.NONE: pass elif item == Action.DISPLAY_ERROR: - raise UnhandledError("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error.") elif item == Action.RETRY_SYNTAX: - raise UnhandledError("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error.") elif item == Action.RETRY_PARAM: - raise UnhandledError("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error.") elif item == Action.USE_A2L: - raise UnhandledError("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error.") elif item == Action.USE_ALTERATIVE: - raise UnhandledError("Could not proceed due to unhandled error.") # TODO: check alternatives. + raise SystemExit("Could not proceed due to unhandled error.") # TODO: check alternatives. elif item == Action.REPEAT: repetitionCount = Repeater.REPEAT elif item == Action.REPEAT_2_TIMES: @@ -262,13 +261,13 @@ def actions(self, preActions, actions): elif item == Action.REPEAT_INF_TIMES: repetitionCount = Repeater.INFINITE elif item == Action.RESTART_SESSION: - raise UnhandledError("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error.") elif item == Action.TERMINATE_SESSION: - raise UnhandledError("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error.") elif item == Action.SKIP: pass elif item == Action.NEW_FLASH_WARE: - raise UnhandledError("Could not proceed due to unhandled error") + raise SystemExit("Could not proceed due to unhandled error") return result_pre_actions, result_actions, Repeater(repetitionCount) @@ -313,13 +312,14 @@ def __getitem__(self, ndx): class Executor(SingletonBase): """""" - handlerStack = HandlerStack() - repeater = None - logger = application.log - previous_error_code = None - error_code = None - func = None - arguments = None + def __init__(self): + self.handlerStack = HandlerStack() + self.repeater = None + self.logger = logging.getLogger("PyXCP") + self.previous_error_code = None + self.error_code = None + self.func = None + self.arguments = None def __call__(self, inst, func, arguments): self.inst = inst diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 2dcccbe..9f7eada 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -24,7 +24,7 @@ makeWordPacker, makeWordUnpacker, ) -from pyxcp.daq_stim.stim import DaqEventInfo, Stim, get_policy_lock, get_writer_lock +from pyxcp.daq_stim.stim import DaqEventInfo, Stim from pyxcp.master.errorhandler import disable_error_handling, wrapped from pyxcp.transport.base import createTransport from pyxcp.utils import SHORT_SLEEP, decode_bytes, delay @@ -75,12 +75,10 @@ def __init__(self, transport_name: str, config, policy=None): transport_config = config.transport self.transport = createTransport(transport_name, transport_config, policy) - self.transport.set_writer_lock(get_writer_lock()) - self.transport.set_policy_lock(get_policy_lock()) self.stim = Stim() self.stim.clear() self.stim.set_policy_feeder(self.transport.policy.feed) - self.stim.set_frame_sender(self.transport.send) + self.stim.set_frame_sender(self.transport.block_request) # In some cases the transport-layer needs to communicate with us. self.transport.parent = self diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index a2eb83c..da58f56 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -1,5 +1,4 @@ - #if !defined(__REKORDER_HPP) #define __REKORDER_HPP @@ -12,7 +11,6 @@ #include #include #include - #include #include #include #include @@ -20,9 +18,7 @@ #include #include #include - #include #include - #include #include #include #include @@ -30,6 +26,10 @@ #include #include + #include "blockmem.hpp" + #include "event.hpp" + #include "tsqueue.hpp" + #if defined(_WIN32) #include #include @@ -55,6 +55,7 @@ using namespace pybind11::literals; #define __ALIGNMENT_REQUIREMENT __BIGGEST_ALIGNMENT__ #define __ALIGN alignas(__ALIGNMENT_REQUIREMENT) + constexpr auto kilobytes(std::uint32_t value) -> std::uint32_t { return value * 1024; } @@ -182,127 +183,6 @@ inline void hexdump(blob_t const * buf, std::uint16_t sz) { printf("\n\r"); } -template -class TsQueue { - public: - - TsQueue() = default; - - TsQueue(const TsQueue& other) noexcept { - std::scoped_lock lock(other.m_mtx); - m_queue = other.m_queue; - } - - void put(T value) noexcept { - std::scoped_lock lock(m_mtx); - m_queue.push(value); - m_cond.notify_one(); - } - - std::shared_ptr get() noexcept { - std::unique_lock lock(m_mtx); - m_cond.wait(lock, [this] { return !m_queue.empty(); }); - std::shared_ptr result(std::make_shared(m_queue.front())); - m_queue.pop(); - return result; - } - - bool empty() const noexcept { - std::scoped_lock lock(m_mtx); - return m_queue.empty(); - } - - private: - - mutable std::mutex m_mtx; - std::queue m_queue; - std::condition_variable m_cond; -}; - -class Event { - public: - - Event(const Event& other) noexcept { - std::scoped_lock lock(other.m_mtx); - m_flag = other.m_flag; - } - - ~Event() = default; - Event() = default; - - void signal() noexcept { - std::scoped_lock lock(m_mtx); - m_flag = true; - m_cond.notify_one(); - } - - void wait() noexcept { - std::unique_lock lock(m_mtx); - m_cond.wait(lock, [this] { return m_flag; }); - m_flag = false; - } - - bool state() const noexcept { - std::scoped_lock lock(m_mtx); - return m_flag; - } - - private: - - mutable std::mutex m_mtx{}; - bool m_flag{ false }; - std::condition_variable m_cond{}; -}; - -/* - * - * Super simplicistic block memory manager. - * - */ -template -class BlockMemory { - public: - - using mem_block_t = std::array; - - explicit BlockMemory() noexcept : m_memory{ nullptr }, m_allocation_count{ 0 } { - m_memory = new T[_IS * _NB]; - } - - ~BlockMemory() noexcept { - if (m_memory) { - delete[] m_memory; - } - } - - BlockMemory(const BlockMemory&) = delete; - - T* acquire() noexcept { - const std::scoped_lock lock(m_mtx); - - if (m_allocation_count >= _NB) { - return nullptr; - } - T* ptr = reinterpret_cast(m_memory + (m_allocation_count * _IS)); - m_allocation_count++; - return ptr; - } - - void release() noexcept { - const std::scoped_lock lock(m_mtx); - if (m_allocation_count == 0) { - return; - } - m_allocation_count--; - } - - private: - - T* m_memory; - std::uint32_t m_allocation_count; - std::mutex m_mtx; -}; - #include "reader.hpp" #include "unfolder.hpp" #include "writer.hpp" diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 60c050d..9da193f 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -3,13 +3,22 @@ #define RECORDER_UNFOLDER_HPP #include +#include #include #include #include +#include + #include "daqlist.hpp" #include "mcobject.hpp" + +using measurement_value_t = std::variant; +using measurement_tuple_t = std::tuple>; +using measurement_callback_t = std::function)>; + + // NOTE: C++23 has std::byteswap() constexpr auto _bswap(std::uint64_t v) noexcept { return ((v & UINT64_C(0x0000'0000'0000'00FF)) << 56) | ((v & UINT64_C(0x0000'0000'0000'FF00)) << 40) | @@ -27,8 +36,6 @@ constexpr auto _bswap(std::uint16_t v) noexcept { return ((v & UINT16_C(0x00FF)) << 8) | ((v & UINT16_C(0xFF00)) >> 8); } -using measurement_value_t = std::variant; -using measurement_tuple_t = std::tuple>; template auto get_value(blob_t const * buf, std::uint32_t offset) -> Ty { @@ -90,6 +97,80 @@ auto get_value_swapped(blob_t const * buf, std::uint32_t offset) - return static_cast(get_value_swapped(buf, offset)); } + +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// +template +void set_value(blob_t * buf, std::uint32_t offset, Ty value) { + ::memcpy(&buf[offset], &value, sizeof(Ty)); +} + +template +void set_value_swapped(blob_t * buf, std::uint32_t offset, Ty value) { + set_value(buf, offset, _bswap(value)); +} + +template<> +void set_value(blob_t * buf, std::uint32_t offset, std::int8_t value) { + buf[offset] = static_cast(value); +} + +template<> +void set_value(blob_t * buf, std::uint32_t offset, std::uint8_t value) { + buf[offset] = static_cast(value); +} + +template<> +void set_value(blob_t * buf, std::uint32_t offset, std::int16_t value) { + set_value(buf, offset, static_cast(value)); +} + +template<> +void set_value_swapped(blob_t * buf, std::uint32_t offset, std::int16_t value) { + set_value_swapped(buf, offset, static_cast(value)); +} + +template<> +void set_value(blob_t * buf, std::uint32_t offset, std::int32_t value) { + set_value(buf, offset, static_cast(value)); +} + +template<> +void set_value_swapped(blob_t * buf, std::uint32_t offset, std::int32_t value) { + set_value_swapped(buf, offset, static_cast(value)); +} + +template<> +void set_value(blob_t * buf, std::uint32_t offset, std::int64_t value) { + set_value(buf, offset, static_cast(value)); +} + +template<> +void set_value_swapped(blob_t * buf, std::uint32_t offset, std::int64_t value) { + set_value_swapped(buf, offset, static_cast(value)); +} + +template<> +void set_value(blob_t * buf, std::uint32_t offset, float value) { + set_value(buf, offset, static_cast(value)); +} + +template<> +void set_value_swapped(blob_t * buf, std::uint32_t offset, float value) { + set_value_swapped(buf, offset, static_cast(value)); +} + +template<> +void set_value(blob_t * buf, std::uint32_t offset, double value) { + set_value(buf, offset, static_cast(value)); +} + +template<> +void set_value_swapped(blob_t * buf, std::uint32_t offset, double value) { + set_value_swapped(buf, offset, static_cast(value)); +} + + /* ** Get primitive datatypes, consider byte-order. */ @@ -214,6 +295,145 @@ struct Getter { std::map> m_odt_to_daq_map; }; +////////////////////////////////////////////////////////////////////////////////////////////// +struct Setter { + Setter() = default; + + explicit Setter(bool requires_swap, std::uint8_t id_size, std::uint8_t ts_size) : m_id_size(id_size), m_ts_size(ts_size) { + int8 = set_value; + uint8 = set_value; + + if (requires_swap) { + int16 = set_value_swapped; + int32 = set_value_swapped; + int64 = set_value_swapped; + uint16 = set_value_swapped; + uint32 = set_value_swapped; + uint64 = set_value_swapped; + float_ = set_value_swapped; + double_ = set_value_swapped; + } else { + int16 = set_value; + int32 = set_value; + int64 = set_value; + uint16 = set_value; + uint32 = set_value; + uint64 = set_value; + float_ = set_value; + double_ = set_value; + } + } + + std::uint32_t set_timestamp(blob_t * buf, std::uint32_t timestamp) { + switch (m_ts_size) { + case 0: + break; + case 1: + uint8(buf, m_id_size, timestamp); + break; + case 2: + uint16(buf, m_id_size, timestamp); + break; + case 4: + uint32(buf, m_id_size, timestamp); + break; + default: + throw std::runtime_error("Unsupported timestamp size: " + std::to_string(m_ts_size)); + } + } + + void writer(std::uint16_t tp, blob_t * buf, std::uint16_t offset, const measurement_value_t& value) { + switch (tp) { + case 0: + uint8(buf, offset, static_cast(std::get(value))); + break; + case 1: + int8(buf, offset, static_cast(std::get(value))); + break; + case 2: + uint16(buf, offset, static_cast(std::get(value))); + break; + case 3: + int16(buf, offset, static_cast(std::get(value))); + break; + case 4: + uint32(buf, offset, static_cast(std::get(value))); + break; + case 5: + int32(buf, offset, static_cast(std::get(value))); + break; + case 6: + uint64(buf, offset, std::get(value)); + break; + case 7: + int64(buf, offset, std::get(value)); + break; + case 8: + float_(buf, offset, static_cast(std::get(value))); + break; + case 9: + double_(buf, offset, static_cast(std::get(value))); + break; + default: + throw std::runtime_error("Unsupported data type: " + std::to_string(tp)); + } + } + +#if 0 + void set_first_pids(const std::vector& daq_lists, const std::vector& first_pids) { + m_first_pids = first_pids; + + if (m_id_size == 1) { + // In case of 1-byte ID field (absolute ODT number) we need a mapping. + std::uint16_t daq_list_num = 0; + for (const auto& daq_list : daq_lists) { + auto first_pid = m_first_pids[daq_list_num]; + + for (std::uint16_t idx = first_pid; idx < daq_list.set_odt_count() + first_pid; ++idx) { + m_odt_to_daq_map[idx] = { daq_list_num, (idx - first_pid) }; + } + daq_list_num++; + } + } + } +#endif + +#if 0 + std::tuple set_id(blob_t const * buf) { + std::uint16_t odt_num = 0; + + switch (m_id_size) { + case 1: + odt_num = uint8(buf, 0); // Get 1-byte ODT number... + return m_odt_to_daq_map[odt_num]; // ...and return mapped values. + case 2: + return { uint8(buf, 1), uint8(buf, 0) }; + case 3: + return { uint16(buf, 1), uint8(buf, 0) }; + case 4: + return { uint16(buf, 2), uint8(buf, 0) }; + default: + throw std::runtime_error("Unsupported ID size: " + std::to_string(m_id_size)); + } + } +#endif + std::uint8_t m_id_size; + std::uint8_t m_ts_size; + std::function int8; + std::function uint8; + std::function int16; + std::function int32; + std::function int64; + std::function uint16; + std::function uint32; + std::function uint64; + std::function float_; + std::function double_; + std::vector m_first_pids; + std::map> m_odt_to_daq_map; +}; +////////////////////////////////////////////////////////////////////////////////////////////// + struct MeasurementParameters { MeasurementParameters() = delete; @@ -305,6 +525,7 @@ class DaqListState { bool feed(uint16_t odt_num, double timestamp, const std::string& payload) { auto state = check_state(odt_num); auto finished = false; + if (state == state_t::COLLECTING) { m_timestamp0 = timestamp; parse_Odt(odt_num, payload); @@ -321,6 +542,7 @@ class DaqListState { } void add_result(measurement_tuple_t& result_buffer) { + //std::cout << "add_result: " << m_daq_list_num << " " << m_timestamp0 << " " << m_timestamp1 << std::endl; result_buffer = { m_daq_list_num, m_timestamp0, m_timestamp1, m_buffer }; } @@ -390,6 +612,7 @@ auto requires_swap(std::uint8_t byte_order) { //////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////// + class UnfolderBase { public: @@ -430,17 +653,18 @@ class UnfolderBase { #endif - void feed(double timestamp, const std::string& payload) noexcept { + std::optional feed(double timestamp, const std::string& payload) noexcept { const auto data = reinterpret_cast(payload.data()); auto [daq_num, odt_num] = m_getter.get_id(data); - measurement_tuple_t result; if (m_state[daq_num].feed(odt_num, timestamp, payload)) { - m_state[daq_num].add_result(result); - auto [dl, d0, d1, pl] = result; + // DAQ list completed. + measurement_tuple_t result; - // std::cout << "DL: " << dl << " : " << d0 << " : " << d1 << std::endl; + m_state[daq_num].add_result(result); // get_result()??? + return result; } + return std::nullopt; } private: @@ -531,14 +755,14 @@ class XcpLogFileUnfolder { class DAQParser { public: - using callback_t = std::function&)>; + virtual ~DAQParser() { + } - virtual ~DAQParser() = default; DAQParser() = default; void set_parameters(const MeasurementParameters& params) noexcept { m_unfolder = std::make_unique(params); - std::cout << "DAQParser::set_parameters: " << std::endl; + post_setup(); } virtual void on_daq_list( @@ -549,13 +773,17 @@ class DAQParser { if (frame_cat != static_cast(FrameCategory::DAQ)) { return; } - m_unfolder->feed(timestamp, payload); + auto result = m_unfolder->feed(timestamp, payload); + if (result) { + const auto& [daq_list, ts0, ts1, meas] = *result; + on_daq_list(daq_list, ts0, ts1, meas); + } } virtual void post_setup() { } - void finalize() noexcept { + virtual void finalize() { } private: diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index e3ec324..9193d71 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -18,12 +19,16 @@ class PyDAQParser : public DAQParser { void on_daq_list( std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector &measurement ) override { - PYBIND11_OVERRIDE_PURE(void, DAQParser, feed, daq_list_num, timestamp0, timestamp0, measurement); + PYBIND11_OVERRIDE_PURE(void, DAQParser, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); } void post_setup() override { PYBIND11_OVERRIDE(void, DAQParser, post_setup); } + + void finalize() override { + PYBIND11_OVERRIDE(void, DAQParser, finalize); + } }; PYBIND11_MODULE(rekorder, m) { diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 5da6c9e..5695ab0 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -151,6 +151,7 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.closeEvent = threading.Event() self.command_lock = threading.Lock() + self.policy_lock = threading.Lock() self.logger = config.log self._debug = self.logger.level == 10 @@ -187,12 +188,6 @@ def load_config(self, config): class_name = self.__class__.__name__.lower() self.config = getattr(config, class_name) - def set_writer_lock(self, lock): - self.writer_lock = lock - - def set_policy_lock(self, lock): - self.policy_lock = lock - def close(self): """Close the transport-layer connection and event-loop.""" self.finishListener() @@ -222,8 +217,7 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): self.timing.start() with self.policy_lock: self.policy.feed(types.FrameCategory.CMD, self.counterSend, perf_counter(), frame) - with self.writer_lock: - self.send(frame) + self.send(frame) try: xcpPDU = get( self.resQueue, @@ -270,9 +264,17 @@ def block_request(self, cmd, *data): if pid == "ERR" and cmd.name != "SYNCH": err = types.XcpError.parse(xcpPDU[1:]) raise types.XcpResponseError(err) - - frame = self._prepare_request(cmd, *data) - with self.writer_lock: + with self.command_lock: + if isinstance(*data, list): + data = data[0] # C++ interfacing. + frame = self._prepare_request(cmd, *data) + with self.policy_lock: + self.policy.feed( + types.FrameCategory.CMD if int(cmd) >= 0xC0 else types.FrameCategory.STIM, + self.counterSend, + perf_counter(), + frame, + ) self.send(frame) def _prepare_request(self, cmd, *data): diff --git a/pyxcp/utils.py b/pyxcp/utils.py index 2747f03..6220176 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +import functools +import operator import sys from binascii import hexlify from time import perf_counter @@ -33,14 +35,16 @@ def slicer(iterable, sliceLength, converter=None): return [converter(iterable[item : item + sliceLength]) for item in range(0, length, sliceLength)] +def functools_reduce_iconcat(a): + return functools.reduce(operator.iconcat, a, []) + + def flatten(*args): - result = [] - for arg in list(args): - if hasattr(arg, "__iter__"): - result.extend(flatten(*arg)) - else: - result.append(arg) - return result + """Flatten a list of lists into a single list. + + s. https://stackoverflow.com/questions/952914/how-do-i-make-a-flat-list-out-of-a-list-of-lists + """ + return functools.reduce(operator.iconcat, args, []) def getPythonVersion(): diff --git a/setup.py b/setup.py index 348b3f5..1f5d584 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -#!/bin/env python import os import platform import subprocess # nosec @@ -63,7 +62,7 @@ ), Pybind11Extension( EXT_NAMES[2], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/daq_stim"], + include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/daq_stim", "pyxcp/cpp_ext"], sources=["pyxcp/daq_stim/stim.cpp", "pyxcp/daq_stim/stim_wrapper.cpp", "pyxcp/daq_stim/scheduler.cpp"], define_macros=[("EXTENSION_NAME", EXT_NAMES[2]), ("NDEBUG", 1)], optional=False, From 329e3ad449c6532c9c24aa774e34c88165b31793 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 7 Dec 2023 10:53:17 +0100 Subject: [PATCH 19/99] today() --- pyxcp/cpp_ext/helper.hpp | 15 +++++++++++++++ pyxcp/recorder/unfolder.hpp | 2 +- setup.py | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 pyxcp/cpp_ext/helper.hpp diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp new file mode 100644 index 0000000..894cdb1 --- /dev/null +++ b/pyxcp/cpp_ext/helper.hpp @@ -0,0 +1,15 @@ + +#if !defined(__HELPER_HPP) +#define __HELPER_HPP + +#include +#include + +template +constexpr void DBG_PRINTN(Args&&... args) noexcept +{ + ((std::cout << std::forward(args) << " "), ...); +} + + +#endif // __HELPER_HPP diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 9da193f..03efa8e 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -602,7 +602,7 @@ class DaqListState { MeasurementParameters m_params; }; -auto requires_swap(std::uint8_t byte_order) { +auto requires_swap(std::uint8_t byte_order) -> bool { // INTEL(LITTLE)=0, MOTOROLA(BIG)=1 std::endian target_byte_order = (byte_order == 1) ? std::endian::big : std::endian::little; return (target_byte_order != std::endian::native) ? true : false; diff --git a/setup.py b/setup.py index 1f5d584..de53835 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ if sys.platform == "darwin": + os.environ["MACOSX_DEPLOYMENT_TARGET"] = "11.0" os.environ["CC"] = "clang++" os.environ["CXX"] = "clang++" From 0f1f90168b41f677f4cb6059c70f4b85aad16fe6 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 11 Jan 2024 18:26:58 +0100 Subject: [PATCH 20/99] today() --- pyproject.toml | 1 + pyxcp/__init__.py | 14 +- pyxcp/config/__init__.py | 241 ++++-- pyxcp/config/legacy.py | 18 +- pyxcp/cpp_ext/daqlist.hpp | 2 +- pyxcp/cpp_ext/mcobject.hpp | 10 +- pyxcp/daq_stim/__init__.py | 57 +- pyxcp/daq_stim/linreg.hpp | 383 ++++++++++ pyxcp/daq_stim/scheduler.hpp | 4 +- pyxcp/daq_stim/stim.hpp | 204 ++--- pyxcp/daq_stim/stim_wrapper.cpp | 2 +- pyxcp/dllif.py | 13 +- pyxcp/examples/conf_can_vector.toml | 6 +- pyxcp/examples/conf_eth.toml | 7 +- pyxcp/master/errorhandler.py | 42 +- pyxcp/master/master.py | 124 ++- pyxcp/recorder/unfolder.hpp | 24 +- pyxcp/scripts/xcp_info.py | 114 +-- pyxcp/tests/test_can.py | 1091 +++++++++++++++++++++++++++ pyxcp/transport/base.py | 4 +- pyxcp/transport/can.py | 70 +- pyxcp/transport/usb_transport.py | 16 +- pyxcp/types.py | 8 + 23 files changed, 2086 insertions(+), 369 deletions(-) create mode 100644 pyxcp/daq_stim/linreg.hpp diff --git a/pyproject.toml b/pyproject.toml index c707c7c..e7e2716 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,7 @@ uptime = "^3.0.1" rich = "^13.6.0" chardet = "^5.2.0" traitlets = "<=5.11.2" +line-profiler-pycharm = "^1.1.0" [tool.poetry.group.dev.dependencies] ruff = "^0.1.0" diff --git a/pyxcp/__init__.py b/pyxcp/__init__.py index 580bbea..373be26 100644 --- a/pyxcp/__init__.py +++ b/pyxcp/__init__.py @@ -2,13 +2,19 @@ """Universal Calibration Protocol for Python""" import sys -from rich.traceback import install +from rich import pretty +from rich.console import Console +from rich.traceback import install as tb_install -from .master import Master # noqa: F401 -from .transport import Can, Eth, SxI, Usb # noqa: F401 +pretty.install() -install(show_locals=True, max_frames=3) # Install custom exception handler. +from .master import Master # noqa: F401, E402 +from .transport import Can, Eth, SxI, Usb # noqa: F401, E402 + + +console = Console() +tb_install(show_locals=True, max_frames=3) # Install custom exception handler. if sys.platform == "win32" and sys.version_info[:2] < (3, 11): # patch the time module with the high resolution alternatives diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 1bb8fd7..404e1d9 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -3,11 +3,12 @@ import json import sys import typing -import warnings from pathlib import Path import can import toml +from rich.logging import RichHandler +from rich.prompt import Confirm from traitlets import ( Any, Bool, @@ -26,7 +27,7 @@ from pyxcp.config import legacy -warnings.simplefilter("always") +# warnings.simplefilter("always") class CanBase: @@ -556,7 +557,7 @@ class Can(SingletonConfigurable): default_value=None, allow_none=True, help="Channel identification. Expected type and value is backend dependent." ).tag(config=True) max_dlc_required = Bool(False, help="Master to slave frames always to have DLC = MAX_DLC = 8").tag(config=True) - max_can_fd_dlc = Integer(64, help="").tag(config=True) + # max_can_fd_dlc = Integer(64, help="").tag(config=True) padding_value = Integer(0, help="Fill value, if max_dlc_required == True and DLC < MAX_DLC").tag(config=True) use_default_listener = Bool(True, help="").tag(config=True) can_id_master = Integer(allow_none=False, help="CAN-ID master -> slave (Bit31= 1: extended identifier)").tag(config=True) @@ -564,6 +565,9 @@ class Can(SingletonConfigurable): can_id_broadcast = Integer( default_value=None, allow_none=True, help="Auto detection CAN-ID (Bit31= 1: extended identifier)" ).tag(config=True) + daq_identifier = List(trait=Integer(), default_value=[], allow_none=True, help="One CAN identifier per DAQ-list.").tag( + config=True + ) bitrate = Integer(250000, help="CAN bitrate in bits/s (arbitration phase, if CAN FD).").tag(config=True) receive_own_messages = Bool(False, help="Enable self-reception of sent messages.").tag(config=True) poll_interval = Float(default_value=None, allow_none=True, help="Poll interval in seconds when reading messages.").tag( @@ -659,25 +663,25 @@ def __init__(self, **kws): class Eth(SingletonConfigurable): - """ """ + """Ethernet.""" - host = Unicode("localhost").tag(config=True) - port = Integer(5555).tag(config=True) - protocol = Enum(["TCP", "UDP"], default_value="UDP").tag(config=True) - ipv6 = Bool(False).tag(config=True) - tcp_nodelay = Bool(False).tag(config=True) - bind_to_address = Unicode(default_value=None, allow_none=True, help="Specific local address.").tag(config=True) - bind_to_port = Integer(default_value=None, allow_none=True, help="Specific local port.").tag(config=True) + host = Unicode("localhost", help="Hostname or IP address of XCP slave.").tag(config=True) + port = Integer(5555, help="TCP/UDP port to connect.").tag(config=True) + protocol = Enum(["TCP", "UDP"], default_value="UDP", help="").tag(config=True) + ipv6 = Bool(False, help="Use IPv6 if `True` else IPv4.").tag(config=True) + tcp_nodelay = Bool(False, help="*** Expert option *** -- Disable Nagle's algorithm if `True`.").tag(config=True) + bind_to_address = Unicode(default_value=None, allow_none=True, help="Bind to specific local address.").tag(config=True) + bind_to_port = Integer(default_value=None, allow_none=True, help="Bind to specific local port.").tag(config=True) class SxI(SingletonConfigurable): """SPI and SCI connections.""" - port = Unicode("COM1", help="").tag(config=True) - bitrate = Integer(38400, help="").tag(config=True) - bytesize = Enum([5, 6, 7, 8], default_value=8, help="").tag(config=True) - parity = Enum(["N", "E", "O", "M", "S"], default_value="N", help="").tag(config=True) - stopbits = Enum([1, 1.5, 2], default_value=1, help="").tag(config=True) + port = Unicode("COM1", help="Name of communication interface.").tag(config=True) + bitrate = Integer(38400, help="Connection bitrate").tag(config=True) + bytesize = Enum([5, 6, 7, 8], default_value=8, help="Size of byte.").tag(config=True) + parity = Enum(["N", "E", "O", "M", "S"], default_value="N", help="Paritybit calculation.").tag(config=True) + stopbits = Enum([1, 1.5, 2], default_value=1, help="Number of stopbits.").tag(config=True) """ -prot Set the SxI protocol type SYNC = 1,CTR = 2,SYNC+CTR = 3 (Default 0) @@ -685,22 +689,23 @@ class SxI(SingletonConfigurable): """ -class USB(SingletonConfigurable): - """ """ +class Usb(SingletonConfigurable): + """Universal Serial Bus connections.""" - serial_number = Unicode("").tag(config=True) - configuration_number = Integer(1).tag(config=True) - interface_number = Integer(2).tag(config=True) - command_endpoint_number = Integer(0).tag(config=True) - reply_endpoint_number = Integer(1).tag(config=True) - vendor_id = Integer(0).tag(config=True) - product_id = Integer(0).tag(config=True) + serial_number = Unicode("", help="Device serial number.").tag(config=True) + configuration_number = Integer(1, help="USB configuration number.").tag(config=True) + interface_number = Integer(2, help="USB interface number.").tag(config=True) + command_endpoint_number = Integer(0, help="USB command endpoint number.").tag(config=True) + reply_endpoint_number = Integer(1, help="USB reply endpoint number.").tag(config=True) + vendor_id = Integer(0, help="USB vendor ID.").tag(config=True) + product_id = Integer(0, help="USB product ID.").tag(config=True) + library = Unicode("", help="Absolute path to USB shared library.").tag(config=True) class Transport(SingletonConfigurable): """ """ - classes = List([Can, Eth, SxI, USB]) + classes = List([Can, Eth, SxI, Usb]) layer = Enum( ["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=False, help="Choose one of the supported XCP transport layers." @@ -718,50 +723,156 @@ class Transport(SingletonConfigurable): can = Instance(Can).tag(config=True) eth = Instance(Eth).tag(config=True) sxi = Instance(SxI).tag(config=True) - usb = Instance(USB).tag(config=True) + usb = Instance(Usb).tag(config=True) def __init__(self, **kws): super().__init__(**kws) self.can = Can.instance(config=self.config, parent=self) self.eth = Eth.instance(config=self.config, parent=self) self.sxi = SxI.instance(config=self.config, parent=self) - self.usb = USB.instance(config=self.config, parent=self) + self.usb = Usb.instance(config=self.config, parent=self) class General(SingletonConfigurable): """ """ - loglevel = Unicode("WARN").tag(config=True) + loglevel = Unicode("WARN", help="Set the log level by value or name.").tag(config=True) disable_error_handling = Bool(False).tag(config=True) disconnect_response_optional = Bool(False).tag(config=True) seed_n_key_dll = Unicode("", allow_none=False).tag(config=True) seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) seed_n_key_function = Callable(default_value=None, allow_none=True).tag(config=True) + stim_support = Bool(False, help="").tag(config=True) + + +class ProfileCreate(Application): + description = "\nCreate a new profile" + + dest_file = Unicode(default_value=None, allow_none=True, help="destination file name").tag(config=True) + aliases = Dict( # type:ignore[assignment] + dict( + d="ProfileCreate.dest_file", + ) + ) + + def start(self): + self.parent.parent.generate_config_file(sys.stdout, {}) + print("DEST", self.dest_file) + + +class ProfileConvert(Application): + description = "\nConvert legacy configuration file (.json/.toml) to new python based format." + + config_file = Unicode(help="Name of legacy config file (.json/.toml).", default_value=None, allow_none=False).tag( + config=True + ) # default_value="pyxcp_conf.py", + + dest_file = Unicode(default_value=None, allow_none=True, help="destination file name").tag(config=True) + + aliases = Dict( # type:ignore[assignment] + dict( + c="ProfileConvert.config_file", + d="ProfileConvert.dest_file", + ) + ) + + def start(self): + pyxcp = self.parent.parent + pyxcp._read_configuration(self.config_file, emit_warning=False) + if self.dest_file: + dest = Path(self.dest_file) + if dest.exists(): + if not Confirm.ask(f"Destination file [green]{dest.name!r}[/green] already exists. do you want to overwrite it?"): + print("Aborting...") + self.exit(1) + with dest.open("w") as out_file: + pyxcp.generate_config_file(out_file) + else: + pyxcp.generate_config_file(sys.stdout) class ProfileApp(Application): + subcommands = Dict( + dict( + create=(ProfileCreate, ProfileCreate.description.splitlines()[0]), + convert=(ProfileConvert, ProfileConvert.description.splitlines()[0]), + ) + ) + def start(self): - print("Starting ProfileApp") + if self.subapp is None: + print(f"No subcommand specified. Must specify one of: {self.subcommands.keys()}") + print() + self.print_description() + self.print_subcommands() + self.exit(1) + else: + self.subapp.start() class PyXCP(Application): config_file = Unicode(default_value="pyxcp_conf.py", help="base name of config file").tag(config=True) - classes = List([General, Transport, ProfileApp]) + classes = List([General, Transport]) + + subcommands = dict( + profile=( + ProfileApp, + """ + Profile stuff + """.strip(), + ) + ) + + def start(self): + if self.subapp: + self.subapp.start() + exit(2) + else: + self._read_configuration(self.config_file) + + def _setup_logger(self): + from pyxcp.types import Command + + # Remove any handlers installed by `traitlets`. + for hdl in self.log.handlers: + self.log.removeHandler(hdl) + + # formatter = logging.Formatter(fmt=self.log_format, datefmt=self.log_datefmt) + + keywords = list(Command.__members__.keys()) + ["ARGS", "KWS"] # Syntax highlight XCP commands and other stuff. + rich_handler = RichHandler( + rich_tracebacks=True, + tracebacks_show_locals=True, + log_time_format=self.log_datefmt, + level=self.log_level, + keywords=keywords, + ) + # rich_handler.setFormatter(formatter) + self.log.addHandler(rich_handler) def initialize(self, argv=None): - self.parse_command_line(argv) - self.read_configuration_file() + PyXCP.name = Path(sys.argv[0]).name + self.parse_command_line(argv[1:]) + self._setup_logger() + + def _read_configuration(self, file_name: str, emit_warning: bool = True) -> None: + self.read_configuration_file(file_name, emit_warning) self.general = General.instance(config=self.config, parent=self) self.transport = Transport.instance(parent=self) - self.profile_app = ProfileApp.instance(config=self.config, parent=self) - def read_configuration_file(self): - pth = Path(self.config_file) + def read_configuration_file(self, file_name: str, emit_warning: bool = True): + self.legacy_config: bool = False + + pth = Path(file_name) + if not pth.exists(): + raise FileNotFoundError(f"Configuration file {file_name!r} does not exist.") + suffix = pth.suffix.lower() if suffix == ".py": self.load_config_file(self.config_file) else: + self.legacy_config = True if suffix == ".json": reader = json elif suffix == ".toml": @@ -769,10 +880,11 @@ def read_configuration_file(self): else: raise ValueError(f"Unknown file type for config: {suffix}") with pth.open("r") as f: - warnings.warn("Old-style configuration file. Please user python based configuration.", DeprecationWarning) + if emit_warning: + self.log.warning(f"Legacy configuration file format ({suffix}), please use python based configuration.") cfg = reader.loads(f.read()) if cfg: - cfg = legacy.convert_config(cfg) + cfg = legacy.convert_config(cfg, self.log) self.config = cfg return cfg @@ -790,7 +902,7 @@ def read_configuration_file(self): ) ) - def _iterate_config_class(self, klass, class_names: typing.List[str], config) -> None: + def _iterate_config_class(self, klass, class_names: typing.List[str], config, out_file: io.IOBase = sys.stdout) -> None: sub_classes = [] class_path = ".".join(class_names) print( @@ -798,6 +910,7 @@ def _iterate_config_class(self, klass, class_names: typing.List[str], config) -> # {class_path} configuration # ------------------------------------------------------------------------------""", end="\n\n", + file=out_file, ) if hasattr(klass, "classes"): kkk = klass.classes @@ -809,48 +922,54 @@ def _iterate_config_class(self, klass, class_names: typing.List[str], config) -> if md.get("config"): help = md.get("help", "").lstrip() commented_lines = "\n".join([f"# {line}" for line in help.split("\n")]) - print(f"#{commented_lines}") + print(f"#{commented_lines}", file=out_file) value = tr.default() - if isinstance(tr, Instance) and tr.__class__.__name__ not in ("Dict",): + if isinstance(tr, Instance) and tr.__class__.__name__ not in ("Dict", "List"): continue - if isinstance(tr, Unicode) and value is not None: - value = f"'{value}'" if isinstance(tr, Enum): - print(f"# Choices: {tr.info()}") + print(f"# Choices: {tr.info()}", file=out_file) else: - print(f"# Type: {tr.info()}") - print(f"# Default: {value}") + print(f"# Type: {tr.info()}", file=out_file) + print(f"# Default: {value!r}", file=out_file) if name in config: cfg_value = config[name] - if isinstance(cfg_value, str): - cfg_value = f"'{cfg_value}'" - print(f"c.{class_path}.{name} = {cfg_value}", end="\n\n") + print(f"c.{class_path!s}.{name!s} = {cfg_value!r}", end="\n\n", file=out_file) else: - print(f"# c.{class_path}.{name} = {value}", end="\n\n") + print(f"# c.{class_path!s}.{name!s} = {value!r}", end="\n\n", file=out_file) if class_names is None: class_names = [] for sub_klass in sub_classes: - self._iterate_config_class(sub_klass, class_names + [sub_klass.__name__], config=config.get(sub_klass.__name__, {})) + self._iterate_config_class( + sub_klass, class_names + [sub_klass.__name__], config=config.get(sub_klass.__name__, {}), out_file=out_file + ) def generate_config_file(self, file_like: io.IOBase, config=None) -> None: - print("#") - print("# Configuration file for pyXCP.") - print("#") - print("c = get_config() # noqa", end="\n\n") + print("#", file=file_like) + print("# Configuration file for pyXCP.", file=file_like) + print("#", file=file_like) + print("c = get_config() # noqa", end="\n\n", file=file_like) for klass in self._classes_with_config_traits(): - self._iterate_config_class(klass, [klass.__name__], config=self.config.get(klass.__name__, {})) - print(self.config) + self._iterate_config_class( + klass, [klass.__name__], config=self.config.get(klass.__name__, {}) if config is None else {}, out_file=file_like + ) -class Configuration: - pass +application: typing.Optional[PyXCP] = None -def create_application(): +def create_application() -> PyXCP: + global application + if application is not None: + return application application = PyXCP() application.initialize(sys.argv) application.start() - # application.generate_config_file(sys.stdout) + return application + +def get_application() -> PyXCP: + global application + if application is None: + application = create_application() return application diff --git a/pyxcp/config/legacy.py b/pyxcp/config/legacy.py index 0e5c1fd..8f7b037 100644 --- a/pyxcp/config/legacy.py +++ b/pyxcp/config/legacy.py @@ -1,6 +1,6 @@ -import warnings from collections import defaultdict +from traitlets.config import LoggingConfigurable from traitlets.config.loader import Config @@ -22,11 +22,20 @@ "PROTOCOL": "Transport.Eth.protocol", "IPV6": "Transport.Eth.ipv6", "TCP_NODELAY": "Transport.Eth.tcp_nodelay", + # Usb + "SERIAL_NUMBER": "Transport.Usb.serial_number", + "CONFIGURATION_NUMBER": "Transport.Usb.configuration_number", + "INTERFACE_NUMBER": "Transport.Usb.interface_number", + "COMMAND_ENDPOINT_NUMBER": "Transport.Usb.command_endpoint_number", + "REPLY_ENDPOINT_NUMBER": "Transport.Usb.reply_endpoint_number", + "VENDOR_ID": "Transport.Usb.vendor_id", + "PRODUCT_ID": "Transport.Usb.product_id", + "LIBRARY": "Transport.Usb.library", # Can "CAN_DRIVER": "Transport.Can.interface", "CHANNEL": "Transport.Can.channel", "MAX_DLC_REQUIRED": "Transport.Can.max_dlc_required", - "MAX_CAN_FD_DLC": "Transport.Can.max_can_fd_dlc", + # "MAX_CAN_FD_DLC": "Transport.Can.max_can_fd_dlc", "PADDING_VALUE": "Transport.Can.padding_value", "CAN_USE_DEFAULT_LISTENER": "Transport.Can.use_default_listener", "CAN_ID_MASTER": "Transport.Can.can_id_master", @@ -71,14 +80,15 @@ def nested_dict_update(d: dict, key: str, value) -> None: sub_dict[key] = value -def convert_config(legacy_config: dict) -> Config: +def convert_config(legacy_config: dict, logger: LoggingConfigurable) -> Config: interface_name = None resolv = [] d = defaultdict(dict) for key, value in legacy_config.items(): + key = key.upper() item = LEGACY_KEYWORDS.get(key) if item is None: - warnings.warn(f"Unknown keyword '{key}' in config file") # TODO: logger + logger.warning(f"Unknown keyword {key!r} in config file") continue if key == "CAN_DRIVER": value = value.lower() diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index 1f8b497..1ec8603 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -17,7 +17,7 @@ class DaqList { const std::vector& measurements ) : m_name(meas_name), m_event_num(event_num), m_stim(stim), m_enable_timestamps(enable_timestamps) { - std::cout << "DAQ-List: " << meas_name << " " << event_num << " " << stim << " " << enable_timestamps << std::endl; + //std::cout << "DAQ-List: " << meas_name << " " << event_num << " " << stim << " " << enable_timestamps << std::endl; for (const auto& measurement : measurements) { auto const& [name, address, ext, dt_name] = measurement; m_measurements.emplace_back(McObject(name, address, ext, 0, dt_name)); diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index b6b3097..8ea9fe9 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -10,7 +10,7 @@ #include const std::map> TYPE_MAP = { - {"U8", { 0, 1 }}, + { "U8", { 0, 1 }}, { "I8", { 1, 1 }}, { "U16", { 2, 2 }}, { "I16", { 3, 2 }}, @@ -38,7 +38,15 @@ class McObject { m_type_index(-1) { if (data_type != "") { std::string dt_toupper; + + dt_toupper.resize(data_type.size()); + std::transform(data_type.begin(), data_type.end(), dt_toupper.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c); }); + + if (!TYPE_MAP.contains(dt_toupper)) { + throw std::runtime_error("Invalid data type: " + data_type); + } + const auto [ti, len] = TYPE_MAP.at(dt_toupper); m_type_index = ti; m_length = len; diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 7e5bfae..a5a1a3a 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -4,6 +4,7 @@ from typing import List from pyxcp import types +from pyxcp.config import get_application from pyxcp.cpp_ext import DaqList # , StimList from pyxcp.daq_stim.optimize import make_continuous_blocks from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing @@ -31,6 +32,7 @@ def __init__(self, file_name: str, daq_lists: List[DaqList]): self.file_name = file_name self.daq_lists = daq_lists self.setup_called = False + self.log = get_application().log def setup(self, write_multiple: bool = True): self.daq_info = self.xcp_master.getDaqInfo() @@ -43,6 +45,7 @@ def setup(self, write_multiple: bool = True): raise TypeError("DAQ configuration is static, cannot proceed.") self.supports_timestampes = properties["timestampSupported"] self.supports_prescaler = properties["prescalerSupported"] + self.supports_pid_off = properties["pidOffSupported"] if self.supports_timestampes: mode = resolution.get("timestampMode") self.ts_fixed = mode.get("fixed") @@ -104,12 +107,17 @@ def setup(self, write_multiple: bool = True): self.first_pids = [] daq_count = len(self.daq_lists) self.xcp_master.freeDaq() + # Allocate self.xcp_master.allocDaq(daq_count) + measurement_list = [] for i, daq_list in enumerate(self.daq_lists, self.min_daq): measurements = daq_list.measurements_opt + measurement_list.append((i, measurements)) odt_count = len(measurements) self.xcp_master.allocOdt(i, odt_count) + # Iterate again over ODT entries -- we need to respect sequencing requirements. + for i, measurements in measurement_list: for j, measurement in enumerate(measurements): entry_count = len(measurement.entries) self.xcp_master.allocOdtEntry(i, j, entry_count) @@ -132,6 +140,9 @@ def start(self): mode = 0x10 if daq_list.stim: mode |= 0x02 + ### + ## mode |= 0x20 + ### self.xcp_master.setDaqListMode( daqListNumber=i, mode=mode, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xFF # TODO: + MIN_DAQ ) @@ -142,22 +153,30 @@ def start(self): def stop(self): self.xcp_master.startStopSynch(0x00) - import time - - def reader(self): - """ - print("HEADERS") - philez = [] - for idx, d in enumerate(self.daq_lists): - philez.append(open(f"{d.name}.csv", "wt")) - print(d.name, d.header_names) - hdr = ",".join(["timestamp0", "timestamp1"] + d.header_names) - philez[idx].write(f"{hdr}\n") - print("DATA") - start_time = time.perf_counter() - for daq_list, ts0, ts1, payload in unfolder: - philez[daq_list].write(f'{ts0},{ts1},{", ".join([str(x) for x in payload])}\n') - for ph in philez: - ph.close() - print("ETA: ", time.perf_counter() - start_time, "seconds") - """ + +class DaqToCsv(DAQParser): + """Save a measurement as CSV files (one per DAQ-list).""" + + def post_setup(self): + self.log.debug("DaqCsv::post_setup()") + self.files = {} + for num, daq_list in enumerate(self.daq_lists): + if daq_list.stim: + continue + out_file = open(f"{daq_list.name}.csv", "w") + self.files[num] = out_file + hdr = ",".join(["timestamp0", "timestamp1"] + daq_list.header_names) + out_file.write(f"{hdr}\n") + + def on_daq_list(self, daq_list: int, ts0: float, ts1: float, payload: list): + self.files[daq_list].write(f"{ts0},{ts1},{', '.join([str(x) for x in payload])}\n") + + def finalize(self): + self.log.debug("DaqCsv::finalize()") + ## + ## NOTE: `finalize` is guaranteed to be called, but `post_setup` may fail for reasons. + ## So if you allocate resources in `post_setup` check if this really happened. + ## + if hasattr(self, "files"): + for f in self.files.values(): + f.close() diff --git a/pyxcp/daq_stim/linreg.hpp b/pyxcp/daq_stim/linreg.hpp new file mode 100644 index 0000000..583b6d4 --- /dev/null +++ b/pyxcp/daq_stim/linreg.hpp @@ -0,0 +1,383 @@ +// +// Created by Chris on 26.12.2023. +// + +#ifndef PYXCP_LINREG_HPP +#define PYXCP_LINREG_HPP + +#include +#include +#include + +#include +#include + +#if 0 + #include "linreg.h" + #include "print.h" + #include "servo_private.h" +#endif + +/* Maximum and minimum number of points used in regression, + defined as a power of 2 */ +#define MAX_SIZE 6 +#define MIN_SIZE 2 + +#define MAX_POINTS (1 << MAX_SIZE) + +/* Smoothing factor used for long-term prediction error */ +#define ERR_SMOOTH 0.02 +/* Number of updates used for initialization */ +#define ERR_INITIAL_UPDATES 10 +/* Maximum ratio of two err values to be considered equal */ +#define ERR_EQUALS 1.05 + +struct servo { + double max_frequency; + double step_threshold; + double first_step_threshold; + int first_update; + int64_t offset_threshold; + int num_offset_values; + int curr_offset_values; + + void (*destroy)(struct servo *servo); +f + double (*sample)(struct servo *servo, + int64_t offset, uint64_t local_ts, double weight, + enum servo_state *state); + + void (*sync_interval)(struct servo *servo, double interval); + + void (*reset)(struct servo *servo); + + double (*rate_ratio)(struct servo *servo); + + void (*leap)(struct servo *servo, int leap); +}; + + +/* Uncorrected local time vs remote time */ +struct point { + uint64_t x; + uint64_t y; + double w; +}; + +struct result { + /* Slope and intercept from latest regression */ + double slope; + double intercept; + /* Exponential moving average of prediction error */ + double err; + /* Number of initial err updates */ + int err_updates; +}; + +struct linreg_servo { + struct servo servo; + /* Circular buffer of points */ + struct point points[MAX_POINTS]; + /* Current time in x, y */ + struct point reference; + /* Number of stored points */ + unsigned int num_points; + /* Index of the newest point */ + unsigned int last_point; + /* Remainder from last update of reference.x */ + double x_remainder; + /* Local time stamp of last update */ + uint64_t last_update; + /* Regression results for all sizes */ + struct result results[MAX_SIZE - MIN_SIZE + 1]; + /* Selected size */ + unsigned int size; + /* Current frequency offset of the clock */ + double clock_freq; + /* Expected interval between updates */ + double update_interval; + /* Current ratio between remote and local frequency */ + double frequency_ratio; + /* Upcoming leap second */ + int leap; +}; + +class LinearRegression { + public: + + + private: + +}; + + +static void linreg_destroy(struct servo *servo) +{ + struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); + free(s); +} + +static void move_reference(struct linreg_servo *s, int64_t x, int64_t y) +{ + struct result *res; + unsigned int i; + + s->reference.x += x; + s->reference.y += y; + + /* Update intercepts for new reference */ + for (i = MIN_SIZE; i <= MAX_SIZE; i++) { + res = &s->results[i - MIN_SIZE]; + res->intercept += x * res->slope - y; + } +} + +static void update_reference(struct linreg_servo *s, uint64_t local_ts) +{ + double x_interval; + int64_t y_interval; + + if (s->last_update) { + y_interval = local_ts - s->last_update; + + /* Remove current frequency correction from the interval */ + x_interval = y_interval / (1.0 + s->clock_freq / 1e9); + x_interval += s->x_remainder; + s->x_remainder = x_interval - (int64_t)x_interval; + + move_reference(s, (int64_t)x_interval, y_interval); + } + + s->last_update = local_ts; +} + +static void add_sample(struct linreg_servo *s, int64_t offset, double weight) +{ + s->last_point = (s->last_point + 1) % MAX_POINTS; + + s->points[s->last_point].x = s->reference.x; + s->points[s->last_point].y = s->reference.y - offset; + s->points[s->last_point].w = weight; + + if (s->num_points < MAX_POINTS) + s->num_points++; +} + +static void regress(struct linreg_servo *s) +{ + double x, y, y0, e, x_sum, y_sum, xy_sum, x2_sum, w, w_sum; + unsigned int i, l, n, size; + struct result *res; + + x_sum = 0.0, y_sum = 0.0, xy_sum = 0.0, x2_sum = 0.0; w_sum = 0.0; + i = 0; + + y0 = (int64_t)(s->points[s->last_point].y - s->reference.y); + + for (size = MIN_SIZE; size <= MAX_SIZE; size++) { + n = 1 << size; + if (n > s->num_points) + /* Not enough points for this size */ + break; + + res = &s->results[size - MIN_SIZE]; + + /* Update moving average of the prediction error */ + if (res->slope) { + e = fabs(res->intercept - y0); + if (res->err_updates < ERR_INITIAL_UPDATES) { + res->err *= res->err_updates; + res->err += e; + res->err_updates++; + res->err /= res->err_updates; + } else { + res->err += ERR_SMOOTH * (e - res->err); + } + } + + for (; i < n; i++) { + /* Iterate points from newest to oldest */ + l = (MAX_POINTS + s->last_point - i) % MAX_POINTS; + + x = (int64_t)(s->points[l].x - s->reference.x); + y = (int64_t)(s->points[l].y - s->reference.y); + w = s->points[l].w; + + x_sum += x * w; + y_sum += y * w; + xy_sum += x * y * w; + x2_sum += x * x * w; + w_sum += w; + } + + /* Get new intercept and slope */ + res->slope = (xy_sum - x_sum * y_sum / w_sum) / + (x2_sum - x_sum * x_sum / w_sum); + res->intercept = (y_sum - res->slope * x_sum) / w_sum; + } +} + +static void update_size(struct linreg_servo *s) +{ + struct result *res; + double best_err; + int size, best_size; + + /* Find largest size with smallest prediction error */ + + best_size = 0; + best_err = 0.0; + + for (size = MIN_SIZE; size <= MAX_SIZE; size++) { + res = &s->results[size - MIN_SIZE]; + if ((!best_size && res->slope) || + (best_err * ERR_EQUALS > res->err && + res->err_updates >= ERR_INITIAL_UPDATES)) { + best_size = size; + best_err = res->err; + } + } + + s->size = best_size; +} + +static double linreg_sample(struct servo *servo, + int64_t offset, + uint64_t local_ts, + double weight, + enum servo_state *state) +{ + struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); + struct result *res; + int corr_interval; + + /* + * The current time and the time when will be the frequency of the + * clock actually updated is assumed here to be equal to local_ts + * (which is the time stamp of the received sync message). As long as + * the differences are smaller than the update interval, the loop + * should be robust enough to handle this simplification. + */ + + update_reference(s, local_ts); + add_sample(s, offset, weight); + regress(s); + + update_size(s); + + if (s->size < MIN_SIZE) { + /* Not enough points, wait for more */ + *state = SERVO_UNLOCKED; + return -s->clock_freq; + } + + res = &s->results[s->size - MIN_SIZE]; + + pr_debug("linreg: points %d slope %.9f intercept %.0f err %.0f", + 1 << s->size, res->slope, res->intercept, res->err); + + if ((servo->first_update && + servo->first_step_threshold && + servo->first_step_threshold < fabs(res->intercept)) || + (servo->step_threshold && + servo->step_threshold < fabs(res->intercept))) { + /* The clock will be stepped by offset */ + move_reference(s, 0, -offset); + s->last_update -= offset; + *state = SERVO_JUMP; + } else { + *state = SERVO_LOCKED; + } + + /* Set clock frequency to the slope */ + s->clock_freq = 1e9 * (res->slope - 1.0); + + /* + * Adjust the frequency to correct the time offset. Use longer + * correction interval with larger sizes to reduce the frequency error. + * The update interval is assumed to be not affected by the frequency + * adjustment. If it is (e.g. phc2sys controlling the system clock), a + * correction slowing down the clock will result in an overshoot. With + * the system clock's maximum adjustment of 10% that's acceptable. + */ + corr_interval = s->size <= 4 ? 1 : s->size / 2; + s->clock_freq += res->intercept / s->update_interval / corr_interval; + + /* Clamp the frequency to the allowed maximum */ + if (s->clock_freq > servo->max_frequency) + s->clock_freq = servo->max_frequency; + else if (s->clock_freq < -servo->max_frequency) + s->clock_freq = -servo->max_frequency; + + s->frequency_ratio = res->slope / (1.0 + s->clock_freq / 1e9); + + return -s->clock_freq; +} + +static void linreg_sync_interval(struct servo *servo, double interval) +{ + struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); + + s->update_interval = interval; +} + +static void linreg_reset(struct servo *servo) +{ + struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); + unsigned int i; + + s->num_points = 0; + s->last_update = 0; + s->size = 0; + s->frequency_ratio = 1.0; + + for (i = MIN_SIZE; i <= MAX_SIZE; i++) { + s->results[i - MIN_SIZE].slope = 0.0; + s->results[i - MIN_SIZE].err_updates = 0; + } +} + +static double linreg_rate_ratio(struct servo *servo) +{ + struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); + + return s->frequency_ratio; +} + +static void linreg_leap(struct servo *servo, int leap) +{ + struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); + + /* + * Move reference when leap second is applied to the reference + * time as if the clock was stepped in the opposite direction + */ + if (s->leap && !leap) + move_reference(s, 0, s->leap * 1000000000); + + s->leap = leap; +} + +struct servo *linreg_servo_create(double fadj) +{ + struct linreg_servo *s; + + s = calloc(1, sizeof(*s)); + if (!s) + return NULL; + + s->servo.destroy = linreg_destroy; + s->servo.sample = linreg_sample; + s->servo.sync_interval = linreg_sync_interval; + s->servo.reset = linreg_reset; + s->servo.rate_ratio = linreg_rate_ratio; + s->servo.leap = linreg_leap; + + s->clock_freq = -fadj; + s->frequency_ratio = 1.0; + + return &s->servo; +} + + +#endif // PYXCP_LINREG_HPP diff --git a/pyxcp/daq_stim/scheduler.hpp b/pyxcp/daq_stim/scheduler.hpp index b0bdbcb..ed4ed56 100644 --- a/pyxcp/daq_stim/scheduler.hpp +++ b/pyxcp/daq_stim/scheduler.hpp @@ -3,7 +3,9 @@ #ifndef STIM_SCHEDULER_HPP #define STIM_SCHEDULER_HPP -#define _CRT_SECURE_NO_WARNINGS (1) +#if !defined(_CRT_SECURE_NO_WARNINGS) + #define _CRT_SECURE_NO_WARNINGS (1) +#endif #include #include diff --git a/pyxcp/daq_stim/stim.hpp b/pyxcp/daq_stim/stim.hpp index abf6105..c83f2f3 100644 --- a/pyxcp/daq_stim/stim.hpp +++ b/pyxcp/daq_stim/stim.hpp @@ -28,109 +28,12 @@ namespace py = pybind11; constexpr double TMR_RESOLUTION = 1.0 / 1000.0; // Timer resolution is one millisecond. -class Timer { - public: - - Timer() { - QueryPerformanceFrequency(&m_ticks_per_sec); - QueryPerformanceCounter(&m_starting_time); - } - - /* - * Returns the number of µ-seconds since the timer was started. - */ - std::uint64_t elapsed() const { - LARGE_INTEGER now; - LARGE_INTEGER ela; - - QueryPerformanceCounter(&now); - ela.QuadPart = now.QuadPart - m_starting_time.QuadPart; - ela.QuadPart /= (m_ticks_per_sec.QuadPart / 1000000UL); - return ela.QuadPart; - } - - private: - - LARGE_INTEGER m_ticks_per_sec; - LARGE_INTEGER m_starting_time; -}; - -template -class TsQueue { - public: - - TsQueue() = default; - - TsQueue(const TsQueue& other) noexcept { - std::scoped_lock lock(other.m_mtx); - m_queue = other.m_queue; - } - - void put(T value) noexcept { - std::scoped_lock lock(m_mtx); - m_queue.push(value); - m_cond.notify_one(); - } - - std::shared_ptr get() noexcept { - std::unique_lock lock(m_mtx); - m_cond.wait(lock, [this] { return !m_queue.empty(); }); - std::shared_ptr result(std::make_shared(m_queue.front())); - m_queue.pop(); - return result; - } - - bool empty() const noexcept { - std::scoped_lock lock(m_mtx); - return m_queue.empty(); - } - - private: - - mutable std::mutex m_mtx; - std::queue m_queue; - std::condition_variable m_cond; -}; - -struct Mutex { - ~Mutex() { - //std::cout << "Mutex destructor called" << std::endl; - } - - Mutex(std::string_view name) : m_name(name) { - // std::cout << "Mutex constructor called: " << name << std::endl; - } - - void lock() { - // std::cout << "\tMutex lock: " << m_name << " : " << m_counter << std::endl; - m_mtx.lock(); - m_counter++; - #if 0 - if (!m_mtx.try_lock()) { - ///std::cout << "\t\tMutex LOCK FAILED!!!" << std::endl; - } else { - m_counter++; - } - #endif - } - - void unlock() { - //std::cout << "\tMutex un-lock: " << m_name << " : " << m_counter << std::endl; - m_mtx.unlock(); - m_counter--; - } - - int m_counter{ 0 }; - std::string m_name{}; - std::recursive_mutex m_mtx{}; -}; - constexpr std::uint8_t MIN_STIM_PID = 0x00; constexpr std::uint8_t MAX_STIM_PID = 0xBF; struct DAQListType {}; -///// From BlueParrot. +///// From BlueParrot XCP. using XcpDaq_ODTEntryIntegerType = std::uint16_t; using XcpDaq_ODTIntegerType = std::uint16_t; @@ -261,7 +164,7 @@ class FakeEnum { return 8; } - /*std::string*/py::bytes to_bytes(std::uint8_t length, std::string_view encoding) const { + py::bytes to_bytes(std::uint8_t length, std::string_view encoding) const { std::stringstream ss; ss << m_value; @@ -303,11 +206,9 @@ struct DaqEventInfo { } m_cycle_time = static_cast(cycle_time * 1000.0); } - // DBG_PRINTN("Event:", m_name, " time:", m_cycle_time, "periodic?", m_periodic, std::endl); } public: - std::string m_name; std::int8_t m_unit_exp; std::size_t m_cycle; @@ -371,17 +272,16 @@ class Stim { using feed_function_t = std::function)>; using send_function_t = std::function)>; - explicit Stim() { + explicit Stim(bool activate = true) : m_activate(activate) { if (timeBeginPeriod(100) == TIMERR_NOERROR) { - std::cout << "timeBeginPeriod() OK!!!" << std::endl; + // std::cout << "timeBeginPeriod() OK!!!" << std::endl; } else { - std::cout << "timeBeginPeriod() failed!!!" << std::endl; + // std::cout << "timeBeginPeriod() failed!!!" << std::endl; } DWORD task_index = 0UL; auto xxx = AvSetMmThreadCharacteristics("Pro Audio", &task_index); - std::cout << "AvSetMmThreadCharacteristics() " << xxx << ":" << task_index << std::endl; auto start = timeGetTime(); @@ -396,19 +296,24 @@ class Stim { std::uint16_t idx = 0; DBG_PRINTN("SET_DAQ_EVENT_INFO\n"); + if (!m_activate) { + return; + } for (const auto& event : daq_event_info) { - // m_daq_event_info.try_emplace(std::make_pair(idx, event)); m_daq_event_info.try_emplace(idx, event); - // m_daq_event_info[idx] = event; if (event.m_stim) { - std::cout << "\tSTIM: " << event.m_name << ":" << idx << std::endl; + // std::cout << "\tSTIM: " << event.m_name << ":" << idx << std::endl; } idx++; } } void setDaqPtr(std::uint16_t daqListNumber, std::uint16_t odtNumber, std::uint16_t odtEntryNumber) { + if (!m_activate) { + return; + } + if (!validateEntryNumber(daqListNumber, odtNumber, odtEntryNumber)) { return; } @@ -417,6 +322,10 @@ class Stim { } void clearDaqList(std::uint16_t daqListNumber) { + if (!m_activate) { + return; + } + if (!validateEntryNumber(daqListNumber)) { return; } @@ -426,6 +335,10 @@ class Stim { } void writeDaq(std::uint16_t bitOffset, std::uint16_t entrySize, std::uint16_t addressExt, std::uint32_t address) { + if (!m_activate) { + return; + } + auto [d, o, e] = m_daq_ptr; auto& entry = m_daq_lists[d].m_odts[o].m_entries[e]; @@ -447,15 +360,17 @@ class Stim { std::uint16_t mode, std::uint16_t daqListNumber, std::uint16_t eventChannelNumber, std::uint16_t prescaler, std::uint16_t priority ) { - //std::cout << "<<< Enter setDaqListMode() !!!\n"; - if (!validateEntryNumber(daqListNumber)) { - //std::cout << "Invalid DAQ list number!!!\b\n"; + if (!m_activate) { return; } + if (!validateEntryNumber(daqListNumber)) { + throw std::runtime_error("Invalid DAQ list number: " + std::to_string(eventChannelNumber) + "\n"); + } //std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" // << priority << std::endl; auto& entry = m_daq_lists.at(daqListNumber); + entry.mode = mode; entry.prescaler = prescaler; // The use of a prescaler is only used for DAQ lists with DIRECTION = DAQ. @@ -463,21 +378,27 @@ class Stim { entry.priority = priority; entry.event_channel_number = eventChannelNumber; + if (!m_daq_event_info.contains(eventChannelNumber)) { + throw std::runtime_error("Invalid Event channel number: " + std::to_string(eventChannelNumber) + "\n"); + } auto& event = m_daq_event_info.at(eventChannelNumber); + event.m_daq_lists.emplace(daqListNumber); if ((mode & DIRECTION_STIM) == DIRECTION_STIM) { - std::cout << "\tSTIM-MODE!!!\n"; + //std::cout << "\tSTIM-MODE!!!\n"; m_stim_lists.emplace(daqListNumber); - std::cout << "\t\tEvent: " << event.m_name << " ==> " << event.m_cycle_time << " - " << event.m_periodic << std::endl; + //std::cout << "\t\tEvent: " << event.m_name << " ==> " << event.m_cycle_time << " - " << event.m_periodic << std::endl; calculate_scheduler_period(event.m_cycle_time); } - //std::cout << ">>> Exit setDaqListMode() !!!\n"; } void startStopDaqList(std::uint16_t mode, std::uint16_t daqListNumber) { + if (!m_activate) { + return; + } if (!validateEntryNumber(daqListNumber)) { return; } @@ -499,9 +420,12 @@ class Stim { } void startStopSynch(std::uint16_t mode) { + if (!m_activate) { + return; + } //std::cout << "START_STOP_SYNCH " << mode << ":" << std::endl; - send(0x00, { 0x04, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }); +// send(0x00, { 0x04, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }); for (auto dl : m_stim_lists) { //std::cout << "\tRunning list: " << dl << std::endl; @@ -514,17 +438,26 @@ class Stim { } void writeDaqMultiple(/* const std::vector&*/ std::uint16_t daqElements) { + if (!m_activate) { + return; + } } void dtoCtrProperties( std::uint16_t modifier, std::uint16_t eventChannel, std::uint16_t relatedEventChannel, std::uint16_t mode ) { + if (!m_activate) { + return; + } } void setDaqPackedMode( std::uint16_t, std::uint16_t daqListNumber, std::uint16_t daqPackedMode, std::uint16_t dpmTimestampMode = 0, std::uint16_t dpmSampleCount = 0 ) { + if (!m_activate) { + return; + } if (!validateEntryNumber(daqListNumber)) { return; } @@ -532,41 +465,53 @@ class Stim { } void clear() { + if (!m_activate) { + return; + } m_daq_lists.clear(); - // m_daq_event_info.clear(); m_stim_lists.clear(); m_first_pids.clear(); } void freeDaq() { - //std::cout << "FREE_DAQ\n"; + if (!m_activate) { + return; + } clear(); } void allocDaq(std::uint16_t daqCount) { + if (!m_activate) { + return; + } m_daq_lists.resize(daqCount); - //std::cout << "ALLOC_DAQ " << daqCount << std::endl; std::for_each(m_daq_lists.cbegin(), m_daq_lists.cend(), [](auto elem) { elem.clear(); }); } void allocOdt(std::uint16_t daqListNumber, std::uint16_t odtCount) { + if (!m_activate) { + return; + } if (!validateEntryNumber(daqListNumber)) { return; } - //std::cout << "ALLOC_ODT " << daqListNumber << ":" << odtCount << std::endl; m_daq_lists[daqListNumber].resize(odtCount); } void allocOdtEntry(std::uint16_t daqListNumber, std::uint16_t odtNumber, std::uint16_t odtEntriesCount) { + if (!m_activate) { + return; + } if (!validateEntryNumber(daqListNumber, odtNumber)) { return; } - //std::cout << "ALLOC_ODT_ENTRY " << daqListNumber << ":" << odtNumber << ":" << odtEntriesCount << std::endl; m_daq_lists[daqListNumber].m_odts[odtNumber].resize(odtEntriesCount); } void set_first_pid(std::uint16_t daqListNumber, std::uint16_t firstPid) { - //std::cout << "set_first_pid(" << daqListNumber << ", " << firstPid << ")\n"; + if (!m_activate) { + return; + } m_first_pids[daqListNumber] = firstPid; } @@ -579,6 +524,9 @@ class Stim { } void send(std::uint8_t stim_list_num, const std::vector frame) { + if (!m_activate) { + return; + } if (m_send_function) { m_send_function.value()(FakeEnum(stim_list_num), frame); } @@ -587,25 +535,29 @@ class Stim { protected: void calculate_scheduler_period(std::size_t value) { + if (!m_activate) { + return; + } if (!m_scheduler_period) { m_scheduler_period = value; - //std::cout << "\tSet Period\n"; } if (!m_scheduler_max_value) { m_scheduler_max_value = value; - //std::cout << "\tSet Max\n"; } - std::cout << "\tSCHED_Value: " << value << " - BEFORE: " << *m_scheduler_period << ":" << *m_scheduler_max_value << std::endl; + // std::cout << "\tSCHED_Value: " << value << " - BEFORE: " << *m_scheduler_period << ":" << *m_scheduler_max_value << std::endl; m_scheduler_period = std::gcd(*m_scheduler_period, value); m_scheduler_max_value = std::lcm(*m_scheduler_max_value, value); - std::cout << "\tSCHED_Period: " << *m_scheduler_period << " max: " << *m_scheduler_max_value << std::endl; + // std::cout << "\tSCHED_Period: " << *m_scheduler_period << " max: " << *m_scheduler_max_value << std::endl; } bool validateEntryNumber( std::uint16_t daqListNumber, std::optional odtNumber = std::nullopt, std::optional odtEntryNumber = std::nullopt ) const { + if (!m_activate) { + return false; + } if (daqListNumber >= std::size(m_daq_lists)) { return false; } @@ -625,7 +577,7 @@ class Stim { } private: - + bool m_activate; StimParameters m_params{}; std::vector m_daq_lists{}; std::tuple m_daq_ptr; diff --git a/pyxcp/daq_stim/stim_wrapper.cpp b/pyxcp/daq_stim/stim_wrapper.cpp index 57c726c..f56cc62 100644 --- a/pyxcp/daq_stim/stim_wrapper.cpp +++ b/pyxcp/daq_stim/stim_wrapper.cpp @@ -17,7 +17,7 @@ PYBIND11_MODULE(stim, m) { ); py::class_(m, "Stim") - .def(py::init<>()) + .def(py::init()) .def("setDaqEventInfo", &Stim::setDaqEventInfo) .def("clear", &Stim::clear) .def("freeDaq", &Stim::freeDaq) diff --git a/pyxcp/dllif.py b/pyxcp/dllif.py index bec6bc2..8f00fa2 100644 --- a/pyxcp/dllif.py +++ b/pyxcp/dllif.py @@ -33,7 +33,7 @@ class SeedNKeyError(Exception): elif bwidth == "32bit": use_ctypes = True else: - raise RuntimeError(f"Platform '{sys.platform}' currently not supported.") + raise RuntimeError(f"Platform {sys.platform!r} currently not supported.") def getKey(logger, dllName: str, privilege: int, seed: str, assume_same_bit_width: bool): @@ -46,7 +46,7 @@ def getKey(logger, dllName: str, privilege: int, seed: str, assume_same_bit_widt try: lib = ctypes.cdll.LoadLibrary(dllName) except OSError: - logger.error(f"Could not load DLL '{dllName}' -- Probably an 64bit vs 32bit issue?") + logger.error(f"Could not load DLL {dllName!r} -- Probably an 64bit vs 32bit issue?") return (SeedNKeyResult.ERR_COULD_NOT_LOAD_DLL, None) func = lib.XCP_ComputeKeyFromSeed func.restype = ctypes.c_uint32 @@ -75,14 +75,17 @@ def getKey(logger, dllName: str, privilege: int, seed: str, assume_same_bit_widt shell=False, ) # nosec except FileNotFoundError as exc: - logger.error(f"Could not find executable '{LOADER}' -- {exc}") + logger.error(f"Could not find executable {LOADER!r} -- {exc}") return (SeedNKeyResult.ERR_COULD_NOT_LOAD_DLL, None) except OSError as exc: - logger.error(f"Cannot execute {LOADER} -- {exc}") + logger.error(f"Cannot execute {LOADER!r} -- {exc}") return (SeedNKeyResult.ERR_COULD_NOT_LOAD_DLL, None) key = p0.stdout.read() + p0.stdout.close() + p0.kill() + p0.wait() if not key: - logger.error(f"Something went wrong while calling seed-and-key-DLL '{dllName}'") + logger.error(f"Something went wrong while calling seed-and-key-DLL {dllName!r}") return (SeedNKeyResult.ERR_COULD_NOT_LOAD_DLL, None) res = re.split(b"\r?\n", key) returnCode = int(res[0]) diff --git a/pyxcp/examples/conf_can_vector.toml b/pyxcp/examples/conf_can_vector.toml index 53dc1f1..59aeebc 100644 --- a/pyxcp/examples/conf_can_vector.toml +++ b/pyxcp/examples/conf_can_vector.toml @@ -1,9 +1,11 @@ TRANSPORT = "CAN" CAN_DRIVER = "Vector" CAN_USE_DEFAULT_LISTENER = true -CHANNEL = "1" +CHANNEL = "0" CAN_ID_MASTER = 2 CAN_ID_SLAVE = 1 CAN_ID_BROADCAST = 256 -MAX_DLC_REQUIRED = false +# MAX_DLC_REQUIRED = true CREATE_DAQ_TIMESTAMPS = false +BITRATE=1000000 +SEED_N_KEY_DLL = "SeedNKeyXcp.dll" diff --git a/pyxcp/examples/conf_eth.toml b/pyxcp/examples/conf_eth.toml index 05a143f..65ff82e 100644 --- a/pyxcp/examples/conf_eth.toml +++ b/pyxcp/examples/conf_eth.toml @@ -1,11 +1,10 @@ -# TRANSPORT = "ETH" -TRANSPORT = "CAN" +TRANSPORT = "ETH" +# TRANSPORT = "CAN" CAN_DRIVER = "kvaser" CHANNEL=0 HOST = "localhost" PORT = 5555 PROTOCOL = "UDP" IPV6 = false -CREATE_DAQ_TIMESTAMPS = false +CREATE_DAQ_TIMESTAMPS = true SEED_N_KEY_DLL = "SeedNKeyXcp.dll" - diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index 0862df0..1ae9115 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -42,6 +42,16 @@ class InternalError(Exception): class SystemExit(Exception): """""" + def __init__(self, msg: str, error_code: int = None, *args, **kws): + super().__init__(*args, **kws) + self.error_code = error_code + self.msg = msg + + def __str__(self): + return f"SystemExit(error_code={self.error_code}, message={self.msg!r})" + + __repr__ = __str__ + class UnrecoverableError(Exception): """""" @@ -73,10 +83,10 @@ def getActions(service, error_code): eh = getErrorHandler(service) if eh is None: raise InternalError(f"Invalid Service 0x{service:02x}") - print(f"Try to handle error -- Service: {service.name} Error-Code: {error_code}") + # print(f"Try to handle error -- Service: {service.name} Error-Code: {error_code}") handler = eh.get(error_str) if handler is None: - raise SystemExit(f"Service '{service.name}' has no handler for '{error_code}'.") + raise SystemExit(f"Service {service.name!r} has no handler for {error_code}.", error_code=error_code) preActions, actions = handler return preActions, actions @@ -182,7 +192,7 @@ def __init__(self, instance, func, arguments, error_code=None): self.logger = logging.getLogger("PyXCP") def __str__(self): - return f"Handler(func = {func_name(self.func)} arguments = {self.arguments} service = {self.service} error_code = {self.error_code})" + return f"Handler(func = {func_name(self.func)} -- {self.arguments} service = {self.service} error_code = {self.error_code})" def __eq__(self, other): if other is None: @@ -200,7 +210,7 @@ def repeater(self, value): self._repeater = value def execute(self): - self.logger.debug(f"Execute({func_name(self.func)} arguments = {self.arguments})") + self.logger.debug(f"Execute({func_name(self.func)} -- {self.arguments})") if isinstance(self.func, types.MethodType): return self.func(*self.arguments.args, **self.arguments.kwargs) else: @@ -245,15 +255,17 @@ def actions(self, preActions, actions): if item == Action.NONE: pass elif item == Action.DISPLAY_ERROR: - raise SystemExit("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error (DISPLAY_ERROR).", self.error_code) elif item == Action.RETRY_SYNTAX: - raise SystemExit("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error (RETRY_SYNTAX).", self.error_code) elif item == Action.RETRY_PARAM: - raise SystemExit("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error (RETRY_PARAM).", self.error_code) elif item == Action.USE_A2L: - raise SystemExit("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error (USE_A2L).", self.error_code) elif item == Action.USE_ALTERATIVE: - raise SystemExit("Could not proceed due to unhandled error.") # TODO: check alternatives. + raise SystemExit( + "Could not proceed due to unhandled error (USE_ALTERATIVE).", self.error_code + ) # TODO: check alternatives. elif item == Action.REPEAT: repetitionCount = Repeater.REPEAT elif item == Action.REPEAT_2_TIMES: @@ -261,13 +273,13 @@ def actions(self, preActions, actions): elif item == Action.REPEAT_INF_TIMES: repetitionCount = Repeater.INFINITE elif item == Action.RESTART_SESSION: - raise SystemExit("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error (RESTART_SESSION).", self.error_code) elif item == Action.TERMINATE_SESSION: - raise SystemExit("Could not proceed due to unhandled error.") + raise SystemExit("Could not proceed due to unhandled error (TERMINATE_SESSION).", self.error_code) elif item == Action.SKIP: pass elif item == Action.NEW_FLASH_WARE: - raise SystemExit("Could not proceed due to unhandled error") + raise SystemExit("Could not proceed due to unhandled error (NEW_FLASH_WARE)", self.error_code) return result_pre_actions, result_actions, Repeater(repetitionCount) @@ -334,11 +346,13 @@ def __call__(self, inst, func, arguments): handler = self.handlerStack.tos() res = handler.execute() except XcpResponseError as e: - # self.logger.critical(f"XcpResponseError [{str(e)}]") + # self.logger.critical(f"XcpResponseError [{e.get_error_code()}]") self.error_code = e.get_error_code() + handler.error_code = self.error_code except XcpTimeoutError: # self.logger.error(f"XcpTimeoutError [{str(e)}]") self.error_code = XcpError.ERR_TIMEOUT + handler.error_code = self.error_code except TimeoutError: raise except can.CanError: @@ -370,7 +384,7 @@ def __call__(self, inst, func, arguments): continue else: raise UnrecoverableError( - f"Max. repetition count reached while trying to execute service '{handler.func.__name__}'." + f"Max. repetition count reached while trying to execute service {handler.func.__name__!r}." ) finally: # cleanup of class variables diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 9f7eada..a1e047f 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -11,7 +11,7 @@ import traceback import warnings from time import sleep -from typing import Callable, Collection, Dict, List, Optional +from typing import Any, Callable, Collection, Dict, List, Optional, Tuple from pyxcp import checksum, types from pyxcp.constants import ( @@ -25,7 +25,7 @@ makeWordUnpacker, ) from pyxcp.daq_stim.stim import DaqEventInfo, Stim -from pyxcp.master.errorhandler import disable_error_handling, wrapped +from pyxcp.master.errorhandler import SystemExit, disable_error_handling, wrapped from pyxcp.transport.base import createTransport from pyxcp.utils import SHORT_SLEEP, decode_bytes, delay @@ -75,7 +75,7 @@ def __init__(self, transport_name: str, config, policy=None): transport_config = config.transport self.transport = createTransport(transport_name, transport_config, policy) - self.stim = Stim() + self.stim = Stim(self.config.stim_support) self.stim.clear() self.stim.set_policy_feeder(self.transport.policy.feed) self.stim.set_frame_sender(self.transport.block_request) @@ -109,6 +109,7 @@ def __init__(self, transport_name: str, config, policy=None): self.disconnect_response_optional = self.config.disconnect_response_optional self.slaveProperties = SlaveProperties() self.slaveProperties.pgmProcessor = SlaveProperties() + self.slaveProperties.transport_layer = self.transport_name.upper() def __enter__(self): """Context manager entry part.""" @@ -203,16 +204,19 @@ def connect(self, mode=0x00): self.DWORD_unpack = makeDWordUnpacker(byteOrderPrefix) self.DLONG_pack = makeDLongPacker(byteOrderPrefix) self.DLONG_unpack = makeDLongUnpacker(byteOrderPrefix) - + self.slaveProperties.bytesPerElement = None # Download/Upload commands are using element- not byte-count. if self.slaveProperties.addressGranularity == types.AddressGranularity.BYTE: self.AG_pack = struct.Struct(" (self.slaveProperties.maxCto - 1): - block_response = self.transport.block_receive(length_required=(length - len(response))) + if byte_count > (self.slaveProperties.maxCto - 1): + block_response = self.transport.block_receive(length_required=(byte_count - len(response))) response += block_response elif self.transport_name == "can": # larger sizes will send in multiple CAN messages # each valid message will start with 0xFF followed by the upload bytes # the last message might be padded to the required DLC - rem = length - len(response) + rem = byte_count - len(response) while rem: if len(self.transport.resQueue): data = self.transport.resQueue.popleft() response += data[1 : rem + 1] - rem = length - len(response) + rem = byte_count - len(response) else: sleep(SHORT_SLEEP) - return response @wrapped @@ -452,6 +456,8 @@ def shortUpload(self, length: int, address: int, addressExt: int = 0x00): Parameters ---------- + length : int + Number of elements (address granularity). address : int addressExt : int @@ -460,7 +466,12 @@ def shortUpload(self, length: int, address: int, addressExt: int = 0x00): bytes """ addr = self.DWORD_pack(address) - return self.transport.request(types.Command.SHORT_UPLOAD, length, 0, addressExt, *addr) + byte_count = length * self.slaveProperties.bytesPerElement + max_byte_count = self.slaveProperties.maxCto - 1 + if byte_count > max_byte_count: + self.logger.warn(f"SHORT_UPLOAD: {byte_count} bytes exceeds the maximum value of {max_byte_count}.") + response = self.transport.request(types.Command.SHORT_UPLOAD, length, 0, addressExt, *addr) + return response[:byte_count] @wrapped def buildChecksum(self, blocksize: int): @@ -1619,8 +1630,8 @@ def getSlaveID(self, mode: int): def getDaqId(self, daqListNumber: int): response = self.transportLayerCmd(types.TransportLayerCommands.GET_DAQ_ID, *self.WORD_pack(daqListNumber)) - if response: - return types.GetDaqIdResponse.parse(response, byteOrder=self.slaveProperties.byteOrder) + # if response: + return types.GetDaqIdResponse.parse(response, byteOrder=self.slaveProperties.byteOrder) def setDaqId(self, daqListNumber: int, identifier: int): response = self.transportLayerCmd( @@ -1781,7 +1792,7 @@ def cond_unlock(self, resources=None): MAX_PAYLOAD = self.slaveProperties["maxCto"] - 2 if not (self.seed_n_key_dll or self.seed_n_key_function): - raise RuntimeError("Neither seed and key DLL or function specified, cannot proceed.") + raise RuntimeError("Neither seed and key DLL nor function specified, cannot proceed.") # TODO: ConfigurationError if resources is None: result = [] if self.slaveProperties["supportsCalpag"]: @@ -1797,7 +1808,7 @@ def cond_unlock(self, resources=None): resource_names = [r.lower() for r in re.split(r"[ ,]", resources) if r] for name in resource_names: if name not in types.RESOURCE_VALUES: - raise ValueError(f"Invalid resource name '{name}'.") + raise ValueError(f"Invalid resource name {name!r}.") if not protection_status[name]: continue resource_value = types.RESOURCE_VALUES[name] @@ -1811,14 +1822,14 @@ def cond_unlock(self, resources=None): while remaining > 0: result = self.getSeed(types.XcpGetSeedMode.REMAINING, resource_value) seed.extend(list(result.seed)) - remaining = result.length - self.logger.debug(f"Got seed {seed} for resource {resource_value}.") + remaining -= result.length + self.logger.debug(f"Got seed {seed!r} for resource {resource_value!r}.") if self.seed_n_key_function: key = self.seed_n_key_function(resource_value, bytes(seed)) - self.logger.debug(f"Using seed and key function '{self.seed_n_key_function.__name__}()'.") + self.logger.debug(f"Using seed and key function {self.seed_n_key_function.__name__!r}().") result = SeedNKeyResult.ACK elif self.seed_n_key_dll: - self.logger.debug(f"Using seed and key DLL '{self.seed_n_key_dll}'.") + self.logger.debug(f"Using seed and key DLL {self.seed_n_key_dll!r}.") result, key = getKey( self.logger, self.seed_n_key_dll, @@ -1828,16 +1839,16 @@ def cond_unlock(self, resources=None): ) if result == SeedNKeyResult.ACK: key = list(key) - self.logger.debug(f"Unlocking resource {resource_value} with key {key}.") - total_length = len(key) - offset = 0 - while offset < total_length: - data = key[offset : offset + MAX_PAYLOAD] - key_length = len(data) - offset += key_length - self.unlock(key_length, data) + self.logger.debug(f"Unlocking resource {resource_value!r} with key {key!r}.") + remaining = len(key) + while key: + data = key[:MAX_PAYLOAD] + key_len = len(data) + self.unlock(remaining, data) + key = key[MAX_PAYLOAD:] + remaining -= key_len else: - raise SeedNKeyError(f"SeedAndKey DLL returned: {SeedNKeyResult(result).name}") + raise SeedNKeyError(f"SeedAndKey DLL returned: {SeedNKeyResult(result).name!r}") def identifier(self, id_value: int) -> str: """Return the identifier for the given value. @@ -1915,18 +1926,56 @@ def generate(): gen = make_generator(scan_ranges) for id_value, name in gen: - response = b"" - try: - response = self.identifier(id_value) - except types.XcpResponseError: - # don't depend on confirming implementation, i.e.: ID not implemented ==> empty response. - pass - except Exception: - raise - if response: + status, response = self.try_command(self.identifier, id_value) + if status == types.TryCommandResult.OK and response: result[name] = response + elif status == types.TryCommandResult.XCP_ERROR and response.error_code == types.XcpError.ERR_CMD_UNKNOWN: + break # Nothing to do here. + elif status == types.TryCommandResult.OTHER_ERROR: + raise RuntimeError(f"Error while scanning for ID {id_value}: {response!r}") return result + def try_command(self, cmd: Callable, *args, **kws) -> Tuple[types.TryCommandResult, Any]: + """Call master functions and handle XCP errors more gracefuly. + + Parameter + --------- + cmd: Callable + args: list + variable length arguments to `cmd`. + kws: dict + keyword arguments to `cmd`. + + `extra_msg`: str + Additional info to log message (not passed to `cmd`). + + Returns + ------- + + Note + ---- + Mainly used for plug-and-play applications, e.g. `id_scanner` may confronted with `ERR_OUT_OF_RANGE` errors, which + is normal for this kind of applications -- or to test for optional commands. + Use carefuly not to hide serious error causes. + """ + try: + extra_msg: Optional[str] = kws.get("extra_msg") + if extra_msg: + kws.pop("extra_msg") + res = cmd(*args, **kws) + except SystemExit as e: + if e.error_code == types.XcpError.ERR_CMD_UNKNOWN: + # This is a rather common use-case, so let the user know that there is some functionality missing. + if extra_msg: + self.logger.warning(f"Optional command {cmd.__name__!r} not implemented -- {extra_msg!r}") + else: + self.logger.warning(f"Optional command {cmd.__name__!r} not implemented.") + return (types.TryCommandResult.XCP_ERROR, e) + except Exception as e: + return (types.TryCommandResult.OTHER_ERROR, e) + else: + return (types.TryCommandResult.OK, res) + def ticks_to_seconds(ticks, resolution): """Convert DAQ timestamp/tick value to seconds. @@ -1940,6 +1989,7 @@ def ticks_to_seconds(ticks, resolution): warnings.warn( "ticks_to_seconds() deprecated, use factory :func:`make_tick_converter` instead.", Warning, + stacklevel=1, ) return (10 ** types.DAQ_TIMESTAMP_UNIT_TO_EXP[resolution.timestampMode.unit]) * resolution.timestampTicks * ticks diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 03efa8e..01636aa 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -49,22 +49,30 @@ auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> Ty { template<> auto get_value(blob_t const * buf, std::uint32_t offset) -> float { - return static_cast(get_value(buf, offset)); + auto tmp = get_value(buf, offset); + + return *(reinterpret_cast(&tmp)); } template<> auto get_value(blob_t const * buf, std::uint32_t offset) -> double { - return static_cast(get_value(buf, offset)); + auto tmp = get_value(buf, offset); + + return *(reinterpret_cast(&tmp)); } template<> auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> float { - return static_cast(get_value_swapped(buf, offset)); + auto tmp = get_value_swapped(buf, offset); + + return *(reinterpret_cast(&tmp)); } template<> auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> double { - return static_cast(get_value_swapped(buf, offset)); + auto tmp = get_value_swapped(buf, offset); + + return *(reinterpret_cast(&tmp)); } template<> @@ -152,22 +160,22 @@ void set_value_swapped(blob_t * buf, std::uint32_t offset, std::in template<> void set_value(blob_t * buf, std::uint32_t offset, float value) { - set_value(buf, offset, static_cast(value)); + set_value(buf, offset, *reinterpret_cast(&value)); } template<> void set_value_swapped(blob_t * buf, std::uint32_t offset, float value) { - set_value_swapped(buf, offset, static_cast(value)); + set_value_swapped(buf, offset, *reinterpret_cast(&value)); } template<> void set_value(blob_t * buf, std::uint32_t offset, double value) { - set_value(buf, offset, static_cast(value)); + set_value(buf, offset, *reinterpret_cast(&value)); } template<> void set_value_swapped(blob_t * buf, std::uint32_t offset, double value) { - set_value_swapped(buf, offset, static_cast(value)); + set_value_swapped(buf, offset, *reinterpret_cast(&value)); } diff --git a/pyxcp/scripts/xcp_info.py b/pyxcp/scripts/xcp_info.py index 87b12e0..ca493ad 100644 --- a/pyxcp/scripts/xcp_info.py +++ b/pyxcp/scripts/xcp_info.py @@ -1,74 +1,82 @@ #!/usr/bin/env python -"""Very basic hello-world example. + +"""XCP info/exploration tool. """ + from pprint import pprint from pyxcp.cmdline import ArgumentParser +from pyxcp.types import TryCommandResult -daq_info = False - - -def callout(master, args): - global daq_info - if args.daq_info: - daq_info = True - +ap = ArgumentParser(description="XCP info/exploration tool.") -ap = ArgumentParser(description="pyXCP hello world.", callout=callout) -ap.parser.add_argument( - "-d", - "--daq-info", - dest="daq_info", - help="Display DAQ-info", - default=False, - action="store_true", -) with ap.run() as x: x.connect() if x.slaveProperties.optionalCommMode: - x.getCommModeInfo() - identifier = x.identifier(0x01) + x.try_command(x.getCommModeInfo, extra_msg="availability signaled by CONNECT, this may be a slave configuration error.") print("\nSlave Properties:") print("=================") - print(f"ID: '{identifier}'") pprint(x.slaveProperties) + + result = x.id_scanner() + print("\n") + print("Implemented IDs:") + print("================") + pprint(result) cps = x.getCurrentProtectionStatus() print("\nProtection Status") print("=================") for k, v in cps.items(): print(f" {k:6s}: {v}") - if daq_info: - dqp = x.getDaqProcessorInfo() - print("\nDAQ Processor Info:") - print("===================") - print(dqp) - print("\nDAQ Events:") - print("===========") - for idx in range(dqp.maxEventChannel): - evt = x.getDaqEventInfo(idx) - length = evt.eventChannelNameLength - name = x.pull(length).decode("utf-8") - dq = "DAQ" if evt.daqEventProperties.daq else "" - st = "STIM" if evt.daqEventProperties.stim else "" - dq_st = dq + " " + st - print(f' [{idx:04}] "{name:s}"') - print(f" dir: {dq_st}") - print(f" packed: {evt.daqEventProperties.packed}") - PFX_CONS = "CONSISTENCY_" - print(f" consistency: {evt.daqEventProperties.consistency.strip(PFX_CONS)}") - print(f" max. DAQ lists: {evt.maxDaqList}") - PFX_TU = "EVENT_CHANNEL_TIME_UNIT_" - print(f" unit: {evt.eventChannelTimeUnit.strip(PFX_TU)}") - print(f" cycle: {evt.eventChannelTimeCycle or 'SPORADIC'}") - print(f" priority {evt.eventChannelPriority}") + x.cond_unlock() + print("\nDAQ Info:") + print("=========") + daq_info = x.getDaqInfo() + pprint(daq_info) + + daq_pro = daq_info["processor"] + num_predefined = daq_pro["minDaq"] + print("\nPredefined DAQ-Lists") + print("====================") + if num_predefined > 0: + print(f"There are {num_predefined} predefined DAQ-lists") + for _daq_list in range(num_predefined): + pass + else: + print("*** NO Predefined DAQ-Lists") + daq_properties = daq_pro["properties"] + if x.slaveProperties.transport_layer == "CAN": + if daq_properties["pidOffSupported"]: + print("*** pidOffSupported -- i.e. one CAN-ID per DAQ-list.") + else: + print("*** NO support for PID_OFF") + + print("\nPAG Info:") + print("=========") + if x.slaveProperties.supportsCalpag: + status, pag = x.try_command(x.getPagProcessorInfo) + if status == TryCommandResult.OK: + pprint(pag) + for _idx in range(pag.maxSegments): + # x.getCalPage(0x01, idx) # ECU Access + x.getSegmentInfo() + else: + print("*** PAGING IS NOT SUPPORTED.") - dqr = x.getDaqResolutionInfo() - print("\nDAQ Resolution Info:") - print("====================") - print(dqr) - for idx in range(dqp.maxDaq): - print(f"\nDAQ List Info #{idx}") - print("=================") - print(f"{x.getDaqListInfo(idx)}") + print("\nPGM Info:") + print("=========") + if x.slaveProperties.supportsPgm: + status, pgm = x.try_command(x.getPgmProcessorInfo) + if status == TryCommandResult.OK: + pprint(pgm) + else: + print("*** FLASH PROGRAMMING IS NOT SUPPORTED.") + + if x.slaveProperties.transport_layer == "CAN": + print("OK, CAN!!!") + status, res = x.try_command(x.getDaqId, 0) + print(status, res) x.disconnect() + + print("\nDone.") diff --git a/pyxcp/tests/test_can.py b/pyxcp/tests/test_can.py index 568ab71..f7d90f5 100644 --- a/pyxcp/tests/test_can.py +++ b/pyxcp/tests/test_can.py @@ -8,6 +8,7 @@ IdentifierOutOfRangeError, calculateFilter, isExtendedIdentifier, + padFrame, setDLC, stripIdentifier, ) @@ -231,3 +232,1093 @@ def test_identifier_repr_does_the_trick1(capsys): def test_identifier_repr_does_the_trick2(capsys): i = Identifier(101 | CAN_EXTENDED_ID) assert eval(repr(i)) == Identifier(101 | CAN_EXTENDED_ID) # nosec + + +def test_filter_for_identifier_standard(capsys): + i = Identifier(0x101) + assert i.create_filter_from_id() == {"can_id": 0x101, "can_mask": 0x7FF, "extended": False} + + +def test_filter_for_identifier_extended(capsys): + i = Identifier(0x101 | CAN_EXTENDED_ID) + assert i.create_filter_from_id() == {"can_id": 0x101, "can_mask": 0x1FFFFFFF, "extended": True} + + +def test_pad_frame_no_padding_1(): + frame = bytearray(b"\xaa") + padded_frame = bytearray(b"\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_2(): + frame = bytearray(b"\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_3(): + frame = bytearray(b"\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_4(): + frame = bytearray(b"\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_5(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_6(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_7(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_8(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_9(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_10(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_11(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_12(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_13(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_14(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_15(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_16(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_17(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_18(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_19(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_20(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_21(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_22(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_23(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_24(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_25(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_26(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_27(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_28(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_29(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_30(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_31(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_32(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_33(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_34(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_35(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_36(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_37(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_38(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_39(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_40(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_41(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_42(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_43(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_44(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_45(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_46(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_47(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_48(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_49(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_50(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_51(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_52(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_53(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_54(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_55(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_56(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_57(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_58(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_59(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_60(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_61(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_62(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_63(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_no_padding_64(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + assert padFrame(frame, False, 0x00) == padded_frame + + +def test_pad_frame_padded_1(): + frame = bytearray(b"\xaa") + padded_frame = bytearray(b"\xaa\x00\x00\x00\x00\x00\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_2(): + frame = bytearray(b"\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\x00\x00\x00\x00\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_3(): + frame = bytearray(b"\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\x00\x00\x00\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_4(): + frame = bytearray(b"\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\x00\x00\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_5(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_6(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_7(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_8(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_9(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_10(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_11(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_12(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_13(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_14(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_15(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_16(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_17(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_18(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_19(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_20(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_21(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_22(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_23(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_24(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_25(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_26(): + frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_27(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_28(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_29(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_30(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_31(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_32(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_33(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_34(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_35(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_36(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_37(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_38(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_39(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_40(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_41(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_42(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_43(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_44(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_45(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_46(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_47(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_48(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_49(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_50(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_51(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_52(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_53(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_54(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_55(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_56(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_57(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_58(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_59(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_60(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_61(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_62(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_63(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" + ) + assert padFrame(frame, True, 0x00) == padded_frame + + +def test_pad_frame_padded_64(): + frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + padded_frame = bytearray( + b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + ) + assert padFrame(frame, True, 0x00) == padded_frame diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 5695ab0..689f53e 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -356,7 +356,7 @@ def process_event_packet(self, packet): def processResponse(self, response, length, counter, recv_timestamp=None): if counter == self.counterReceived: - self.logger.warn(f"Duplicate message counter {counter} received from the XCP slave") + self.logger.warning(f"Duplicate message counter {counter} received from the XCP slave") if self._debug: self.logger.debug(f"<- L{length} C{counter} {hexDump(response[:512])}") return @@ -402,7 +402,7 @@ def createTransport(name, *args, **kws): if name in transports: transportClass = transports[name] else: - raise ValueError(f"'{name}' is an invalid transport -- please choose one of [{' | '.join(transports.keys())}].") + raise ValueError(f"{name!r} is an invalid transport -- please choose one of [{' | '.join(transports.keys())}].") return transportClass(*args, **kws) diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index 2118376..82bca17 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -8,13 +8,16 @@ from time import time from typing import Any, Dict, Optional -from can import CanError, Message +from can import CanError, CanInitializationError, Message, detect_available_configs from can.interface import _get_class_for_interface +from rich.console import Console from pyxcp.config import CAN_INTERFACE_MAP from pyxcp.transport.base import BaseTransport +console = Console() + CAN_EXTENDED_ID = 0x80000000 MAX_11_BIT_IDENTIFIER = (1 << 11) - 1 MAX_29_BIT_IDENTIFIER = (1 << 29) - 1 @@ -76,8 +79,8 @@ def samplePointToTsegs(tqs: int, samplePoint: float) -> tuple: return (tseg1, tseg2) -def padFrame(frame: bytes, padding_value: int, padding_len: int = 0) -> bytes: - """Pad frame to next discrete DLC value. +def padFrame(frame: bytes, pad_frame: bool, padding_value: int) -> bytes: + """Pad frame to next discrete DLC value (CAN-FD) or on request (CAN-Classic). References: ----------- @@ -86,9 +89,9 @@ def padFrame(frame: bytes, padding_value: int, padding_len: int = 0) -> bytes: AUTOSAR CP Release 4.3.0, Specification of CAN Driver; [SWS_CAN_00502], [ECUC_Can_00485] AUTOSAR CP Release 4.3.0, Requirements on CAN; [SRS_Can_01073], [SRS_Can_01086], [SRS_Can_01160] """ - frame_len = max(len(frame), padding_len) + frame_len = len(frame) if frame_len <= MAX_DLC_CLASSIC: - actual_len = MAX_DLC_CLASSIC + actual_len = MAX_DLC_CLASSIC if pad_frame else frame_len else: actual_len = CAN_FD_DLCS[bisect_left(CAN_FD_DLCS, frame_len)] # append fill bytes up to MAX_DLC resp. next discrete FD DLC. @@ -116,10 +119,10 @@ def __init__(self, raw_id: int): self._is_extended = isExtendedIdentifier(raw_id) if self._is_extended: if self._id > MAX_29_BIT_IDENTIFIER: - raise IdentifierOutOfRangeError(f"29-bit identifier '{self._id}' is out of range") + raise IdentifierOutOfRangeError(f"29-bit identifier {self._id!r} is out of range") else: if self._id > MAX_11_BIT_IDENTIFIER: - raise IdentifierOutOfRangeError(f"11-bit identifier '{self._id}' is out of range") + raise IdentifierOutOfRangeError(f"11-bit identifier {self._id!r} is out of range") @property def id(self) -> int: @@ -175,6 +178,16 @@ def make_identifier(identifier: int, extended: bool) -> int: """ return Identifier(identifier if not extended else (identifier | CAN_EXTENDED_ID)) + def create_filter_from_id(self) -> Dict: + """Create a single CAN filter entry. + s. https://python-can.readthedocs.io/en/stable/bus.html#filtering + """ + return { + "can_id": self.id, + "can_mask": MAX_29_BIT_IDENTIFIER if self.is_extended else MAX_11_BIT_IDENTIFIER, + "extended": self.is_extended, + } + def __eq__(self, other): return (self.id == other.id) and (self.is_extended == other.is_extended) @@ -213,15 +226,16 @@ def __init__(self, parent, interface_name: str, **parameters): def connect(self): if self.connected: return - can_id = self.parent.can_id_master - can_filter = { - "can_id": can_id.id, - "can_mask": MAX_29_BIT_IDENTIFIER if can_id.is_extended else MAX_11_BIT_IDENTIFIER, - "extended": can_id.is_extended, - } + can_filters = [] + can_filters.append(self.parent.can_id_master.create_filter_from_id()) # Primary CAN filter. self.can_interface = self.can_interface_class(interface=self.interface_name, **self.parameters) - self.can_interface.set_filters([can_filter]) - self.parent.logger.debug(f"Python-CAN driver: {self.interface_name} - {self.can_interface}]") + if self.parent.daq_identifier: + # Add filters for DAQ identifiers. + for daq_id in self.parent.daq_identifier: + can_filters.append(daq_id.create_filter_from_id()) + self.can_interface.set_filters(can_filters) + self.parent.logger.debug(f"Interface: {self.interface_name!r} -- {self.can_interface!s}") + self.parent.logger.debug(f"Filters used: {self.can_interface.filters}") self.connected = True def close(self): @@ -286,13 +300,16 @@ def __init__(self, config, policy=None): # "... If a PDU does not exactly match these configurable sizes the unused bytes shall be padded." # self.fd = self.config.fd - self.max_dlc_required = self.config.max_dlc_required # or self.fd + self.daq_identifier = [] + if self.config.daq_identifier: + for did in self.config.daq_identifier: + self.daq_identifier.append(Identifier(did)) + self.max_dlc_required = self.config.max_dlc_required self.padding_value = self.config.padding_value - self.padding_len = self.config.max_can_fd_dlc if self.fd else MAX_DLC_CLASSIC self.interface_name = self.config.interface - + self.interface_configuration = detect_available_configs(interfaces=[self.interface_name]) parameters = self.get_interface_parameters() - self.logger.debug(f"Opening '{self.interface_name}' CAN-interface -- {list(parameters.items())}") + self.logger.debug(f"Opening {self.interface_name!r} CAN-interface -- {list(parameters.items())}") self.can_interface = PythonCanWrapper(self, self.interface_name, **parameters) def get_interface_parameters(self) -> Dict[str, Any]: @@ -309,7 +326,7 @@ def get_interface_parameters(self) -> Dict[str, Any]: if value is not None: result[n] = value elif value is not None: - self.logger.warning(f"'{self.interface_name}' has no support for parameter '{n}'.") + self.logger.warning(f"{self.interface_name!r} has no support for parameter {n!r}.") # Parameter names that need to be mapped. for base_name, name in can_interface_config_class.CAN_PARAM_MAP.items(): @@ -344,16 +361,19 @@ def listen(self): def connect(self): if self.useDefaultListener: self.startListener() - self.can_interface.connect() + try: + self.can_interface.connect() + except CanInitializationError: + console.print("[red]\nThere may be a problem with the configuration of your CAN-interface.\n") + console.print(f"[grey]Current configuration of interface {self.interface_name!r}:") + console.print(self.interface_configuration) + raise self.status = 1 # connected def send(self, frame: bytes) -> None: - # XCP on CAN trailer: if required, FILL bytes must be appended - if self.max_dlc_required: - frame = padFrame(frame, self.padding_value, self.padding_len) # send the request self.pre_send_timestamp = time() - self.can_interface.transmit(payload=frame) + self.can_interface.transmit(payload=padFrame(frame, self.max_dlc_required, self.padding_value)) self.post_send_timestamp = time() def closeConnection(self): diff --git a/pyxcp/transport/usb_transport.py b/pyxcp/transport/usb_transport.py index 00b2d80..4f915a3 100644 --- a/pyxcp/transport/usb_transport.py +++ b/pyxcp/transport/usb_transport.py @@ -5,6 +5,9 @@ from collections import deque from time import perf_counter, sleep, time +import usb.backend.libusb0 as libusb0 +import usb.backend.libusb1 as libusb1 +import usb.backend.openusb as openusb import usb.core import usb.util @@ -27,6 +30,7 @@ class Usb(BaseTransport): "reply_endpoint_number": (int, True, 1), "vendor_id": (int, False, 0), "product_id": (int, False, 0), + "library": (str, False, ""), # absolute path to USB shared library } HEADER = struct.Struct("<2H") HEADER_SIZE = HEADER.size @@ -41,6 +45,7 @@ def __init__(self, config=None, policy=None): self.interface_number = self.config.interface_number self.command_endpoint_number = self.config.command_endpoint_number self.reply_endpoint_number = self.config.reply_endpoint_number + self.library = self.config.library self.device = None self.status = 0 @@ -53,15 +58,24 @@ def __init__(self, config=None, policy=None): self._packets = deque() def connect(self): + if self.library: + for backend_provider in (libusb1, libusb0, openusb): + backend = backend_provider.get_backend(find_library=lambda x: self.library) + if backend: + break + else: + backend = None if self.vendor_id and self.product_id: kwargs = { "find_all": True, "idVendor": self.vendor_id, "idProduct": self.product_id, + "backend": backend, } else: kwargs = { "find_all": True, + "backend": backend, } for device in usb.core.find(**kwargs): @@ -72,7 +86,7 @@ def connect(self): except BaseException: continue else: - raise Exception(f"Device with serial {self.serial_number} not found") + raise Exception(f"Device with serial {self.serial_number!r} not found") current_configuration = self.device.get_active_configuration() if current_configuration.bConfigurationValue != self.configuration_number: diff --git a/pyxcp/types.py b/pyxcp/types.py index df2c54a..2434dfb 100644 --- a/pyxcp/types.py +++ b/pyxcp/types.py @@ -959,3 +959,11 @@ class FrameCategory(enum.IntEnum): SERV = 5 DAQ = 6 STIM = 7 + + +class TryCommandResult(enum.IntEnum): + """ """ + + OK = 0 + XCP_ERROR = 1 + OTHER_ERROR = 2 From 452eb7c6976fbbe995f523b4a926809a21b5ec08 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 1 Feb 2024 08:06:38 +0100 Subject: [PATCH 21/99] today() --- pyxcp/config/__init__.py | 114 +++++++++++++++++++++++++------ pyxcp/config/legacy.py | 9 +-- pyxcp/daq_stim/stim.hpp | 2 +- pyxcp/recorder/unfolder.hpp | 2 +- pyxcp/scripts/xcp_info.py | 32 +++++---- pyxcp/transport/can.py | 31 +++++++-- pyxcp/transport/sxi.py | 8 ++- pyxcp/transport/usb_transport.py | 48 ++++++------- 8 files changed, 175 insertions(+), 71 deletions(-) diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 404e1d9..30201b3 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -560,8 +560,12 @@ class Can(SingletonConfigurable): # max_can_fd_dlc = Integer(64, help="").tag(config=True) padding_value = Integer(0, help="Fill value, if max_dlc_required == True and DLC < MAX_DLC").tag(config=True) use_default_listener = Bool(True, help="").tag(config=True) - can_id_master = Integer(allow_none=False, help="CAN-ID master -> slave (Bit31= 1: extended identifier)").tag(config=True) - can_id_slave = Integer(allow_none=True, help="CAN-ID slave -> master (Bit31= 1: extended identifier)").tag(config=True) + can_id_master = Integer(allow_none=False, help="CAN-ID master -> slave (Bit31= 1: extended identifier)").tag( + config=True + ) # CMD and STIM packets + can_id_slave = Integer(allow_none=True, help="CAN-ID slave -> master (Bit31= 1: extended identifier)").tag( + config=True + ) # RES, ERR, EV, SERV and DAQ packets. can_id_broadcast = Integer( default_value=None, allow_none=True, help="Auto detection CAN-ID (Bit31= 1: extended identifier)" ).tag(config=True) @@ -675,18 +679,44 @@ class Eth(SingletonConfigurable): class SxI(SingletonConfigurable): - """SPI and SCI connections.""" + """SCI and SPI connections.""" port = Unicode("COM1", help="Name of communication interface.").tag(config=True) bitrate = Integer(38400, help="Connection bitrate").tag(config=True) bytesize = Enum([5, 6, 7, 8], default_value=8, help="Size of byte.").tag(config=True) parity = Enum(["N", "E", "O", "M", "S"], default_value="N", help="Paritybit calculation.").tag(config=True) stopbits = Enum([1, 1.5, 2], default_value=1, help="Number of stopbits.").tag(config=True) - - """ - -prot Set the SxI protocol type SYNC = 1,CTR = 2,SYNC+CTR = 3 (Default 0) - -cs Set the SxI checksum type LEN+CTR+PACKETS = 1, ONLY PACKETS = 2 (Default 0 no checksum) - """ + mode = Enum( + [ + "ASYNCH_FULL_DUPLEX_MODE", + "SYNCH_FULL_DUPLEX_MODE_BYTE", + "SYNCH_FULL_DUPLEX_MODE_WORD", + "SYNCH_FULL_DUPLEX_MODE_DWORD", + "SYNCH_MASTER_SLAVE_MODE_BYTE", + "SYNCH_MASTER_SLAVE_MODE_WORD", + "SYNCH_MASTER_SLAVE_MODE_DWORD", + ], + default_value="ASYNCH_FULL_DUPLEX_MODE", + help="Asynchronous (SCI) or synchronous (SPI) communication mode.", + ).tag(config=True) + header_format = Enum( + [ + "HEADER_LEN_BYTE", + "HEADER_LEN_CTR_BYTE", + "HEADER_LEN_FILL_BYTE", + "HEADER_LEN_WORD", + "HEADER_LEN_CTR_WORD", + "HEADER_LEN_FILL_WORD", + ], + default_value="HEADER_LEN_BYTE", + help="XCPonSxI header format.", + ).tag(config=True) + tail_format = Enum( + ["NO_CHECKSUM", "CHECKSUM_BYTE", "CHECKSUM_WORD"], default_value="NO_CHECKSUM", help="XCPonSxI tail format." + ).tag(config=True) + framing = Bool(False, help="Enable SCI framing mechanism (ESC chars).").tag(config=True) + esc_sync = Integer(0x01, min=0, max=255, help="SCI framing protocol character SYNC.").tag(config=True) + esc_esc = Integer(0x00, min=0, max=255, help="SCI framing protocol character ESC.").tag(config=True) class Usb(SingletonConfigurable): @@ -695,11 +725,55 @@ class Usb(SingletonConfigurable): serial_number = Unicode("", help="Device serial number.").tag(config=True) configuration_number = Integer(1, help="USB configuration number.").tag(config=True) interface_number = Integer(2, help="USB interface number.").tag(config=True) - command_endpoint_number = Integer(0, help="USB command endpoint number.").tag(config=True) - reply_endpoint_number = Integer(1, help="USB reply endpoint number.").tag(config=True) vendor_id = Integer(0, help="USB vendor ID.").tag(config=True) product_id = Integer(0, help="USB product ID.").tag(config=True) library = Unicode("", help="Absolute path to USB shared library.").tag(config=True) + header_format = Enum( + [ + "HEADER_LEN_BYTE", + "HEADER_LEN_CTR_BYTE", + "HEADER_LEN_FILL_BYTE", + "HEADER_LEN_WORD", + "HEADER_LEN_CTR_WORD", + "HEADER_LEN_FILL_WORD", + ], + default_value="HEADER_LEN_CTR_WORD", + help="", + ).tag(config=True) + in_ep_number = Integer(1, help="Ingoing USB reply endpoint number (IN-EP for RES/ERR, DAQ, and EV/SERV).").tag(config=True) + in_ep_transfer_type = Enum( + ["BULK_TRANSFER", "INTERRUPT_TRANSFER"], default_value="BULK_TRANSFER", help="Ingoing: Supported USB transfer types." + ).tag(config=True) + in_ep_max_packet_size = Integer(512, help="Ingoing: Maximum packet size of endpoint in bytes.").tag(config=True) + in_ep_polling_interval = Integer(0, help="Ingoing: Polling interval of endpoint.").tag(config=True) + in_ep_message_packing = Enum( + ["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], + default_value="MESSAGE_PACKING_SINGLE", + help="Ingoing: Packing of XCP Messages.", + ).tag(config=True) + in_ep_alignment = Enum( + ["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], + default_value="ALIGNMENT_8_BIT", + help="Ingoing: Alignment border.", + ).tag(config=True) + in_ep_recommended_host_bufsize = Integer(0, help="Ingoing: Recommended host buffer size.").tag(config=True) + out_ep_number = Integer(0, help="Outgoing USB command endpoint number (OUT-EP for CMD and STIM).").tag(config=True) + out_ep_transfer_type = Enum( + ["BULK_TRANSFER", "INTERRUPT_TRANSFER"], default_value="BULK_TRANSFER", help="Outgoing: Supported USB transfer types." + ).tag(config=True) + out_ep_max_packet_size = Integer(512, help="Outgoing: Maximum packet size of endpoint in bytes.").tag(config=True) + out_ep_polling_interval = Integer(0, help="Outgoing: Polling interval of endpoint.").tag(config=True) + out_ep_message_packing = Enum( + ["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], + default_value="MESSAGE_PACKING_SINGLE", + help="Outgoing: Packing of XCP Messages.", + ).tag(config=True) + out_ep_alignment = Enum( + ["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], + default_value="ALIGNMENT_8_BIT", + help="Outgoing: Alignment border.", + ).tag(config=True) + out_ep_recommended_host_bufsize = Integer(0, help="Outgoing: Recommended host buffer size.").tag(config=True) class Transport(SingletonConfigurable): @@ -709,9 +783,7 @@ class Transport(SingletonConfigurable): layer = Enum( ["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=False, help="Choose one of the supported XCP transport layers." - ).tag( - config=True - ) # Enum + ).tag(config=True) create_daq_timestamps = Bool(False, help="Record time of frame reception or set timestamp to 0.").tag(config=True) timeout = Float( 2.0, @@ -737,11 +809,16 @@ class General(SingletonConfigurable): """ """ loglevel = Unicode("WARN", help="Set the log level by value or name.").tag(config=True) - disable_error_handling = Bool(False).tag(config=True) - disconnect_response_optional = Bool(False).tag(config=True) - seed_n_key_dll = Unicode("", allow_none=False).tag(config=True) - seed_n_key_dll_same_bit_width = Bool(False).tag(config=True) - seed_n_key_function = Callable(default_value=None, allow_none=True).tag(config=True) + disable_error_handling = Bool(False, help="Disable XCP error-handler for performance reasons.").tag(config=True) + disconnect_response_optional = Bool(False, help="Ignore missing response on DISCONNECT request.").tag(config=True) + seed_n_key_dll = Unicode("", allow_none=False, help="Dynamic library used for slave resource unlocking.").tag(config=True) + seed_n_key_dll_same_bit_width = Bool(False, help="").tag(config=True) + seed_n_key_function = Callable( + default_value=None, + allow_none=True, + help="""Python function used for slave resource unlocking. +Could be used if seed-and-key algorithm is known instead of `seed_n_key_dll`.""", + ).tag(config=True) stim_support = Bool(False, help="").tag(config=True) @@ -757,7 +834,6 @@ class ProfileCreate(Application): def start(self): self.parent.parent.generate_config_file(sys.stdout, {}) - print("DEST", self.dest_file) class ProfileConvert(Application): diff --git a/pyxcp/config/legacy.py b/pyxcp/config/legacy.py index 8f7b037..e3eb2a8 100644 --- a/pyxcp/config/legacy.py +++ b/pyxcp/config/legacy.py @@ -26,8 +26,8 @@ "SERIAL_NUMBER": "Transport.Usb.serial_number", "CONFIGURATION_NUMBER": "Transport.Usb.configuration_number", "INTERFACE_NUMBER": "Transport.Usb.interface_number", - "COMMAND_ENDPOINT_NUMBER": "Transport.Usb.command_endpoint_number", - "REPLY_ENDPOINT_NUMBER": "Transport.Usb.reply_endpoint_number", + "COMMAND_ENDPOINT_NUMBER": "Transport.Usb.out_ep_number", + "REPLY_ENDPOINT_NUMBER": "Transport.Usb.in_ep_number", "VENDOR_ID": "Transport.Usb.vendor_id", "PRODUCT_ID": "Transport.Usb.product_id", "LIBRARY": "Transport.Usb.library", @@ -38,8 +38,9 @@ # "MAX_CAN_FD_DLC": "Transport.Can.max_can_fd_dlc", "PADDING_VALUE": "Transport.Can.padding_value", "CAN_USE_DEFAULT_LISTENER": "Transport.Can.use_default_listener", - "CAN_ID_MASTER": "Transport.Can.can_id_master", - "CAN_ID_SLAVE": "Transport.Can.can_id_slave", + # Swap master and slave IDs. (s. https://github.com/christoph2/pyxcp/issues/130) + "CAN_ID_SLAVE": "Transport.Can.can_id_master", + "CAN_ID_MASTER": "Transport.Can.can_id_slave", "CAN_ID_BROADCAST": "Transport.Can.can_id_broadcast", "BITRATE": "Transport.Can.bitrate", "RECEIVE_OWN_MESSAGES": "Transport.Can.receive_own_messages", diff --git a/pyxcp/daq_stim/stim.hpp b/pyxcp/daq_stim/stim.hpp index c83f2f3..6abf362 100644 --- a/pyxcp/daq_stim/stim.hpp +++ b/pyxcp/daq_stim/stim.hpp @@ -295,7 +295,7 @@ class Stim { void setDaqEventInfo(const std::vector& daq_event_info) { std::uint16_t idx = 0; - DBG_PRINTN("SET_DAQ_EVENT_INFO\n"); + // DBG_PRINTN("SET_DAQ_EVENT_INFO\n"); if (!m_activate) { return; } diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 01636aa..6051677 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -522,7 +522,7 @@ class DaqListState { return state_t::FINISHED; } } else { - std::cout << "\t\tODT num out of order: " << odt_num << " -- expected: " << m_next_odt << std::endl; + // std::cout << "\t\tODT num out of order: " << odt_num << " -- expected: " << m_next_odt << std::endl; resetSM(); return state_t::_ERROR; } diff --git a/pyxcp/scripts/xcp_info.py b/pyxcp/scripts/xcp_info.py index ca493ad..b233c2b 100644 --- a/pyxcp/scripts/xcp_info.py +++ b/pyxcp/scripts/xcp_info.py @@ -36,31 +36,35 @@ pprint(daq_info) daq_pro = daq_info["processor"] + daq_properties = daq_pro["properties"] + if x.slaveProperties.transport_layer == "CAN": + if daq_properties["pidOffSupported"]: + print("*** pidOffSupported -- i.e. one CAN-ID per DAQ-list.") + else: + print("*** NO support for PID_OFF") num_predefined = daq_pro["minDaq"] print("\nPredefined DAQ-Lists") print("====================") if num_predefined > 0: print(f"There are {num_predefined} predefined DAQ-lists") - for _daq_list in range(num_predefined): - pass + for idx in range(num_predefined): + print(f"DAQ-List #{idx}\n____________\n") + status, dm = x.try_command(x.getDaqListMode, idx) + if status == TryCommandResult.OK: + print(dm) + status, di = x.try_command(x.getDaqListInfo, idx) + if status == TryCommandResult.OK: + print(di) else: print("*** NO Predefined DAQ-Lists") - daq_properties = daq_pro["properties"] - if x.slaveProperties.transport_layer == "CAN": - if daq_properties["pidOffSupported"]: - print("*** pidOffSupported -- i.e. one CAN-ID per DAQ-list.") - else: - print("*** NO support for PID_OFF") - print("\nPAG Info:") print("=========") if x.slaveProperties.supportsCalpag: status, pag = x.try_command(x.getPagProcessorInfo) if status == TryCommandResult.OK: - pprint(pag) - for _idx in range(pag.maxSegments): - # x.getCalPage(0x01, idx) # ECU Access - x.getSegmentInfo() + print(pag) + # for idx in range(pag.maxSegments): + # x.getSegmentInfo(0x01, idx, 0, 0) else: print("*** PAGING IS NOT SUPPORTED.") @@ -69,7 +73,7 @@ if x.slaveProperties.supportsPgm: status, pgm = x.try_command(x.getPgmProcessorInfo) if status == TryCommandResult.OK: - pprint(pgm) + print(pgm) else: print("*** FLASH PROGRAMMING IS NOT SUPPORTED.") diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index 82bca17..5a920fc 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -155,6 +155,18 @@ def is_extended(self) -> bool: """ return self._is_extended + @property + def type_str(self) -> str: + """ + + Returns + ------- + str + - "S" - 11-bit identifier. + - "E" - 29-bit identifier. + """ + return "E" if self.is_extended else "S" + @staticmethod def make_identifier(identifier: int, extended: bool) -> int: """Factory method. @@ -227,7 +239,7 @@ def connect(self): if self.connected: return can_filters = [] - can_filters.append(self.parent.can_id_master.create_filter_from_id()) # Primary CAN filter. + can_filters.append(self.parent.can_id_slave.create_filter_from_id()) # Primary CAN filter. self.can_interface = self.can_interface_class(interface=self.interface_name, **self.parameters) if self.parent.daq_identifier: # Add filters for DAQ identifiers. @@ -245,8 +257,8 @@ def close(self): def transmit(self, payload: bytes) -> None: frame = Message( - arbitration_id=self.parent.can_id_slave.id, - is_extended_id=True if self.parent.can_id_slave.is_extended else False, + arbitration_id=self.parent.can_id_master.id, + is_extended_id=True if self.parent.can_id_master.is_extended else False, is_fd=self.parent.fd, data=payload, ) @@ -256,11 +268,11 @@ def read(self) -> Optional[Frame]: if not self.connected: return None try: - frame = self.can_interface.recv(5) + frame = self.can_interface.recv(self.timeout) except CanError: return None else: - if frame is None or frame.arbitration_id != self.parent.can_id_master.id or not len(frame.data): + if frame is None or not len(frame.data): return None # Timeout condition. extended = frame.is_extended_id identifier = Identifier.make_identifier(frame.arbitration_id, extended) @@ -302,15 +314,20 @@ def __init__(self, config, policy=None): self.fd = self.config.fd self.daq_identifier = [] if self.config.daq_identifier: - for did in self.config.daq_identifier: - self.daq_identifier.append(Identifier(did)) + for daq_id in self.config.daq_identifier: + self.daq_identifier.append(Identifier(daq_id)) self.max_dlc_required = self.config.max_dlc_required self.padding_value = self.config.padding_value self.interface_name = self.config.interface self.interface_configuration = detect_available_configs(interfaces=[self.interface_name]) parameters = self.get_interface_parameters() self.logger.debug(f"Opening {self.interface_name!r} CAN-interface -- {list(parameters.items())}") + self.logger.debug( + f"""Master-ID (Tx): 0x{self.can_id_master.id:08X}{self.can_id_master.type_str} -- + Slave-ID (Rx): 0x{self.can_id_slave.id:08X}{self.can_id_slave.type_str}""" + ) self.can_interface = PythonCanWrapper(self, self.interface_name, **parameters) + self.can_interface.timeout = config.timeout # c.Transport.timeout def get_interface_parameters(self) -> Dict[str, Any]: result = dict(channel=self.config.channel) diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index c63d9c3..ed3c5ef 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -23,6 +23,12 @@ def __init__(self, config=None, policy=None): self.bytesize = self.config.bytesize self.parity = self.config.parity self.stopbits = self.config.stopbits + self.mode = self.config.mode + self.header_format = self.config.header_format + self.tail_format = self.config.tail_format + self.framing = self.config.framing + self.esc_sync = self.config.esc_sync + self.esc_esc = self.config.esc_esc def __del__(self): self.closeConnection() @@ -41,7 +47,7 @@ def connect(self): except serial.SerialException as e: self.logger.critical(f"{e}") raise - self.logger.info(f"Serial comm_port openend as '{self.comm_port.portstr}' @ {self.baudrate} Bits/Sec.") + self.logger.info(f"Serial comm_port openend as {self.comm_port.portstr}@{self.baudrate} Bits/Sec.") self.startListener() def output(self, enable): diff --git a/pyxcp/transport/usb_transport.py b/pyxcp/transport/usb_transport.py index 4f915a3..9072511 100644 --- a/pyxcp/transport/usb_transport.py +++ b/pyxcp/transport/usb_transport.py @@ -10,6 +10,7 @@ import usb.backend.openusb as openusb import usb.core import usb.util +from usb.core import USBError, USBTimeoutError from pyxcp.transport.base import BaseTransport from pyxcp.utils import SHORT_SLEEP @@ -21,17 +22,6 @@ class Usb(BaseTransport): """""" - PARAMETER_MAP = { - # Type Req'd Default - "serial_number": (str, True, ""), - "configuration_number": (int, True, 1), - "interface_number": (int, True, 2), - "command_endpoint_number": (int, True, 0), - "reply_endpoint_number": (int, True, 1), - "vendor_id": (int, False, 0), - "product_id": (int, False, 0), - "library": (str, False, ""), # absolute path to USB shared library - } HEADER = struct.Struct("<2H") HEADER_SIZE = HEADER.size @@ -43,11 +33,22 @@ def __init__(self, config=None, policy=None): self.product_id = self.config.product_id self.configuration_number = self.config.configuration_number self.interface_number = self.config.interface_number - self.command_endpoint_number = self.config.command_endpoint_number - self.reply_endpoint_number = self.config.reply_endpoint_number self.library = self.config.library - self.device = None + self.header_format = self.config.header_format + + ## IN-EP (RES/ERR, DAQ, and EV/SERV) Parameters. + self.in_ep_number = self.config.in_ep_number + self.in_ep_transfer_type = self.config.in_ep_transfer_type + self.in_ep_max_packet_size = self.config.in_ep_max_packet_size + self.in_ep_polling_interval = self.config.in_ep_polling_interval + self.in_ep_message_packing = self.config.in_ep_message_packing + self.in_ep_alignment = self.config.in_ep_alignment + self.in_ep_recommended_host_bufsize = self.config.in_ep_recommended_host_bufsize + + ## OUT-EP (CMD and STIM) Parameters. + self.out_ep_number = self.config.out_ep_number + self.device = None self.status = 0 self._packet_listener = threading.Thread( @@ -83,7 +84,7 @@ def connect(self): if device.serial_number.strip().strip("\0").lower() == self.serial_number.lower(): self.device = device break - except BaseException: + except (USBError, USBTimeoutError): continue else: raise Exception(f"Device with serial {self.serial_number!r} not found") @@ -95,8 +96,8 @@ def connect(self): interface = cfg[(self.interface_number, 0)] - self.command_endpoint = interface[self.command_endpoint_number] - self.reply_endpoint = interface[self.reply_endpoint_number] + self.out_ep = interface[self.out_ep_number] + self.in_ep = interface[self.in_ep_number] self.startListener() self.status = 1 # connected @@ -121,7 +122,7 @@ def _packet_listen(self): close_event_set = self.closeEvent.is_set _packets = self._packets - read = self.reply_endpoint.read + read = self.in_ep.read buffer = array("B", bytes(RECV_SIZE)) buffer_view = memoryview(buffer) @@ -130,7 +131,6 @@ def _packet_listen(self): try: if close_event_set(): return - try: recv_timestamp = time() read_count = read(buffer, 100) # 100ms timeout @@ -138,12 +138,12 @@ def _packet_listen(self): _packets.append((buffer_view[:read_count].tobytes(), recv_timestamp)) else: _packets.append((buffer.tobytes(), recv_timestamp)) - except BaseException: + except (USBError, USBTimeoutError): # print(format_exc()) sleep(SHORT_SLEEP) continue - - except BaseException: + except BaseException: # noqa: B036 + # Note: catch-all only permitted if the intention is re-raising. self.status = 0 # disconnected break @@ -211,8 +211,8 @@ def listen(self): def send(self, frame): self.pre_send_timestamp = time() try: - self.command_endpoint.write(frame) - except BaseException: + self.out_ep.write(frame) + except (USBError, USBTimeoutError): # sometimes usb.core.USBError: [Errno 5] Input/Output Error is raised # even though the command is send and a reply is received from the device. # Ignore this here since a Timeout error will be raised anyway if From 6fdf349254459930d6ba2938cce49c86d70d29cc Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 6 Feb 2024 09:55:42 +0100 Subject: [PATCH 22/99] today() --- CONTRIBUTORS | 10 ++ pyxcp/config/__init__.py | 9 +- pyxcp/daq_stim/__init__.py | 25 ++-- pyxcp/examples/run_daq.py | 145 +++++++++++++++++++++ pyxcp/recorder/unfolder.hpp | 4 +- pyxcp/recorder/wrap.cpp | 6 +- pyxcp/scripts/xcp_info.py | 3 +- pyxcp/transport/can.py | 6 +- pyxcp/transport/candriver/__init__.py | 1 - pyxcp/transport/candriver/pc_canalystii.py | 26 ---- pyxcp/transport/candriver/pc_etas.py | 24 ---- pyxcp/transport/candriver/pc_gsusb.py | 22 ---- pyxcp/transport/candriver/pc_iscan.py | 22 ---- pyxcp/transport/candriver/pc_ixxat.py | 26 ---- pyxcp/transport/candriver/pc_kvaser.py | 38 ------ pyxcp/transport/candriver/pc_neovi.py | 30 ----- pyxcp/transport/candriver/pc_nican.py | 22 ---- pyxcp/transport/candriver/pc_nixnet.py | 22 ---- pyxcp/transport/candriver/pc_pcan.py | 24 ---- pyxcp/transport/candriver/pc_seeed.py | 27 ---- pyxcp/transport/candriver/pc_serial.py | 26 ---- pyxcp/transport/candriver/pc_slcan.py | 28 ---- pyxcp/transport/candriver/pc_socketcan.py | 22 ---- pyxcp/transport/candriver/pc_systec.py | 28 ---- pyxcp/transport/candriver/pc_usb2can.py | 29 ----- pyxcp/transport/candriver/pc_vector.py | 33 ----- pyxcp/transport/candriver/python_can.py | 6 - 27 files changed, 183 insertions(+), 481 deletions(-) create mode 100644 pyxcp/examples/run_daq.py delete mode 100644 pyxcp/transport/candriver/__init__.py delete mode 100644 pyxcp/transport/candriver/pc_canalystii.py delete mode 100644 pyxcp/transport/candriver/pc_etas.py delete mode 100644 pyxcp/transport/candriver/pc_gsusb.py delete mode 100644 pyxcp/transport/candriver/pc_iscan.py delete mode 100644 pyxcp/transport/candriver/pc_ixxat.py delete mode 100644 pyxcp/transport/candriver/pc_kvaser.py delete mode 100644 pyxcp/transport/candriver/pc_neovi.py delete mode 100644 pyxcp/transport/candriver/pc_nican.py delete mode 100644 pyxcp/transport/candriver/pc_nixnet.py delete mode 100644 pyxcp/transport/candriver/pc_pcan.py delete mode 100644 pyxcp/transport/candriver/pc_seeed.py delete mode 100644 pyxcp/transport/candriver/pc_serial.py delete mode 100644 pyxcp/transport/candriver/pc_slcan.py delete mode 100644 pyxcp/transport/candriver/pc_socketcan.py delete mode 100644 pyxcp/transport/candriver/pc_systec.py delete mode 100644 pyxcp/transport/candriver/pc_usb2can.py delete mode 100644 pyxcp/transport/candriver/pc_vector.py delete mode 100644 pyxcp/transport/candriver/python_can.py diff --git a/CONTRIBUTORS b/CONTRIBUTORS index f04252b..ac8e1b5 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,6 +1,16 @@ +chrisoro <4160557+chrisoro@users.noreply.github.com> Christoph Schueler +Damien KAYSER Daniel Hrisca +danielhrisca +Devendra Giramkar +dkuschmierz <32884309+dkuschmierz@users.noreply.github.com> Felix Nieuwenhuizen +Paul Gee +rui +Sedov Aleksandr Steffen Sanwald +The Codacy Badger +toebsen torgrimb waszil diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 30201b3..21669f4 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -906,6 +906,7 @@ def start(self): exit(2) else: self._read_configuration(self.config_file) + self._setup_logger() def _setup_logger(self): from pyxcp.types import Command @@ -928,9 +929,12 @@ def _setup_logger(self): self.log.addHandler(rich_handler) def initialize(self, argv=None): + from pyxcp import __version__ as pyxcp_version + + PyXCP.version = pyxcp_version PyXCP.name = Path(sys.argv[0]).name self.parse_command_line(argv[1:]) - self._setup_logger() + self.log.debug(f"pyxcp version: {self.version}") def _read_configuration(self, file_name: str, emit_warning: bool = True) -> None: self.read_configuration_file(file_name, emit_warning) @@ -943,10 +947,9 @@ def read_configuration_file(self, file_name: str, emit_warning: bool = True): pth = Path(file_name) if not pth.exists(): raise FileNotFoundError(f"Configuration file {file_name!r} does not exist.") - suffix = pth.suffix.lower() if suffix == ".py": - self.load_config_file(self.config_file) + self.load_config_file(pth) else: self.legacy_config = True if suffix == ".json": diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index a5a1a3a..2bf84db 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from pprint import pprint +# from pprint import pprint from typing import List from pyxcp import types @@ -36,7 +36,7 @@ def __init__(self, file_name: str, daq_lists: List[DaqList]): def setup(self, write_multiple: bool = True): self.daq_info = self.xcp_master.getDaqInfo() - pprint(self.daq_info) + # pprint(self.daq_info) try: processor = self.daq_info.get("processor") properties = processor.get("properties") @@ -70,10 +70,10 @@ def setup(self, write_multiple: bool = True): # print("NO TIMESTAMP SUPPORT") else: if self.ts_fixed: - print("Fixed timestamp") + # print("Fixed timestamp") max_payload_size_first = max_payload_size - self.ts_size else: - print("timestamp variable.") + # print("timestamp variable.") self.selectable_timestamps = True except Exception as e: @@ -89,7 +89,7 @@ def setup(self, write_multiple: bool = True): ttt = make_continuous_blocks(daq_list.measurements, max_payload_size, max_payload_size_first) daq_list.measurements_opt = first_fit_decreasing(ttt, max_payload_size, max_payload_size_first) byte_order = 0 if self.xcp_master.slaveProperties.byteOrder == "INTEL" else 1 - self.uf = MeasurementParameters( + measurement_params = MeasurementParameters( byte_order, header_len, self.supports_timestampes, @@ -101,8 +101,7 @@ def setup(self, write_multiple: bool = True): self.min_daq, self.daq_lists, ) - - self.set_parameters(self.uf) + self.set_parameters(measurement_params) self.first_pids = [] daq_count = len(self.daq_lists) @@ -134,7 +133,7 @@ def start(self): if not self.setup_called: raise RuntimeError("please run setup() before start()") for i, daq_list in enumerate(self.daq_lists, self.min_daq): - print(daq_list.name, daq_list.event_num, daq_list.stim) + # print(daq_list.name, daq_list.event_num, daq_list.stim) mode = 0x00 if self.supports_timestampes and (self.ts_fixed or (self.selectable_timestamps and daq_list.enable_timestamps)): mode = 0x10 @@ -144,7 +143,7 @@ def start(self): ## mode |= 0x20 ### self.xcp_master.setDaqListMode( - daqListNumber=i, mode=mode, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xFF # TODO: + MIN_DAQ + daqListNumber=i, mode=mode, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xFF ) res = self.xcp_master.startStopDaqList(0x02, i) self.first_pids.append(res.firstPid) @@ -157,8 +156,8 @@ def stop(self): class DaqToCsv(DAQParser): """Save a measurement as CSV files (one per DAQ-list).""" - def post_setup(self): - self.log.debug("DaqCsv::post_setup()") + def Initialize(self): + self.log.debug("DaqCsv::Initialize()") self.files = {} for num, daq_list in enumerate(self.daq_lists): if daq_list.stim: @@ -174,8 +173,8 @@ def on_daq_list(self, daq_list: int, ts0: float, ts1: float, payload: list): def finalize(self): self.log.debug("DaqCsv::finalize()") ## - ## NOTE: `finalize` is guaranteed to be called, but `post_setup` may fail for reasons. - ## So if you allocate resources in `post_setup` check if this really happened. + ## NOTE: `finalize` is guaranteed to be called, but `Initialize` may fail for reasons. + ## So if you allocate resources in `Initialize` check if this really happened. ## if hasattr(self, "files"): for f in self.files.values(): diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py new file mode 100644 index 0000000..0ad6e95 --- /dev/null +++ b/pyxcp/examples/run_daq.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python + +import time + +from pyxcp.cmdline import ArgumentParser +from pyxcp.daq_stim import DaqList, DaqToCsv + + +# RECORDER_FILE_NAME = "daq_test" + +ap = ArgumentParser(description="DAQ test") + +XCP_LITE = True + + +# Completly random configurations, only for illustrative purposes. +# +# NOTE: UPDATE TO CORRECT ADDRESSES BEFORE RUNNING!!! +# +if XCP_LITE: + # Vectorgrp XCPlite. + DAQ_LISTS = [ + DaqList( + "part_1", + 0, + False, + False, + [ + ("byteCounter", 0x203EA, 0, "U8"), + ("wordCounter", 0x203EC, 0, "U16"), + ("dwordCounter", 0x20410, 0, "U32"), + ("sbyteCounter", 0x203EB, 0, "I8"), + ], + ), + DaqList( + "part_2", + 0, + False, + False, + [ + ("swordCounter", 0x20414, 0, "I16"), + ("sdwordCounter", 0x20418, 0, "I32"), + ("channel1", 0x203F8, 0, "F64"), + ("channel2", 0x20400, 0, "F64"), + ("channel3", 0x20408, 0, "F64"), + ], + ), + ] +else: + # XCPsim from CANape. + DAQ_LISTS = [ + DaqList( + "pwm_stuff", + 2, + False, + False, + [ + ("channel1", 0x1BD004, 0, "F32"), + ("period", 0x001C0028, 0, "F32"), + ("channel2", 0x1BD008, 0, "F32"), + ("PWMFiltered", 0x1BDDE2, 0, "U8"), + ("PWM", 0x1BDDDF, 0, "U8"), + ("Triangle", 0x1BDDDE, 0, "I8"), + ], + ), + DaqList( + "bytes", + 1, + False, + True, + [ + ("TestByte_000", 0x1BE11C, 0, "U8"), + ("TestByte_015", 0x1BE158, 0, "U8"), + ("TestByte_016", 0x1BE15C, 0, "U8"), + ("TestByte_023", 0x1BE178, 0, "U8"), + ("TestByte_024", 0x1BE17C, 0, "U8"), + ("TestByte_034", 0x1BE1A4, 0, "U8"), + ("TestByte_059", 0x1BE208, 0, "U8"), + ("TestByte_061", 0x1BE210, 0, "U8"), + ("TestByte_063", 0x1BE218, 0, "U8"), + ("TestByte_064", 0x1BE21C, 0, "U8"), + ("TestByte_097", 0x1BE2A0, 0, "U8"), + ("TestByte_107", 0x1BE2C8, 0, "U8"), + ("TestByte_131", 0x1BE328, 0, "U8"), + ("TestByte_156", 0x1BE38C, 0, "U8"), + ("TestByte_159", 0x1BE398, 0, "U8"), + ("TestByte_182", 0x1BE3F4, 0, "U8"), + ("TestByte_183", 0x1BE3F8, 0, "U8"), + ("TestByte_189", 0x1BE410, 0, "U8"), + ("TestByte_195", 0x1BE428, 0, "U8"), + ("TestByte_216", 0x1BE47C, 0, "U8"), + ("TestByte_218", 0x1BE484, 0, "U8"), + ("TestByte_221", 0x1BE490, 0, "U8"), + ("TestByte_251", 0x1BE508, 0, "U8"), + ("TestByte_263", 0x1BE538, 0, "U8"), + ("TestByte_276", 0x1BE56C, 0, "U8"), + ("TestByte_277", 0x1BE570, 0, "U8"), + ("TestByte_297", 0x1BE5C0, 0, "U8"), + ("TestByte_302", 0x1BE5D4, 0, "U8"), + ("TestByte_324", 0x1BE62C, 0, "U8"), + ("TestByte_344", 0x1BE67C, 0, "U8"), + ("TestByte_346", 0x1BE684, 0, "U8"), + ], + ), + DaqList( + "words", + 3, + False, + True, + [ + ("TestWord_001", 0x1BE120, 0, "U16"), + ("TestWord_003", 0x1BE128, 0, "U16"), + ("TestWord_004", 0x1BE12C, 0, "U16"), + ("TestWord_005", 0x1BE134, 0, "U16"), + ("TestWord_006", 0x1BE134, 0, "U16"), + ("TestWord_007", 0x1BE138, 0, "U16"), + ("TestWord_008", 0x1BE13C, 0, "U16"), + ("TestWord_009", 0x1BE140, 0, "U16"), + ("TestWord_011", 0x1BE148, 0, "U16"), + ], + ), + ] + +daq_parser = DaqToCsv(None, DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. + +with ap.run(policy=daq_parser) as x: + x.connect() + if x.slaveProperties.optionalCommMode: + x.getCommModeInfo() + + x.cond_unlock() # DAQ resource is locked in many cases. + + print("setup DAQ lists.") + daq_parser.setup() # Run internal and XCP setup procedures. + print("start DAQ lists.") + daq_parser.start() # Start DAQ lists. + time.sleep(5.0) # Arbitrary termination condition. + daq_parser.stop() # Stop DAQ lists. + print("finalize DAQ lists.\n") + x.disconnect() + +print("Data written to:") +print("================") +for fl in daq_parser.files.values(): # `files` attribute is specific to `DaqToCsv`. + print(fl.name) diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 6051677..017c364 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -770,7 +770,7 @@ class DAQParser { void set_parameters(const MeasurementParameters& params) noexcept { m_unfolder = std::make_unique(params); - post_setup(); + Initialize(); } virtual void on_daq_list( @@ -788,7 +788,7 @@ class DAQParser { } } - virtual void post_setup() { + virtual void Initialize() { } virtual void finalize() { diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 9193d71..d87b9c3 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -22,8 +22,8 @@ class PyDAQParser : public DAQParser { PYBIND11_OVERRIDE_PURE(void, DAQParser, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); } - void post_setup() override { - PYBIND11_OVERRIDE(void, DAQParser, post_setup); + void Initialize() override { + PYBIND11_OVERRIDE(void, DAQParser, Initialize); } void finalize() override { @@ -54,5 +54,5 @@ PYBIND11_MODULE(rekorder, m) { .def("feed", &DAQParser::feed) .def("finalize", &DAQParser::finalize) .def("set_parameters", &DAQParser::set_parameters) - .def("post_setup", &DAQParser::post_setup); + .def("Initialize", &DAQParser::Initialize); } diff --git a/pyxcp/scripts/xcp_info.py b/pyxcp/scripts/xcp_info.py index b233c2b..2affde0 100644 --- a/pyxcp/scripts/xcp_info.py +++ b/pyxcp/scripts/xcp_info.py @@ -38,6 +38,7 @@ daq_pro = daq_info["processor"] daq_properties = daq_pro["properties"] if x.slaveProperties.transport_layer == "CAN": + print("") if daq_properties["pidOffSupported"]: print("*** pidOffSupported -- i.e. one CAN-ID per DAQ-list.") else: @@ -78,7 +79,7 @@ print("*** FLASH PROGRAMMING IS NOT SUPPORTED.") if x.slaveProperties.transport_layer == "CAN": - print("OK, CAN!!!") + # print("OK, CAN!!!") status, res = x.try_command(x.getDaqId, 0) print(status, res) x.disconnect() diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index 5a920fc..59ac304 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -321,10 +321,10 @@ def __init__(self, config, policy=None): self.interface_name = self.config.interface self.interface_configuration = detect_available_configs(interfaces=[self.interface_name]) parameters = self.get_interface_parameters() - self.logger.debug(f"Opening {self.interface_name!r} CAN-interface -- {list(parameters.items())}") + self.logger.debug(f"Opening {self.interface_name!r} CAN-interface {list(parameters.items())}") self.logger.debug( - f"""Master-ID (Tx): 0x{self.can_id_master.id:08X}{self.can_id_master.type_str} -- - Slave-ID (Rx): 0x{self.can_id_slave.id:08X}{self.can_id_slave.type_str}""" + f"Master-ID (Tx): 0x{self.can_id_master.id:08X}{self.can_id_master.type_str} -- " + f"Slave-ID (Rx): 0x{self.can_id_slave.id:08X}{self.can_id_slave.type_str}" ) self.can_interface = PythonCanWrapper(self, self.interface_name, **parameters) self.can_interface.timeout = config.timeout # c.Transport.timeout diff --git a/pyxcp/transport/candriver/__init__.py b/pyxcp/transport/candriver/__init__.py deleted file mode 100644 index 4265cc3..0000000 --- a/pyxcp/transport/candriver/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/env python diff --git a/pyxcp/transport/candriver/pc_canalystii.py b/pyxcp/transport/candriver/pc_canalystii.py deleted file mode 100644 index 8d211f3..0000000 --- a/pyxcp/transport/candriver/pc_canalystii.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for CANalyst-II(+) by ZLG ZHIYUAN Electronics interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Canalystii(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "BAUD ": (int, False, None), - "TIMING0": (int, False, None), - "TIMING1": (int, False, None), - } - - PARAMETER_TO_KW_ARG_MAP = { - "BAUD": "baud", - "TIMING0": "Timing0", - "TIMING1": "Timing1", - } - - def __init__(self): - super().__init__(bustype="canalystii") diff --git a/pyxcp/transport/candriver/pc_etas.py b/pyxcp/transport/candriver/pc_etas.py deleted file mode 100644 index 96ee5e1..0000000 --- a/pyxcp/transport/candriver/pc_etas.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for ETAS BOA interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Etas(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "FD": (bool, False, False), - "DATA_BITRATE": (int, False, None), - } - - PARAMETER_TO_KW_ARG_MAP = { - "FD": "fd", - "DATA_BITRATE": "data_bitrate", - } - - def __init__(self): - super().__init__(bustype="etas") diff --git a/pyxcp/transport/candriver/pc_gsusb.py b/pyxcp/transport/candriver/pc_gsusb.py deleted file mode 100644 index fb3c711..0000000 --- a/pyxcp/transport/candriver/pc_gsusb.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for CAN driver for Geschwister Schneider USB/CAN devices and bytewerk.org candleLight USB CAN interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class GsUsb(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - """ - PARAMETER_MAP = { - # Type Req'd Default - } - - PARAMETER_TO_KW_ARG_MAP = { - } - """ - - def __init__(self): - super().__init__(bustype="gs_usb") diff --git a/pyxcp/transport/candriver/pc_iscan.py b/pyxcp/transport/candriver/pc_iscan.py deleted file mode 100644 index aa33b40..0000000 --- a/pyxcp/transport/candriver/pc_iscan.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for isCAN from Thorsis Technologies GmbH. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class IsCAN(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "POLL_INTERVAL": (float, False, 0.01), - } - - PARAMETER_TO_KW_ARG_MAP = { - "POLL_INTERVAL": "poll_interval", - } - - def __init__(self): - super().__init__(bustype="iscan") diff --git a/pyxcp/transport/candriver/pc_ixxat.py b/pyxcp/transport/candriver/pc_ixxat.py deleted file mode 100644 index e0bc0fa..0000000 --- a/pyxcp/transport/candriver/pc_ixxat.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for Ixxat interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Ixxat(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "UNIQUE_HARDWARE_ID": (str, False, None), - "RX_FIFO_SIZE": (int, False, 16), - "TX_FIFO_SIZE": (int, False, 16), - } - - PARAMETER_TO_KW_ARG_MAP = { - "UNIQUE_HARDWARE_ID": "UniqueHardwareId", - "RX_FIFO_SIZE": "rxFifoSize", - "TX_FIFO_SIZE": "txFifoSize", - } - - def __init__(self): - super().__init__(bustype="ixxat") diff --git a/pyxcp/transport/candriver/pc_kvaser.py b/pyxcp/transport/candriver/pc_kvaser.py deleted file mode 100644 index 721a981..0000000 --- a/pyxcp/transport/candriver/pc_kvaser.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for Kvaser interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Kvaser(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "ACCEPT_VIRTUAL": (bool, False, True), - "DRIVER_MODE": (bool, False, True), - "NO_SAMP": (int, False, 1), - "SJW": (int, False, 2), - "TSEG1": (int, False, 5), - "TSEG2": (int, False, 2), - "SINGLE_HANDLE": (bool, False, True), - "FD": (bool, False, False), - "DATA_BITRATE": (int, False, None), - } - - PARAMETER_TO_KW_ARG_MAP = { - "ACCEPT_VIRTUAL": "accept_virtual", - "TSEG1": "tseg1", - "TSEG2": "tseg2", - "SJW": "sjw", - "NO_SAMP": "no_samp", - "DRIVER_MODE": "driver_mode", - "SINGLE_HANDLE": "single_handle", - "FD": "fd", - "DATA_BITRATE": "data_bitrate", - } - - def __init__(self): - super().__init__(bustype="kvaser") diff --git a/pyxcp/transport/candriver/pc_neovi.py b/pyxcp/transport/candriver/pc_neovi.py deleted file mode 100644 index bce8d43..0000000 --- a/pyxcp/transport/candriver/pc_neovi.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for ICS NeoVi interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Neovi(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "FD": (bool, False, False), - "DATA_BITRATE": (int, False, None), - "USE_SYSTEM_TIMESTAMP": (bool, False, False), - "SERIAL": (str, False, None), - "OVERRIDE_LIBRARY_NAME": (str, False, None), - } - - PARAMETER_TO_KW_ARG_MAP = { - "FD": "fd", - "DATA_BITRATE": "data_bitrate", - "USE_SYSTEM_TIMESTAMP": "use_system_timestamp", - "SERIAL": "serial", - "OVERRIDE_LIBRARY_NAME": "override_library_name", - } - - def __init__(self): - super().__init__(bustype="neovi") diff --git a/pyxcp/transport/candriver/pc_nican.py b/pyxcp/transport/candriver/pc_nican.py deleted file mode 100644 index 4a526bd..0000000 --- a/pyxcp/transport/candriver/pc_nican.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for National Instruments interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class NiCan(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "LOG_ERRORS": (bool, False, False), - } - - PARAMETER_TO_KW_ARG_MAP = { - "LOG_ERRORS": "log_errors", - } - - def __init__(self): - super().__init__(bustype="nican") diff --git a/pyxcp/transport/candriver/pc_nixnet.py b/pyxcp/transport/candriver/pc_nixnet.py deleted file mode 100644 index 2e07e2c..0000000 --- a/pyxcp/transport/candriver/pc_nixnet.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for National Instruments xnet interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class NiXnet(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "LOG_ERRORS": (bool, False, False), - } - - PARAMETER_TO_KW_ARG_MAP = { - "LOG_ERRORS": "log_errors", - } - - def __init__(self): - super().__init__(bustype="nixnet") diff --git a/pyxcp/transport/candriver/pc_pcan.py b/pyxcp/transport/candriver/pc_pcan.py deleted file mode 100644 index 21e23b0..0000000 --- a/pyxcp/transport/candriver/pc_pcan.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for Peak System interfaces. -""" -from can import BusState - -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class PCan(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "STATE": (BusState, False, BusState.ACTIVE), - } - - PARAMETER_TO_KW_ARG_MAP = { - "STATE": "state", - } - - def __init__(self): - super().__init__(bustype="pcan") diff --git a/pyxcp/transport/candriver/pc_seeed.py b/pyxcp/transport/candriver/pc_seeed.py deleted file mode 100644 index 0611ffd..0000000 --- a/pyxcp/transport/candriver/pc_seeed.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for USB-CAN Analyzer by Seeed Studio interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Seeed(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # # Type Req'd Default - # "FD": (bool, False, False), - # "DATA_BITRATE": (int, False, None), - } - - PARAMETER_TO_KW_ARG_MAP = { - # "FD": "fd", - # "DATA_BITRATE": "data_bitrate", - } - - def __init__(self): - super().__init__(bustype="seeedstudio") - - -# from can.interfaces.seeedstudio import SeeedBus diff --git a/pyxcp/transport/candriver/pc_serial.py b/pyxcp/transport/candriver/pc_serial.py deleted file mode 100644 index 7f23dfc..0000000 --- a/pyxcp/transport/candriver/pc_serial.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver serial port connected interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Serial(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "BAUDRATE": (int, False, 115200), - "TIMEOUT": (float, False, 0.1), - "RTSCTS": (bool, False, False), - } - - PARAMETER_TO_KW_ARG_MAP = { - "BAUDRATE": "baudrate", - "TIMEOUT": "timeout", - "RTSCTS": "rtscts", - } - - def __init__(self): - super().__init__(bustype="serial") diff --git a/pyxcp/transport/candriver/pc_slcan.py b/pyxcp/transport/candriver/pc_slcan.py deleted file mode 100644 index 91b8521..0000000 --- a/pyxcp/transport/candriver/pc_slcan.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for CAN over Serial (like Lawicel) interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class SlCan(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "TTY_BAUDRATE": (int, False, 115200), - "POLL_INTERVAL": (float, False, 0.01), - "SLEEP_AFTER_OPEN": (float, False, 2.0), - "RTSCTS": (bool, False, False), - } - - PARAMETER_TO_KW_ARG_MAP = { - "TTY_BAUDRATE": "ttyBaudrate", - "POLL_INTERVAL": "poll_interval", - "SLEEP_AFTER_OPEN": "sleep_after_open", - "RTSCTS": "rtscts", - } - - def __init__(self): - super().__init__(bustype="slcan") diff --git a/pyxcp/transport/candriver/pc_socketcan.py b/pyxcp/transport/candriver/pc_socketcan.py deleted file mode 100644 index 58baf6e..0000000 --- a/pyxcp/transport/candriver/pc_socketcan.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for Linux SocketCAN interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class SocketCAN(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "FD": (bool, False, False), - } - - PARAMETER_TO_KW_ARG_MAP = { - "FD": "fd", - } - - def __init__(self): - super().__init__(bustype="socketcan") diff --git a/pyxcp/transport/candriver/pc_systec.py b/pyxcp/transport/candriver/pc_systec.py deleted file mode 100644 index 3fcf7c4..0000000 --- a/pyxcp/transport/candriver/pc_systec.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for Systec interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Systec(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "DEVICE_NUMBER": (int, False, 255), - "RX_BUFFER_ENTRIES": (int, False, 4096), - "TX_BUFFER_ENTRIES": (int, False, 4096), - "STATE": (str, False, "ACTIVE"), - } - - PARAMETER_TO_KW_ARG_MAP = { - "DEVICE_NUMBER": "device_number", - "RX_BUFFER_ENTRIES": "rx_buffer_entries", - "TX_BUFFER_ENTRIES": "tx_buffer_entries", - "STATE": "state", - } - - def __init__(self): - super().__init__(bustype="systec") diff --git a/pyxcp/transport/candriver/pc_usb2can.py b/pyxcp/transport/candriver/pc_usb2can.py deleted file mode 100644 index ba73b60..0000000 --- a/pyxcp/transport/candriver/pc_usb2can.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for 8devices USB2CAN interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Usb2Can(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "FLAGS": (int, False, 0), - } - - PARAMETER_TO_KW_ARG_MAP = { - "FLAGS": "flags", - } - - """ - - :param int flags: - Flags to directly pass to open function of the usb2can abstraction layer. - - """ - - def __init__(self): - super().__init__(bustype="usb2can") diff --git a/pyxcp/transport/candriver/pc_vector.py b/pyxcp/transport/candriver/pc_vector.py deleted file mode 100644 index ef795c4..0000000 --- a/pyxcp/transport/candriver/pc_vector.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -""" -python-can driver for Vector Informatik interfaces. -""" -import pyxcp.transport.can as can -import pyxcp.transport.candriver.python_can as python_can - - -class Vector(python_can.PythonCAN, can.CanInterfaceBase): - """""" - - PARAMETER_MAP = { - # Type Req'd Default - "POLL_INTERVAL": (float, False, 0.01), - "APP_NAME": (str, False, ""), - "SERIAL": (int, False, None), - "RX_QUEUE_SIZE": (int, False, 16384), - "FD": (bool, False, False), - "DATA_BITRATE": (int, False, 0), - "DATA_SAMPLE_POINT": (float, False, 0), - } - - PARAMETER_TO_KW_ARG_MAP = { - "POLL_INTERVAL": "poll_interval", - "RX_QUEUE_SIZE": "rx_queue_size", - "FD": "fd", - "DATA_BITRATE": "data_bitrate", - "APP_NAME": "app_name", - "SERIAL": "serial", - } - - def __init__(self): - super().__init__(bustype="vector") diff --git a/pyxcp/transport/candriver/python_can.py b/pyxcp/transport/candriver/python_can.py deleted file mode 100644 index 9e6264a..0000000 --- a/pyxcp/transport/candriver/python_can.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python -""" -Support for python-can - github.com/hardbyte/python-can -""" - -from can.interface import _get_class_for_interface # noqa: F401 From 57d71fdf7052f19748b5f31acc0007fa1cfe469f Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 11 Feb 2024 12:16:10 +0200 Subject: [PATCH 23/99] Build extensions --- build_ext.py | 104 +++++++++++++++++++++++++++++++++-- pyproject.toml | 2 +- pyxcp/daq_stim/scheduler.cpp | 4 ++ pyxcp/daq_stim/scheduler.hpp | 3 + 4 files changed, 107 insertions(+), 6 deletions(-) diff --git a/build_ext.py b/build_ext.py index 1fc2256..b262e97 100644 --- a/build_ext.py +++ b/build_ext.py @@ -1,27 +1,97 @@ +import os +from pathlib import Path +import platform import subprocess # nosec +import sys from typing import Any, Dict -from pybind11.setup_helpers import ParallelCompile, Pybind11Extension, naive_recompile +import setuptools.command.build_py +import setuptools.command.develop +from setuptools import Distribution +ROOT_DIRPATH = Path(".") -print("Running 'build.py'...") +if sys.platform == "darwin": + os.environ["CC"] = "clang++" + os.environ["CXX"] = "clang++" -PYB11_INCLUDE_DIRS = subprocess.check_output(["pybind11-config", "--includes"]) # nosec -EXT_NAMES = ["rekorder"] +from pybind11.setup_helpers import ( + ParallelCompile, + Pybind11Extension, + naive_recompile, +build_ext, +) ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() +PYB11_INCLUDE_DIRS = subprocess.check_output(["pybind11-config", "--includes"]) # nosec +EXT_NAMES = ["pyxcp.recorder.rekorder", "pyxcp.cpp_ext.cpp_ext", "pyxcp.daq_stim.stim"] ext_modules = [ Pybind11Extension( EXT_NAMES[0], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/recorder"], + include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/recorder", "pyxcp/cpp_ext"], sources=["pyxcp/recorder/lz4.c", "pyxcp/recorder/wrap.cpp"], define_macros=[("EXTENSION_NAME", EXT_NAMES[0]), ("NDEBUG", 1)], optional=False, + cxx_std=20, + ), + Pybind11Extension( + EXT_NAMES[1], + include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/cpp_ext"], + sources=["pyxcp/cpp_ext/extension_wrapper.cpp"], + define_macros=[("EXTENSION_NAME", EXT_NAMES[1]), ("NDEBUG", 1)], + optional=False, + cxx_std=20, + ), + Pybind11Extension( + EXT_NAMES[2], + include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/daq_stim", "pyxcp/cpp_ext"], + sources=["pyxcp/daq_stim/stim.cpp", "pyxcp/daq_stim/stim_wrapper.cpp", "pyxcp/daq_stim/scheduler.cpp"], + define_macros=[("EXTENSION_NAME", EXT_NAMES[2]), ("NDEBUG", 1)], + optional=False, cxx_std=20, # Extension will use C++20 generators/coroutines. ), ] +class AsamKeyDllAutogen(setuptools.Command): + """Custom command to compile `asamkeydll.exe`.""" + + description = "Compile `asamkeydll.exe`." + + def initialize_options(self): + pass + + def finalize_options(self): + """Post-process options.""" + asamkeydll = os.path.join("pyxcp", "asamkeydll.c") + target = os.path.join("pyxcp", "asamkeydll.exe") + self.arguments = [asamkeydll, f"-o{target}"] + + def run(self): + """Run gcc""" + word_width, _ = platform.architecture() + if sys.platform == "win32" and word_width == "64bit": + gccCmd = ["gcc", "-m32", "-O3", "-Wall"] + self.announce(" ".join(gccCmd + self.arguments)) + try: + subprocess.check_call(gccCmd + self.arguments) # nosec + except Exception as e: + print(f"Building pyxcp/asamkeydll.exe failed: '{str(e)}'") + else: + print("Successfully build pyxcp/asamkeydll.exe") + + +class CustomBuildPy(setuptools.command.build_py.build_py): + def run(self): + self.run_command("asamkeydll") + super().run() + + +class CustomDevelop(setuptools.command.develop.develop): + def run(self): + self.run_command("asamkeydll") + super().run() + def build(setup_kwargs: Dict[str, Any]) -> None: setup_kwargs.update( @@ -31,3 +101,27 @@ def build(setup_kwargs: Dict[str, Any]) -> None: "zip_safe": False, } ) + +def invoke_command(distribution: Distribution, name: str) -> None: + cmd = distribution.cmdclass.get(name)(distribution) + print(f"Building target {name!r}...") + cmd.inplace = 1 + cmd.ensure_finalized() + cmd.run() + +### +if __name__ == "__main__": + distribution = Distribution( + { + "cmdclass": { + "asam_key_dll": AsamKeyDllAutogen, + "CXX_extensions": build_ext, + }, + "name": "pyxcp", + "ext_modules": ext_modules, + "package_dir": {"pyxcp": str(ROOT_DIRPATH / "pyxcp")}, + } + ) + + invoke_command(distribution, "asam_key_dll") + invoke_command(distribution, "CXX_extensions") diff --git a/pyproject.toml b/pyproject.toml index c707c7c..88e1c06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["poetry-core>=1.0.0", "setuptools>=68.0.0", ] # , "pybind11<3.0.0,>=2.9.0", "wheel", "build", "twine" +requires = ["poetry-core>=1.0.0", "setuptools>=68.0.0" , "pybind11>=2.11.1"] build-backend = "poetry.core.masonry.api" diff --git a/pyxcp/daq_stim/scheduler.cpp b/pyxcp/daq_stim/scheduler.cpp index 108bb71..ee0fd80 100644 --- a/pyxcp/daq_stim/scheduler.cpp +++ b/pyxcp/daq_stim/scheduler.cpp @@ -1,6 +1,8 @@ #include "scheduler.hpp" +#if defined(_WIN32) + VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) { if (lpParam == NULL) { printf("TimerRoutine lpParam is NULL\n"); @@ -23,3 +25,5 @@ void mul4_vectorized(float* ptr) { f = _mm_mul_ps(f, f); _mm_storeu_ps(ptr, f); } +#endif + diff --git a/pyxcp/daq_stim/scheduler.hpp b/pyxcp/daq_stim/scheduler.hpp index b0bdbcb..5233412 100644 --- a/pyxcp/daq_stim/scheduler.hpp +++ b/pyxcp/daq_stim/scheduler.hpp @@ -6,6 +6,8 @@ #define _CRT_SECURE_NO_WARNINGS (1) #include + +#if defined(_WIN32) #include #include @@ -59,5 +61,6 @@ struct Scheduler { HANDLE m_timer{}; HANDLE m_TimerQueue; }; +#endif #endif // STIM_SCHEDULER_HPP From 457b795e52fb2dcdd6c7cd20a14373e67beb6b13 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 11 Feb 2024 14:22:07 +0200 Subject: [PATCH 24/99] Fix MacOS --- pyxcp/daq_stim/scheduler.hpp | 8 ++++++++ pyxcp/daq_stim/stim.hpp | 8 ++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pyxcp/daq_stim/scheduler.hpp b/pyxcp/daq_stim/scheduler.hpp index 8d25a3f..662c798 100644 --- a/pyxcp/daq_stim/scheduler.hpp +++ b/pyxcp/daq_stim/scheduler.hpp @@ -63,6 +63,14 @@ struct Scheduler { HANDLE m_timer{}; HANDLE m_TimerQueue; }; +#else + +struct Scheduler { + Scheduler() = default; + ~Scheduler() = default; + +}; + #endif #endif // STIM_SCHEDULER_HPP diff --git a/pyxcp/daq_stim/stim.hpp b/pyxcp/daq_stim/stim.hpp index 6abf362..66e68d1 100644 --- a/pyxcp/daq_stim/stim.hpp +++ b/pyxcp/daq_stim/stim.hpp @@ -21,8 +21,10 @@ #include "scheduler.hpp" #include "helper.hpp" +#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64) ) #include - #include + #include )>; explicit Stim(bool activate = true) : m_activate(activate) { +#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64) ) if (timeBeginPeriod(100) == TIMERR_NOERROR) { // std::cout << "timeBeginPeriod() OK!!!" << std::endl; } else { @@ -284,8 +287,9 @@ class Stim { auto xxx = AvSetMmThreadCharacteristics("Pro Audio", &task_index); auto start = timeGetTime(); - +#endif // m_scheduler.start_thread(); + } void setParameters(const StimParameters& params) { From 46194d0f12a89b1604b4e991b673b127221a0d8c Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 12 Feb 2024 10:05:04 +0200 Subject: [PATCH 25/99] MacOS patches --- pyxcp/dllif.py | 2 +- pyxcp/examples/run_daq.py | 2 +- pyxcp/scripts/xcp_id_scanner.py | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pyxcp/dllif.py b/pyxcp/dllif.py index 8f00fa2..b203534 100644 --- a/pyxcp/dllif.py +++ b/pyxcp/dllif.py @@ -27,7 +27,7 @@ class SeedNKeyError(Exception): bwidth, _ = platform.architecture() -if sys.platform in ("win32", "linux"): +if sys.platform in ("win32", "linux", "darwin"): if bwidth == "64bit": use_ctypes = False elif bwidth == "32bit": diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 0ad6e95..e452c9a 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -128,7 +128,7 @@ if x.slaveProperties.optionalCommMode: x.getCommModeInfo() - x.cond_unlock() # DAQ resource is locked in many cases. + x.cond_unlock("DAQ") # DAQ resource is locked in many cases. print("setup DAQ lists.") daq_parser.setup() # Run internal and XCP setup procedures. diff --git a/pyxcp/scripts/xcp_id_scanner.py b/pyxcp/scripts/xcp_id_scanner.py index 9f365ce..24f60cc 100644 --- a/pyxcp/scripts/xcp_id_scanner.py +++ b/pyxcp/scripts/xcp_id_scanner.py @@ -11,10 +11,8 @@ def main(): with ap.run() as x: x.connect() result = x.id_scanner() - print("\n") - print("Implemented IDs".center(80)) - print("=" * 80) - pprint(result) + for key, value in result.items(): + print(f"{key}: {value}", end="\n\n") x.disconnect() From a9b8f2baa67f1dc59c62eb13efcf860a19261945 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 13 Feb 2024 06:57:14 +0200 Subject: [PATCH 26/99] Fix compilation issues --- build_ext.py | 6 +- poetry.lock | 2519 +++++++++++++++++++++++++++++++ pyproject.toml | 9 + pyxcp/cpp_ext/mcobject.hpp | 4 +- pyxcp/daq_stim/stim.cpp | 2 + pyxcp/daq_stim/stim.hpp | 4 +- pyxcp/daq_stim/stim_wrapper.cpp | 2 + pyxcp/recorder/unfolder.hpp | 21 +- 8 files changed, 2552 insertions(+), 15 deletions(-) create mode 100644 poetry.lock diff --git a/build_ext.py b/build_ext.py index b262e97..4bacb37 100644 --- a/build_ext.py +++ b/build_ext.py @@ -70,10 +70,11 @@ def finalize_options(self): def run(self): """Run gcc""" word_width, _ = platform.architecture() - if sys.platform == "win32" and word_width == "64bit": + if sys.platform in ("win32") and word_width == "64bit": gccCmd = ["gcc", "-m32", "-O3", "-Wall"] self.announce(" ".join(gccCmd + self.arguments)) try: + print("running: ", gccCmd + self.arguments) subprocess.check_call(gccCmd + self.arguments) # nosec except Exception as e: print(f"Building pyxcp/asamkeydll.exe failed: '{str(e)}'") @@ -122,6 +123,5 @@ def invoke_command(distribution: Distribution, name: str) -> None: "package_dir": {"pyxcp": str(ROOT_DIRPATH / "pyxcp")}, } ) - - invoke_command(distribution, "asam_key_dll") invoke_command(distribution, "CXX_extensions") + invoke_command(distribution, "asam_key_dll") \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..137c8a0 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2519 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "alabaster" +version = "0.7.13" +description = "A configurable sidebar-enabled Sphinx theme" +optional = false +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] + +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = "*" +files = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "authlib" +version = "1.2.1" +description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +optional = false +python-versions = "*" +files = [ + {file = "Authlib-1.2.1-py2.py3-none-any.whl", hash = "sha256:c88984ea00149a90e3537c964327da930779afa4564e354edfd98410bea01911"}, + {file = "Authlib-1.2.1.tar.gz", hash = "sha256:421f7c6b468d907ca2d9afede256f068f87e34d23dd221c07d13d4c234726afb"}, +] + +[package.dependencies] +cryptography = ">=3.2" + +[[package]] +name = "babel" +version = "2.14.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, +] + +[package.dependencies] +pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "bandit" +version = "1.7.5" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.7" +files = [ + {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, + {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +GitPython = ">=1.0.1" +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" + +[package.extras] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] +toml = ["tomli (>=1.1.0)"] +yaml = ["PyYAML"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "black" +version = "23.3.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.7" +files = [ + {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, + {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, + {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, + {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, + {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, + {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, + {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, + {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, + {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, + {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, + {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, + {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, + {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, + {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] + +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "construct" +version = "2.10.70" +description = "A powerful declarative symmetric parser/builder for binary data" +optional = false +python-versions = ">=3.6" +files = [ + {file = "construct-2.10.70-py3-none-any.whl", hash = "sha256:c80be81ef595a1a821ec69dc16099550ed22197615f4320b57cc9ce2a672cb30"}, + {file = "construct-2.10.70.tar.gz", hash = "sha256:4d2472f9684731e58cc9c56c463be63baa1447d674e0d66aeb5627b22f512c29"}, +] + +[package.extras] +extras = ["arrow", "cloudpickle", "cryptography", "lz4", "numpy", "ruamel.yaml"] + +[[package]] +name = "coverage" +version = "7.2.7" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, + {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, + {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, + {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, + {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, + {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, + {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, + {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, + {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, + {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, + {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, + {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, + {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, + {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, + {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, + {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, + {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, + {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "cryptography" +version = "42.0.2" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, + {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, + {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, + {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, + {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, + {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, + {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "darglint" +version = "1.8.1" +description = "A utility for ensuring Google-style docstrings stay up to date with the source code." +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, + {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, +] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "docutils" +version = "0.17.1" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, + {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, +] + +[[package]] +name = "dparse" +version = "0.6.4b0" +description = "A parser for Python dependency files" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dparse-0.6.4b0-py3-none-any.whl", hash = "sha256:592ff183348b8a5ea0a18442a7965e29445d3a26063654ec2c7e8ef42cd5753c"}, + {file = "dparse-0.6.4b0.tar.gz", hash = "sha256:f8d49b41a527f3d16a269f854e6665245b325e50e41d2c213810cb984553e5c8"}, +] + +[package.dependencies] +packaging = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +all = ["dparse[conda]", "dparse[pipenv]", "dparse[poetry]"] +conda = ["pyyaml"] +pipenv = ["pipenv"] +poetry = ["poetry"] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.12.2" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, + {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, +] + +[package.extras] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "flake8" +version = "5.0.4" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, + {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=1.1.0,<4.3", markers = "python_version < \"3.8\""} +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.9.0,<2.10.0" +pyflakes = ">=2.5.0,<2.6.0" + +[[package]] +name = "flake8-bugbear" +version = "23.3.12" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-bugbear-23.3.12.tar.gz", hash = "sha256:e3e7f74c8a49ad3794a7183353026dabd68c74030d5f46571f84c1fb0eb79363"}, + {file = "flake8_bugbear-23.3.12-py3-none-any.whl", hash = "sha256:beb5c7efcd7ccc2039ef66a77bb8db925e7be3531ff1cb4d0b7030d0e2113d72"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +flake8 = ">=3.0.0" + +[package.extras] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] + +[[package]] +name = "flake8-docstrings" +version = "1.7.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, + {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, +] + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-rst-docstrings" +version = "0.3.0" +description = "Python docstring reStructuredText (RST) validator for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-rst-docstrings-0.3.0.tar.gz", hash = "sha256:d1ce22b4bd37b73cd86b8d980e946ef198cfcc18ed82fedb674ceaa2f8d1afa4"}, + {file = "flake8_rst_docstrings-0.3.0-py3-none-any.whl", hash = "sha256:f8c3c6892ff402292651c31983a38da082480ad3ba253743de52989bdc84ca1c"}, +] + +[package.dependencies] +flake8 = ">=3" +pygments = "*" +restructuredtext-lint = "*" + +[package.extras] +develop = ["build", "twine"] + +[[package]] +name = "furo" +version = "2022.9.29" +description = "A clean customisable Sphinx documentation theme." +optional = false +python-versions = ">=3.7" +files = [ + {file = "furo-2022.9.29-py3-none-any.whl", hash = "sha256:559ee17999c0f52728481dcf6b1b0cf8c9743e68c5e3a18cb45a7992747869a9"}, + {file = "furo-2022.9.29.tar.gz", hash = "sha256:d4238145629c623609c2deb5384f8d036e2a1ee2a101d64b67b4348112470dbd"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=4.0,<6.0" +sphinx-basic-ng = "*" + +[[package]] +name = "gitdb" +version = "4.0.11" +description = "Git Object Database" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, + {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, +] + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "gitpython" +version = "3.1.41" +description = "GitPython is a Python library used to interact with Git repositories" +optional = false +python-versions = ">=3.7" +files = [ + {file = "GitPython-3.1.41-py3-none-any.whl", hash = "sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c"}, + {file = "GitPython-3.1.41.tar.gz", hash = "sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048"}, +] + +[package.dependencies] +gitdb = ">=4.0.1,<5" +typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} + +[package.extras] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "sumtypes"] + +[[package]] +name = "identify" +version = "2.5.24" +description = "File identification library for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, + {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "4.2.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.6" +files = [ + {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, + {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.11.5" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, + {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, +] + +[package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "line-profiler" +version = "4.1.2" +description = "Line-by-line profiler" +optional = false +python-versions = ">=3.6" +files = [ + {file = "line_profiler-4.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4344c1504ad1a57029a8ab30812d967a0917cad7b654077e8787e4a7d7ea3469"}, + {file = "line_profiler-4.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0720b356db3e9ca297c3260f280c5be3bb4b230eda61ce73b4df5e553418d37a"}, + {file = "line_profiler-4.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:09f742af37166768f92495bd3d3a71da1ba41d3004307a66c108f29ed947d6e1"}, + {file = "line_profiler-4.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:443a5df10eb7910df694340c8a81c1668a88bb59ca44149a3291f7b2ae960891"}, + {file = "line_profiler-4.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a906f9d1687eea7e5b22e3bd367d4b63706fcea1906baaad76b1cc4c1142553d"}, + {file = "line_profiler-4.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3b2c8cc34a776c5cfaa4a4a09a51541efcc9082dce15b19e494000e82576ced"}, + {file = "line_profiler-4.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:55ca0a78eb8d52515486c374ec53fa9e65e3c4128e8bbc909d8bfce267a91fdd"}, + {file = "line_profiler-4.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:f4a11389f06831d7984b63be0743fbbbae1ffb56fad04b4e538d3e6933b5c265"}, + {file = "line_profiler-4.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:32fa07f6fecfd209329559e4ae945dc7bdc0703355c8924bbf19101495b2373f"}, + {file = "line_profiler-4.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f8e9e8af6660629f214e424613c56a6622cf36d9c638c569c926b21374d7029"}, + {file = "line_profiler-4.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4753113c4e2c30a547937dbc456900d7f3a1b99bc8bc81a640a89306cd729c0f"}, + {file = "line_profiler-4.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f0989302404850a2a041ba60afe6c7240aea10fdd9432d5c1d464aca39a0369"}, + {file = "line_profiler-4.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f4b25ee412b0cd624614edd16c4c0af02dbeb73db2a08a49a14b120005a5630"}, + {file = "line_profiler-4.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93c6a49009ee75dcd8ff644c5fd39eeb8bb672d5a41bacdd239db14ae1ba3098"}, + {file = "line_profiler-4.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b96964cdb306741a01b95d210d634cc79ed70d2904336cbd8f69a9b5f284426d"}, + {file = "line_profiler-4.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:46a8cad2cb4b6a1229ddccf06694b1d01fd5acd1cf8c502caf937765a7c877de"}, + {file = "line_profiler-4.1.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a102fd8e13abd367379e39fd9426fd60e1e3a39fcd80fa25641618969464c022"}, + {file = "line_profiler-4.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44ee51bce974d6b2269492299d4abae6db1b06ae7617760c7436c597dbdbd032"}, + {file = "line_profiler-4.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e4cafd9a1effe1b9646f6a86716dbd291684fde1f8a297930d845d8a9340299"}, + {file = "line_profiler-4.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b433a2918e522d6dd0e6bdcf1216cede15c4f201f7eeb0d816114fbac5031cd7"}, + {file = "line_profiler-4.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad96accb1f5cdedfe2e6607f9be86d28196d3f743229e2b67bd28a40f76f133"}, + {file = "line_profiler-4.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4eb9df035861f7c2e9852773dff72a3324e2e5aebc0b8c7c2ba22437387ef5e7"}, + {file = "line_profiler-4.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e733c0e6626d0e9f1b434da40b93ed1c00ea503f3ced04f5a58c22d1163fe1c1"}, + {file = "line_profiler-4.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:8cc0c24384e29e99da5627669dbf312a23d11138de0169aa58d4ea5187522ba0"}, + {file = "line_profiler-4.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:900ad7be6d609fb1442200c7757de3534b381d6eeac22fa0135c5d0a900b5787"}, + {file = "line_profiler-4.1.2-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49c6c6e19c3c0d7cc8f1641ece9e52fec5e99c56472e26156c16473b7568d374"}, + {file = "line_profiler-4.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7ed1edd85f9a005a3e1316b3962a5fc42a159257cf2dfd13d10fcbefaece8ce"}, + {file = "line_profiler-4.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:2ed7027f7d1b3ae9a379a2f407f512b84ccf82d6a3a7b53a90bb17ada61928a9"}, + {file = "line_profiler-4.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e8537be16b46133ab86d6e805ca83b012b17ef36a7445dd5c89c45ba70b97aad"}, + {file = "line_profiler-4.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:934870b5e451c938f149c5475cc0286133d8718ba99ff4ec04fb1a87f7bfb985"}, + {file = "line_profiler-4.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbda8e0bb98b1790ba8819d0a72ee3e11e669c79fc703eaf0e5ed747cac2d441"}, + {file = "line_profiler-4.1.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cfd263c79927f74f174e32b83e4692e26ada2fefcdfef0c1dae5cfabb37a37"}, + {file = "line_profiler-4.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:390f5e5dc047a62ffb7dbd236b4d44c6175d4f66aabe654f4b35df9b9aa79d02"}, + {file = "line_profiler-4.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dce014572ee599b2d571cf45fbd0c7d5f1a1e822dabe82581e18dd0229b16799"}, + {file = "line_profiler-4.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4fe92a239d8097a3a0cacb280e0a2455be6633da3c844b784ba011043d090b36"}, + {file = "line_profiler-4.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:3df9b30cdd8b3652e658acb38a9533bac47f2b8f5c320c5a03dbdd378ac11b35"}, + {file = "line_profiler-4.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5643cb19c89f6749039452913803a8cfb554c07676f6c00bc96e0632a054abb6"}, + {file = "line_profiler-4.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:163d26586511b68551735052a1bcca173c9d8366573ab4a91c470c7f7bd89967"}, + {file = "line_profiler-4.1.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8fa3128e93e49ad8b5216e40dc9d2bc2e354e896c1512feead3d6db1668ce649"}, + {file = "line_profiler-4.1.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a1eb88cec273300377b364eee9ceffce2e639906bf210e7d7233c88dc87e62f"}, + {file = "line_profiler-4.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7f213eeb846c9bc950fd210dfcd0fa93b1d2991f218b8788c0759f06bd00557"}, + {file = "line_profiler-4.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ec6f137dbbdc0af6b88a1053c1430681c07a3b2d1719dc1f59be70d464851a23"}, + {file = "line_profiler-4.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3af457b2dfad6e2019f7e5bbe9eabac9b2c34824fb2ea574aee7b17998c48c98"}, + {file = "line_profiler-4.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:9dd72adc753019788ff0498dd686068c4d8e65d38c0eca1b4b58b5719c14fa7d"}, + {file = "line_profiler-4.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:62776d67dfc6c358de5c19d606eccbd95e6feb75928064850be0232e9276f751"}, + {file = "line_profiler-4.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:060d71ba11ff5476d7c10774a34955566bab545ab5ff39231306b4d84081725d"}, + {file = "line_profiler-4.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ad13e1d5a174336508bbf275202822c8898cd1f014881059103b748310d5bc84"}, + {file = "line_profiler-4.1.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77824dfc1f58dc7fe62fb053aa54586979ef60fea221dcdbba2022608c1314f"}, + {file = "line_profiler-4.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8ffc44a030789f7bc6594de581b39e8da0591fc6c598dd4243cf140b200528"}, + {file = "line_profiler-4.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4729820d8da3ed92f14e30dbd28a851eeefe2ba70b8b897f2d9c886ade8007c1"}, + {file = "line_profiler-4.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0bce5c04d0daf6dd19348540012b0a6d69206ae40db096de222e6d5f824922e8"}, + {file = "line_profiler-4.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:a65b70d6ecef4f2e61cf504a5c77085718f1dae9508b21c9058ad483ae7e16ee"}, + {file = "line_profiler-4.1.2.tar.gz", hash = "sha256:aa56578b0ff5a756fe180b3fda7bd67c27bbd478b3d0124612d8cf00e4a21df2"}, +] + +[package.extras] +all = ["Cython (>=3.0.3)", "IPython (>=7.14.0)", "IPython (>=7.18.0)", "IPython (>=8.12.2)", "IPython (>=8.14.0)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.8.1)", "cmake (>=3.21.2)", "coverage[toml] (>=5.3)", "ninja (>=1.10.2)", "pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=4.6.0,<=4.6.11)", "pytest (>=4.6.0,<=4.6.11)", "pytest (>=4.6.0,<=6.1.2)", "pytest (>=6.2.5)", "pytest-cov (>=2.8.1)", "pytest-cov (>=2.8.1)", "pytest-cov (>=2.9.0)", "pytest-cov (>=3.0.0)", "rich (>=12.3.0)", "scikit-build (>=0.11.1)", "setuptools (>=41.0.1)", "setuptools (>=68.2.2)", "ubelt (>=1.3.4)", "xdoctest (>=1.1.2)"] +all-strict = ["Cython (==3.0.3)", "IPython (==7.14.0)", "IPython (==7.18.0)", "IPython (==8.12.2)", "IPython (==8.14.0)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.8.1)", "cmake (==3.21.2)", "coverage[toml] (==5.3)", "ninja (==1.10.2)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "rich (==12.3.0)", "scikit-build (==0.11.1)", "setuptools (==41.0.1)", "setuptools (==68.2.2)", "ubelt (==1.3.4)", "xdoctest (==1.1.2)"] +ipython = ["IPython (>=7.14.0)", "IPython (>=7.18.0)", "IPython (>=8.12.2)", "IPython (>=8.14.0)"] +ipython-strict = ["IPython (==7.14.0)", "IPython (==7.18.0)", "IPython (==8.12.2)", "IPython (==8.14.0)"] +optional = ["IPython (>=7.14.0)", "IPython (>=7.18.0)", "IPython (>=8.12.2)", "IPython (>=8.14.0)", "rich (>=12.3.0)"] +optional-strict = ["IPython (==7.14.0)", "IPython (==7.18.0)", "IPython (==8.12.2)", "IPython (==8.14.0)", "rich (==12.3.0)"] +tests = ["coverage[toml] (>=5.3)", "pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=4.6.0,<=4.6.11)", "pytest (>=4.6.0,<=4.6.11)", "pytest (>=4.6.0,<=6.1.2)", "pytest (>=6.2.5)", "pytest-cov (>=2.8.1)", "pytest-cov (>=2.8.1)", "pytest-cov (>=2.9.0)", "pytest-cov (>=3.0.0)", "ubelt (>=1.3.4)", "xdoctest (>=1.1.2)"] +tests-strict = ["coverage[toml] (==5.3)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "ubelt (==1.3.4)", "xdoctest (==1.1.2)"] + +[[package]] +name = "line-profiler-pycharm" +version = "1.1.0" +description = "PyCharm Line Profiler helper package with which one can visualize profiles from the 'line-profiler' into PyCharm" +optional = false +python-versions = ">=3" +files = [ + {file = "line-profiler-pycharm-1.1.0.tar.gz", hash = "sha256:e419de1db1b0ed49213d0b9ee33f99f24f6a2d431c7a76d39361f2c2a031af30"}, + {file = "line_profiler_pycharm-1.1.0-py3-none-any.whl", hash = "sha256:3b9d80571d44685a7c5a01e3985965cd76fe523c22db6250cefb4a75ebd73fc7"}, +] + +[package.dependencies] +line-profiler = "*" + +[[package]] +name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +optional = false +python-versions = "*" +files = [ + {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, +] + +[package.dependencies] +six = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} + +[[package]] +name = "mako" +version = "1.2.4" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, + {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, +] + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markdown-it-py" +version = "2.2.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.7" +files = [ + {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, + {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" +typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "marshmallow" +version = "3.19.0" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.7" +files = [ + {file = "marshmallow-3.19.0-py3-none-any.whl", hash = "sha256:93f0958568da045b0021ec6aeb7ac37c81bfcccbb9a0e7ed8559885070b3a19b"}, + {file = "marshmallow-3.19.0.tar.gz", hash = "sha256:90032c0fd650ce94b6ec6dc8dfeb0e3ff50c144586462c389b81a07205bedb78"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.3.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.3.5" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdit-py-plugins-0.3.5.tar.gz", hash = "sha256:eee0adc7195e5827e17e02d2a258a2ba159944a0748f59c5099a4a27f78fcf6a"}, + {file = "mdit_py_plugins-0.3.5-py3-none-any.whl", hash = "sha256:ca9a0714ea59a24b2b044a1831f48d817dd0c817e84339f20e7889f392d77c4e"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<3.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["attrs", "myst-parser (>=0.16.1,<0.17.0)", "sphinx-book-theme (>=0.1.0,<0.2.0)"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "msgpack" +version = "1.0.5" +description = "MessagePack serializer" +optional = false +python-versions = "*" +files = [ + {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:525228efd79bb831cf6830a732e2e80bc1b05436b086d4264814b4b2955b2fa9"}, + {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f8d8b3bf1ff2672567d6b5c725a1b347fe838b912772aa8ae2bf70338d5a198"}, + {file = "msgpack-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdc793c50be3f01106245a61b739328f7dccc2c648b501e237f0699fe1395b81"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb47c21a8a65b165ce29f2bec852790cbc04936f502966768e4aae9fa763cb7"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42b9594cc3bf4d838d67d6ed62b9e59e201862a25e9a157019e171fbe672dd3"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55b56a24893105dc52c1253649b60f475f36b3aa0fc66115bffafb624d7cb30b"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1967f6129fc50a43bfe0951c35acbb729be89a55d849fab7686004da85103f1c"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a97bf595a232c3ee6d57ddaadd5453d174a52594bf9c21d10407e2a2d9b3bd"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d25dd59bbbbb996eacf7be6b4ad082ed7eacc4e8f3d2df1ba43822da9bfa122a"}, + {file = "msgpack-1.0.5-cp310-cp310-win32.whl", hash = "sha256:382b2c77589331f2cb80b67cc058c00f225e19827dbc818d700f61513ab47bea"}, + {file = "msgpack-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:4867aa2df9e2a5fa5f76d7d5565d25ec76e84c106b55509e78c1ede0f152659a"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f5ae84c5c8a857ec44dc180a8b0cc08238e021f57abdf51a8182e915e6299f0"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e6ca5d5699bcd89ae605c150aee83b5321f2115695e741b99618f4856c50898"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5494ea30d517a3576749cad32fa27f7585c65f5f38309c88c6d137877fa28a5a"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab2f3331cb1b54165976a9d976cb251a83183631c88076613c6c780f0d6e45a"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28592e20bbb1620848256ebc105fc420436af59515793ed27d5c77a217477705"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe5c63197c55bce6385d9aee16c4d0641684628f63ace85f73571e65ad1c1e8d"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed40e926fa2f297e8a653c954b732f125ef97bdd4c889f243182299de27e2aa9"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b2de4c1c0538dcb7010902a2b97f4e00fc4ddf2c8cda9749af0e594d3b7fa3d7"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf22a83f973b50f9d38e55c6aade04c41ddda19b00c4ebc558930d78eecc64ed"}, + {file = "msgpack-1.0.5-cp311-cp311-win32.whl", hash = "sha256:c396e2cc213d12ce017b686e0f53497f94f8ba2b24799c25d913d46c08ec422c"}, + {file = "msgpack-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c4c68d87497f66f96d50142a2b73b97972130d93677ce930718f68828b382e2"}, + {file = "msgpack-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a2b031c2e9b9af485d5e3c4520f4220d74f4d222a5b8dc8c1a3ab9448ca79c57"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f837b93669ce4336e24d08286c38761132bc7ab29782727f8557e1eb21b2080"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1d46dfe3832660f53b13b925d4e0fa1432b00f5f7210eb3ad3bb9a13c6204a6"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:366c9a7b9057e1547f4ad51d8facad8b406bab69c7d72c0eb6f529cf76d4b85f"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4c075728a1095efd0634a7dccb06204919a2f67d1893b6aa8e00497258bf926c"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:f933bbda5a3ee63b8834179096923b094b76f0c7a73c1cfe8f07ad608c58844b"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:36961b0568c36027c76e2ae3ca1132e35123dcec0706c4b7992683cc26c1320c"}, + {file = "msgpack-1.0.5-cp36-cp36m-win32.whl", hash = "sha256:b5ef2f015b95f912c2fcab19c36814963b5463f1fb9049846994b007962743e9"}, + {file = "msgpack-1.0.5-cp36-cp36m-win_amd64.whl", hash = "sha256:288e32b47e67f7b171f86b030e527e302c91bd3f40fd9033483f2cacc37f327a"}, + {file = "msgpack-1.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:137850656634abddfb88236008339fdaba3178f4751b28f270d2ebe77a563b6c"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c05a4a96585525916b109bb85f8cb6511db1c6f5b9d9cbcbc940dc6b4be944b"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a62ec00b636583e5cb6ad313bbed36bb7ead5fa3a3e38938503142c72cba4f"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef8108f8dedf204bb7b42994abf93882da1159728a2d4c5e82012edd92c9da9f"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1835c84d65f46900920b3708f5ba829fb19b1096c1800ad60bae8418652a951d"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e57916ef1bd0fee4f21c4600e9d1da352d8816b52a599c46460e93a6e9f17086"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:17358523b85973e5f242ad74aa4712b7ee560715562554aa2134d96e7aa4cbbf"}, + {file = "msgpack-1.0.5-cp37-cp37m-win32.whl", hash = "sha256:cb5aaa8c17760909ec6cb15e744c3ebc2ca8918e727216e79607b7bbce9c8f77"}, + {file = "msgpack-1.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:ab31e908d8424d55601ad7075e471b7d0140d4d3dd3272daf39c5c19d936bd82"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b72d0698f86e8d9ddf9442bdedec15b71df3598199ba33322d9711a19f08145c"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:379026812e49258016dd84ad79ac8446922234d498058ae1d415f04b522d5b2d"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:332360ff25469c346a1c5e47cbe2a725517919892eda5cfaffe6046656f0b7bb"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:476a8fe8fae289fdf273d6d2a6cb6e35b5a58541693e8f9f019bfe990a51e4ba"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9985b214f33311df47e274eb788a5893a761d025e2b92c723ba4c63936b69b1"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48296af57cdb1d885843afd73c4656be5c76c0c6328db3440c9601a98f303d87"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:addab7e2e1fcc04bd08e4eb631c2a90960c340e40dfc4a5e24d2ff0d5a3b3edb"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:916723458c25dfb77ff07f4c66aed34e47503b2eb3188b3adbec8d8aa6e00f48"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:821c7e677cc6acf0fd3f7ac664c98803827ae6de594a9f99563e48c5a2f27eb0"}, + {file = "msgpack-1.0.5-cp38-cp38-win32.whl", hash = "sha256:1c0f7c47f0087ffda62961d425e4407961a7ffd2aa004c81b9c07d9269512f6e"}, + {file = "msgpack-1.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:bae7de2026cbfe3782c8b78b0db9cbfc5455e079f1937cb0ab8d133496ac55e1"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:20c784e66b613c7f16f632e7b5e8a1651aa5702463d61394671ba07b2fc9e025"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:266fa4202c0eb94d26822d9bfd7af25d1e2c088927fe8de9033d929dd5ba24c5"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18334484eafc2b1aa47a6d42427da7fa8f2ab3d60b674120bce7a895a0a85bdd"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57e1f3528bd95cc44684beda696f74d3aaa8a5e58c816214b9046512240ef437"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586d0d636f9a628ddc6a17bfd45aa5b5efaf1606d2b60fa5d87b8986326e933f"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a740fa0e4087a734455f0fc3abf5e746004c9da72fbd541e9b113013c8dc3282"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3055b0455e45810820db1f29d900bf39466df96ddca11dfa6d074fa47054376d"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a61215eac016f391129a013c9e46f3ab308db5f5ec9f25811e811f96962599a8"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:362d9655cd369b08fda06b6657a303eb7172d5279997abe094512e919cf74b11"}, + {file = "msgpack-1.0.5-cp39-cp39-win32.whl", hash = "sha256:ac9dd47af78cae935901a9a500104e2dea2e253207c924cc95de149606dc43cc"}, + {file = "msgpack-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:06f5174b5f8ed0ed919da0e62cbd4ffde676a374aba4020034da05fab67b9164"}, + {file = "msgpack-1.0.5.tar.gz", hash = "sha256:c075544284eadc5cddc70f4757331d99dcbc16b2bbd4849d15f8aae4cf36d31c"}, +] + +[[package]] +name = "mypy" +version = "1.4.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, + {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, + {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, + {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, + {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, + {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, + {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, + {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, + {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, + {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, + {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, + {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, + {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, + {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, + {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, + {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, + {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, + {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, + {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, + {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "myst-parser" +version = "0.18.1" +description = "An extended commonmark compliant parser, with bridges to docutils & sphinx." +optional = false +python-versions = ">=3.7" +files = [ + {file = "myst-parser-0.18.1.tar.gz", hash = "sha256:79317f4bb2c13053dd6e64f9da1ba1da6cd9c40c8a430c447a7b146a594c246d"}, + {file = "myst_parser-0.18.1-py3-none-any.whl", hash = "sha256:61b275b85d9f58aa327f370913ae1bec26ebad372cc99f3ab85c8ec3ee8d9fb8"}, +] + +[package.dependencies] +docutils = ">=0.15,<0.20" +jinja2 = "*" +markdown-it-py = ">=1.0.0,<3.0.0" +mdit-py-plugins = ">=0.3.1,<0.4.0" +pyyaml = "*" +sphinx = ">=4,<6" +typing-extensions = "*" + +[package.extras] +code-style = ["pre-commit (>=2.12,<3.0)"] +linkify = ["linkify-it-py (>=1.0,<2.0)"] +rtd = ["ipython", "sphinx-book-theme", "sphinx-design", "sphinxcontrib.mermaid (>=0.7.1,<0.8.0)", "sphinxext-opengraph (>=0.6.3,<0.7.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] +testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=6,<7)", "pytest-cov", "pytest-param-files (>=0.3.4,<0.4.0)", "pytest-regressions", "sphinx (<5.2)", "sphinx-pytest"] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pathspec" +version = "0.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "pbr" +version = "6.0.0" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +files = [ + {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, + {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, +] + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "4.0.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, + {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + +[[package]] +name = "pluggy" +version = "1.2.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.20.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, + {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + +[[package]] +name = "pre-commit-hooks" +version = "4.4.0" +description = "Some out-of-the-box hooks for pre-commit." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pre_commit_hooks-4.4.0-py2.py3-none-any.whl", hash = "sha256:fc8837335476221ccccda3d176ed6ae29fe58753ce7e8b7863f5d0f987328fc6"}, + {file = "pre_commit_hooks-4.4.0.tar.gz", hash = "sha256:7011eed8e1a25cde94693da009cba76392194cecc2f3f06c51a44ea6ad6c2af9"}, +] + +[package.dependencies] +"ruamel.yaml" = ">=0.15" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "pycodestyle" +version = "2.9.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, + {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pydantic" +version = "1.10.14" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4fcec873f90537c382840f330b90f4715eebc2bc9925f04cb92de593eae054"}, + {file = "pydantic-1.10.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e3a76f571970fcd3c43ad982daf936ae39b3e90b8a2e96c04113a369869dc87"}, + {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d886bd3c3fbeaa963692ef6b643159ccb4b4cefaf7ff1617720cbead04fd1d"}, + {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:798a3d05ee3b71967844a1164fd5bdb8c22c6d674f26274e78b9f29d81770c4e"}, + {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:23d47a4b57a38e8652bcab15a658fdb13c785b9ce217cc3a729504ab4e1d6bc9"}, + {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9f674b5c3bebc2eba401de64f29948ae1e646ba2735f884d1594c5f675d6f2a"}, + {file = "pydantic-1.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:24a7679fab2e0eeedb5a8924fc4a694b3bcaac7d305aeeac72dd7d4e05ecbebf"}, + {file = "pydantic-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d578ac4bf7fdf10ce14caba6f734c178379bd35c486c6deb6f49006e1ba78a7"}, + {file = "pydantic-1.10.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa7790e94c60f809c95602a26d906eba01a0abee9cc24150e4ce2189352deb1b"}, + {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad4e10efa5474ed1a611b6d7f0d130f4aafadceb73c11d9e72823e8f508e663"}, + {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1245f4f61f467cb3dfeced2b119afef3db386aec3d24a22a1de08c65038b255f"}, + {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:21efacc678a11114c765eb52ec0db62edffa89e9a562a94cbf8fa10b5db5c046"}, + {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:412ab4a3f6dbd2bf18aefa9f79c7cca23744846b31f1d6555c2ee2b05a2e14ca"}, + {file = "pydantic-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:e897c9f35281f7889873a3e6d6b69aa1447ceb024e8495a5f0d02ecd17742a7f"}, + {file = "pydantic-1.10.14-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d604be0f0b44d473e54fdcb12302495fe0467c56509a2f80483476f3ba92b33c"}, + {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42c7d17706911199798d4c464b352e640cab4351efe69c2267823d619a937e5"}, + {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:596f12a1085e38dbda5cbb874d0973303e34227b400b6414782bf205cc14940c"}, + {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bfb113860e9288d0886e3b9e49d9cf4a9d48b441f52ded7d96db7819028514cc"}, + {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bc3ed06ab13660b565eed80887fcfbc0070f0aa0691fbb351657041d3e874efe"}, + {file = "pydantic-1.10.14-cp37-cp37m-win_amd64.whl", hash = "sha256:ad8c2bc677ae5f6dbd3cf92f2c7dc613507eafe8f71719727cbc0a7dec9a8c01"}, + {file = "pydantic-1.10.14-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c37c28449752bb1f47975d22ef2882d70513c546f8f37201e0fec3a97b816eee"}, + {file = "pydantic-1.10.14-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49a46a0994dd551ec051986806122767cf144b9702e31d47f6d493c336462597"}, + {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e3819bd20a42470d6dd0fe7fc1c121c92247bca104ce608e609b59bc7a77ee"}, + {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbb503bbbbab0c588ed3cd21975a1d0d4163b87e360fec17a792f7d8c4ff29f"}, + {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:336709883c15c050b9c55a63d6c7ff09be883dbc17805d2b063395dd9d9d0022"}, + {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ae57b4d8e3312d486e2498d42aed3ece7b51848336964e43abbf9671584e67f"}, + {file = "pydantic-1.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:dba49d52500c35cfec0b28aa8b3ea5c37c9df183ffc7210b10ff2a415c125c4a"}, + {file = "pydantic-1.10.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c66609e138c31cba607d8e2a7b6a5dc38979a06c900815495b2d90ce6ded35b4"}, + {file = "pydantic-1.10.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d986e115e0b39604b9eee3507987368ff8148222da213cd38c359f6f57b3b347"}, + {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:646b2b12df4295b4c3148850c85bff29ef6d0d9621a8d091e98094871a62e5c7"}, + {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282613a5969c47c83a8710cc8bfd1e70c9223feb76566f74683af889faadc0ea"}, + {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:466669501d08ad8eb3c4fecd991c5e793c4e0bbd62299d05111d4f827cded64f"}, + {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:13e86a19dca96373dcf3190fcb8797d40a6f12f154a244a8d1e8e03b8f280593"}, + {file = "pydantic-1.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:08b6ec0917c30861e3fe71a93be1648a2aa4f62f866142ba21670b24444d7fd8"}, + {file = "pydantic-1.10.14-py3-none-any.whl", hash = "sha256:8ee853cd12ac2ddbf0ecbac1c289f95882b2d4482258048079d13be700aa114c"}, + {file = "pydantic-1.10.14.tar.gz", hash = "sha256:46f17b832fe27de7850896f3afee50ea682220dd218f7e9c88d436788419dca6"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=2.0.0,<5.0.0", markers = "python_version < \"3.8\""} +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "2.5.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, + {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, +] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyserial" +version = "3.5" +description = "Python Serial Port Extension" +optional = false +python-versions = "*" +files = [ + {file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, + {file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, +] + +[package.extras] +cp2110 = ["hidapi"] + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-can" +version = "4.2.2" +description = "Controller Area Network interface module for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python-can-4.2.2.tar.gz", hash = "sha256:6ad50f4613289f3c4d276b6d2ac8901d776dcb929994cce93f55a69e858c595f"}, + {file = "python_can-4.2.2-py3-none-any.whl", hash = "sha256:7eea9b81b0ff908000a825db024313f622895bd578e8a17433e0474cd7d2da83"}, +] + +[package.dependencies] +msgpack = {version = ">=1.0.0,<1.1.0", markers = "platform_system != \"Windows\""} +packaging = "*" +pywin32 = {version = ">=305", markers = "platform_system == \"Windows\" and platform_python_implementation == \"CPython\""} +setuptools = "*" +typing-extensions = ">=3.10.0.0" +wrapt = ">=1.10,<2.0" + +[package.extras] +canalystii = ["canalystii (>=0.1.0)"] +canine = ["python-can-canine (>=0.2.2)"] +cantact = ["cantact (>=0.0.7)"] +cvector = ["python-can-cvector"] +gs-usb = ["gs-usb (>=0.2.1)"] +mf4 = ["asammdf (>=6.0.0)"] +neovi = ["filelock", "python-ics (>=2.12)"] +nixnet = ["nixnet (>=0.3.2)"] +pcan = ["uptime (>=3.0.1,<3.1.0)"] +remote = ["python-can-remote"] +seeedstudio = ["pyserial (>=3.0)"] +serial = ["pyserial (>=3.0,<4.0)"] +sontheim = ["python-can-sontheim (>=0.1.2)"] +viewer = ["windows-curses"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pyupgrade" +version = "3.3.2" +description = "A tool to automatically upgrade syntax for newer versions." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyupgrade-3.3.2-py2.py3-none-any.whl", hash = "sha256:c05b82c911934b3a638b29f48f48dc6e0ef6c57c55ec36f2b41ae9dbf6711b4b"}, + {file = "pyupgrade-3.3.2.tar.gz", hash = "sha256:bcfed63d38811809f179fd269dec246680b0aaa5bbe662b535826e5fa2275219"}, +] + +[package.dependencies] +tokenize-rt = ">=3.2.0" + +[[package]] +name = "pyusb" +version = "1.2.1" +description = "Python USB access module" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "pyusb-1.2.1-py3-none-any.whl", hash = "sha256:2b4c7cb86dbadf044dfb9d3a4ff69fd217013dbe78a792177a3feb172449ea36"}, + {file = "pyusb-1.2.1.tar.gz", hash = "sha256:a4cc7404a203144754164b8b40994e2849fde1cfff06b08492f12fff9d9de7b9"}, +] + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "restructuredtext-lint" +version = "1.4.0" +description = "reStructuredText linter" +optional = false +python-versions = "*" +files = [ + {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, +] + +[package.dependencies] +docutils = ">=0.11,<1.0" + +[[package]] +name = "rich" +version = "13.7.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "ruamel-yaml" +version = "0.18.6" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, + {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.8" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.6" +files = [ + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, + {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, + {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, +] + +[[package]] +name = "ruff" +version = "0.1.15" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"}, + {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"}, + {file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"}, + {file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"}, + {file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"}, + {file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"}, +] + +[[package]] +name = "safety" +version = "3.0.1" +description = "Checks installed dependencies for known vulnerabilities and licenses." +optional = false +python-versions = ">=3.7" +files = [ + {file = "safety-3.0.1-py3-none-any.whl", hash = "sha256:1ed058bc4bef132b974e58d7fcad020fb897cd255328016f8a5a194b94ca91d2"}, + {file = "safety-3.0.1.tar.gz", hash = "sha256:1f2000f03652f3a0bfc67f8fd1e98bc5723ccb76e15cb1bdd68545c3d803df01"}, +] + +[package.dependencies] +Authlib = ">=1.2.0" +Click = ">=8.0.2" +dparse = ">=0.6.4b0" +jinja2 = ">=3.1.0" +marshmallow = ">=3.15.0" +packaging = ">=21.0" +pydantic = ">=1.10.12,<2.0" +requests = "*" +rich = "*" +"ruamel.yaml" = ">=0.17.21" +safety-schemas = ">=0.0.1" +setuptools = ">=65.5.1" +typer = "*" +typing-extensions = ">=4.7.1" +urllib3 = ">=1.26.5" + +[package.extras] +github = ["pygithub (>=1.43.3)"] +gitlab = ["python-gitlab (>=1.3.0)"] +spdx = ["spdx-tools (>=0.8.2)"] + +[[package]] +name = "safety-schemas" +version = "0.0.2" +description = "Schemas for Safety tools" +optional = false +python-versions = ">=3.7" +files = [ + {file = "safety_schemas-0.0.2-py3-none-any.whl", hash = "sha256:277c077ce6e53221874a87c29515ffdd2f3773a6db4d035a9f67cc98db3b8c7f"}, + {file = "safety_schemas-0.0.2.tar.gz", hash = "sha256:7d1b040ec06480f05cff6b45ea7a93e09c8942df864fb0d01ddeb67c323cfa8c"}, +] + +[package.dependencies] +dparse = ">=0.6.4b0" +packaging = ">=21.0" +pydantic = "*" +ruamel-yaml = ">=0.17.21" +typing-extensions = ">=4.7.1" + +[[package]] +name = "setuptools" +version = "68.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, + {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "smmap" +version = "5.0.1" +description = "A pure Python implementation of a sliding window memory map manager" +optional = false +python-versions = ">=3.7" +files = [ + {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, + {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.4.1" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, + {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, +] + +[[package]] +name = "sphinx" +version = "4.3.2" +description = "Python documentation generator" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Sphinx-4.3.2-py3-none-any.whl", hash = "sha256:6a11ea5dd0bdb197f9c2abc2e0ce73e01340464feaece525e64036546d24c851"}, + {file = "Sphinx-4.3.2.tar.gz", hash = "sha256:0a8836751a68306b3fe97ecbe44db786f8479c3bf4b80e3a7f5c838657b4698c"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=1.3" +colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.18" +imagesize = "*" +Jinja2 = ">=2.3" +packaging = "*" +Pygments = ">=2.0" +requests = ">=2.5.0" +setuptools = "*" +snowballstemmer = ">=1.1" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.920)", "types-pkg-resources", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] + +[[package]] +name = "sphinx-autobuild" +version = "2021.3.14" +description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." +optional = false +python-versions = ">=3.6" +files = [ + {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, + {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, +] + +[package.dependencies] +colorama = "*" +livereload = "*" +sphinx = "*" + +[package.extras] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b2" +description = "A modern skeleton for Sphinx themes." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, + {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, +] + +[package.dependencies] +sphinx = ">=4.0" + +[package.extras] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinx-click" +version = "4.4.0" +description = "Sphinx extension that automatically documents click applications" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-click-4.4.0.tar.gz", hash = "sha256:cc67692bd28f482c7f01531c61b64e9d2f069bfcf3d24cbbb51d4a84a749fa48"}, + {file = "sphinx_click-4.4.0-py3-none-any.whl", hash = "sha256:2821c10a68fc9ee6ce7c92fad26540d8d8c8f45e6d7258f0e4fb7529ae8fab49"}, +] + +[package.dependencies] +click = ">=7.0" +docutils = "*" +sphinx = ">=2.0" + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.2" +description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, + {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.6" +files = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "stevedore" +version = "3.5.2" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.6" +files = [ + {file = "stevedore-3.5.2-py3-none-any.whl", hash = "sha256:fa2630e3d0ad3e22d4914aff2501445815b9a4467a6edc49387c667a38faf5bf"}, + {file = "stevedore-3.5.2.tar.gz", hash = "sha256:cf99f41fc0d5a4f185ca4d3d42b03be9011b0a1ec1a4ea1a282be1b4b306dcc2"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "tokenize-rt" +version = "5.0.0" +description = "A wrapper around the stdlib `tokenize` which roundtrips." +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenize_rt-5.0.0-py2.py3-none-any.whl", hash = "sha256:c67772c662c6b3dc65edf66808577968fb10badfc2042e3027196bed4daf9e5a"}, + {file = "tokenize_rt-5.0.0.tar.gz", hash = "sha256:3160bc0c3e8491312d0485171dea861fc160a240f5f5766b72a1165408d10740"}, +] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tornado" +version = "6.2" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.7" +files = [ + {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, + {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, + {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, + {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, + {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, +] + +[[package]] +name = "traitlets" +version = "5.9.0" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.7" +files = [ + {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, + {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] + +[[package]] +name = "typed-ast" +version = "1.5.5" +description = "a fork of Python 2 and 3 ast modules with type comment support" +optional = false +python-versions = ">=3.6" +files = [ + {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, + {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, + {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, + {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, + {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, + {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, + {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, + {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, + {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, + {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, + {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, + {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, + {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, + {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, + {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, +] + +[[package]] +name = "typeguard" +version = "2.13.3" +description = "Run-time type checker for Python" +optional = false +python-versions = ">=3.5.3" +files = [ + {file = "typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1"}, + {file = "typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4"}, +] + +[package.extras] +doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["mypy", "pytest", "typing-extensions"] + +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "uptime" +version = "3.0.1" +description = "Cross-platform uptime library" +optional = false +python-versions = "*" +files = [ + {file = "uptime-3.0.1.tar.gz", hash = "sha256:7c300254775b807ce46e3dcbcda30aa3b9a204b9c57a7ac1e79ee6dbe3942973"}, +] + +[[package]] +name = "urllib3" +version = "2.0.7" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.7" +files = [ + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.4.7" +description = "Virtual Python Environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +files = [ + {file = "virtualenv-20.4.7-py2.py3-none-any.whl", hash = "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76"}, + {file = "virtualenv-20.4.7.tar.gz", hash = "sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467"}, +] + +[package.dependencies] +appdirs = ">=1.4.3,<2" +distlib = ">=0.3.1,<1" +filelock = ">=3.0.0,<4" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "xonsh (>=0.9.16)"] + +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + +[[package]] +name = "xdoctest" +version = "1.1.3" +description = "A rewrite of the builtin doctest module" +optional = false +python-versions = ">=3.6" +files = [ + {file = "xdoctest-1.1.3-py3-none-any.whl", hash = "sha256:9360535bd1a971ffc216d9613898cedceb81d0fd024587cc3c03c74d14c00a31"}, + {file = "xdoctest-1.1.3.tar.gz", hash = "sha256:84e76a42a11a5926ff66d9d84c616bc101821099672550481ad96549cbdd02ae"}, +] + +[package.dependencies] +colorama = {version = "*", optional = true, markers = "platform_system == \"Windows\" and extra == \"colors\""} +Pygments = {version = "*", optional = true, markers = "python_version >= \"3.5.0\" and extra == \"colors\""} + +[package.extras] +all = ["IPython (>=7.10.0)", "IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=5.2.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=6.1.5)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "tomli (>=0.2.0)", "typing (>=3.7.4)"] +all-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "tomli (==0.2.0)", "typing (==3.7.4)"] +colors = ["Pygments", "Pygments", "colorama"] +jupyter = ["IPython", "IPython", "attrs", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "nbconvert"] +optional = ["IPython (>=7.10.0)", "IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=5.2.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=6.1.5)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "tomli (>=0.2.0)"] +optional-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"] +tests = ["pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "typing (>=3.7.4)"] +tests-binary = ["cmake", "cmake", "ninja", "ninja", "pybind11", "pybind11", "scikit-build", "scikit-build"] +tests-binary-strict = ["cmake (==3.21.2)", "cmake (==3.25.0)", "ninja (==1.10.2)", "ninja (==1.11.1)", "pybind11 (==2.10.3)", "pybind11 (==2.7.1)", "scikit-build (==0.11.1)", "scikit-build (==0.16.1)"] +tests-strict = ["pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "typing (==3.7.4)"] + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.7" +content-hash = "a3741ad0d4e88cb58029d364f412b9f731be5938bbdcb707c246b097100cd624" diff --git a/pyproject.toml b/pyproject.toml index e55452c..c2d8a10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,15 @@ classifiers = [ "Programming Language :: Python :: 3.12" ] build = "build_ext.py" +include = [ + { path = "pyxcp/cpp_ext/*.so", format = "wheel" }, + { path = "pyxcp/cpp_ext/*.pyd", format = "wheel" }, + { path = "pyxcp/daq_stim/*.so", format = "wheel" }, + { path = "pyxcp/daq_stim/*.pyd", format = "wheel" }, + { path = "pyxcp/recorder/*.so", format = "wheel" }, + { path = "pyxcp/recorder/*.pyd", format = "wheel" }, + { path = "pyxcp/*.exe", format = "wheel" }, +] [tool.poetry.dependencies] python = "^3.7" diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index 8ea9fe9..4b927b3 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -34,8 +34,8 @@ class McObject { m_ext(ext), m_length(length), m_data_type(data_type), - m_components(components), - m_type_index(-1) { + m_type_index(-1), + m_components(components) { if (data_type != "") { std::string dt_toupper; diff --git a/pyxcp/daq_stim/stim.cpp b/pyxcp/daq_stim/stim.cpp index b1dc6cd..1eeec71 100644 --- a/pyxcp/daq_stim/stim.cpp +++ b/pyxcp/daq_stim/stim.cpp @@ -1,6 +1,8 @@ +#if defined(_MSC_VER) #pragma comment(lib, "Winmm.lib") #pragma comment(lib, "Avrt.lib") +#endif #include "stim.hpp" diff --git a/pyxcp/daq_stim/stim.hpp b/pyxcp/daq_stim/stim.hpp index 66e68d1..9289cba 100644 --- a/pyxcp/daq_stim/stim.hpp +++ b/pyxcp/daq_stim/stim.hpp @@ -592,8 +592,8 @@ class Stim { std::set m_stim_lists{}; std::optional m_feed_function{ std::nullopt }; std::optional m_send_function{ std::nullopt }; - Scheduler m_scheduler{}; - bool m_daq_running{ false }; + // Scheduler m_scheduler{}; + // bool m_daq_running{ false }; }; #endif // __STIM_HPP diff --git a/pyxcp/daq_stim/stim_wrapper.cpp b/pyxcp/daq_stim/stim_wrapper.cpp index f56cc62..046b805 100644 --- a/pyxcp/daq_stim/stim_wrapper.cpp +++ b/pyxcp/daq_stim/stim_wrapper.cpp @@ -7,7 +7,9 @@ namespace py = pybind11; using namespace py::literals; +#if defined(_MSC_VER) #pragma warning(disable: 4251 4273) +#endif #include "stim.hpp" diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 017c364..d135d6f 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -332,7 +332,7 @@ struct Setter { } } - std::uint32_t set_timestamp(blob_t * buf, std::uint32_t timestamp) { + /*std::uint32_t*/void set_timestamp(blob_t * buf, std::uint32_t timestamp) { switch (m_ts_size) { case 0: break; @@ -467,10 +467,10 @@ struct MeasurementParameters { bool m_timestamps_supported; bool m_ts_fixed; bool m_prescaler_supported; + bool m_selectable_timestamps; double m_ts_scale_factor; std::uint8_t m_ts_size; std::uint16_t m_min_daq; - bool m_selectable_timestamps; std::vector m_daq_lists; }; @@ -495,12 +495,17 @@ class DaqListState { m_total_entries(total_entries), m_enable_timestamps(enable_timestamps), m_initial_offset(initial_offset), + m_next_odt(0), + m_current_idx(0), + m_timestamp0(0.0), + m_timestamp1(0.0), + m_state(state_t::IDLE), + m_buffer{}, m_flatten_odts(flatten_odts), m_getter(getter), - m_params(params), - m_state(state_t::IDLE) { - // std::cout << "DaqListState: " << daq_list_num << " : " << num_odts << " : " << total_entries << " : " << - // enable_timestamps << std::endl; + m_params(params) + { + m_buffer.resize(m_total_entries); } @@ -587,7 +592,7 @@ class DaqListState { ); } - m_buffer[m_current_idx++] = std::move(m_getter.reader(type_index, payload_data, offset)); + m_buffer[m_current_idx++] = m_getter.reader(type_index, payload_data, offset); offset += size; } } @@ -679,7 +684,7 @@ class UnfolderBase { void create_state_vars(const MeasurementParameters& params) noexcept { m_getter = Getter(requires_swap(params.m_byte_order), params.m_id_field_size, params.m_ts_size); - for (auto idx = 0; idx < params.m_daq_lists.size(); ++idx) { + for (std::size_t idx = 0; idx < params.m_daq_lists.size(); ++idx) { m_state.emplace_back(DaqListState( idx, params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), params.m_daq_lists[idx].get_enable_timestamps(), params.m_id_field_size, params.m_daq_lists[idx].get_flatten_odts(), From 9090e99ae8aa6d89c340467c90fed1109d4ceee9 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sat, 17 Feb 2024 20:55:25 +0200 Subject: [PATCH 27/99] Add float16 support --- pyxcp/cpp_ext/bin.hpp | 15 ++++ pyxcp/cpp_ext/daqlist.hpp | 50 ++++++++++++ pyxcp/cpp_ext/helper.hpp | 83 +++++++++++++++++++ pyxcp/cpp_ext/mcobject.hpp | 27 ++++++ pyxcp/recorder/unfolder.hpp | 159 +++++++++++++++++++++++++++++++----- pyxcp/recorder/wrap.cpp | 4 +- 6 files changed, 317 insertions(+), 21 deletions(-) diff --git a/pyxcp/cpp_ext/bin.hpp b/pyxcp/cpp_ext/bin.hpp index 36052d5..8a1fd6c 100644 --- a/pyxcp/cpp_ext/bin.hpp +++ b/pyxcp/cpp_ext/bin.hpp @@ -45,6 +45,21 @@ class Bin { return (m_size == other.m_size) && (m_residual_capacity == other.m_residual_capacity) && (m_entries == other.m_entries); } + std::string dumps() const { + std::stringstream ss; + + ss << to_binary(m_size); + ss << to_binary(m_residual_capacity); + + std::size_t entries_size = m_entries.size(); + ss << to_binary(entries_size); + for (const auto& entry : m_entries) { + ss << entry.dumps(); + } + + return ss.str(); + } + private: std::uint16_t m_size; diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index 1ec8603..ddc0da3 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -3,6 +3,7 @@ #define __DAQ_LIST_HPP #include "bin.hpp" + #include "helper.hpp" #include "mcobject.hpp" class DaqList { @@ -95,6 +96,55 @@ class DaqList { m_total_length = total_length; } + std::string dumps() const { + std::stringstream ss; + + ss << to_binary(m_name); + ss << to_binary(m_event_num); + ss << to_binary(m_stim); + ss << to_binary(m_enable_timestamps); + + ss << to_binary(m_odt_count); + ss << to_binary(m_total_entries); + ss << to_binary(m_total_length); + + std::size_t meas_size = m_measurements.size(); + ss << to_binary(meas_size); + for (const auto& mc_obj : m_measurements) { + ss << mc_obj.dumps(); + } + std::size_t meas_opt_size = m_measurements_opt.size(); + ss << to_binary(meas_opt_size); + for (const auto& mc_obj : m_measurements_opt) { + ss << mc_obj.dumps(); + } + std::size_t hname_size = m_header_names.size(); + ss << to_binary(hname_size); + for (const auto& hdr_obj : m_header_names) { + ss << to_binary(hdr_obj); + } + ///// + std::size_t odt_size = m_flatten_odts.size(); + ss << to_binary(odt_size); + for (const auto& odt : m_flatten_odts) { + ss << to_binary(odt.size()); + for (const auto& odt_entry : odt) { + const auto& [name, address, ext, size, type_index] = odt_entry; + ss << to_binary(name); + ss << to_binary(address); + ss << to_binary(ext); + ss << to_binary(size); + ss << to_binary(type_index); + } + } + return ss.str(); + } + + static void loads(std::string_view buffer) { + + } + + private: std::string m_name; diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 894cdb1..884b10f 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -5,6 +5,26 @@ #include #include +#if __cplusplus >= 202302L + #include + + #if defined(__STDCPP_BFLOAT16_T__) + #define HAS_BFLOAT16 (1) + #else + #define HAS_BFLOAT16 (0) + #endif + + #if defined(__STDCPP_FLOAT16_T__) + #define HAS_FLOAT16 (1) + #else + #define HAS_FLOAT16 (0) + #endif +#else + #define HAS_FLOAT16 (0) + #define HAS_BFLOAT16 (0) +#endif + + template constexpr void DBG_PRINTN(Args&&... args) noexcept { @@ -12,4 +32,67 @@ constexpr void DBG_PRINTN(Args&&... args) noexcept } +// NOTE: C++23 has std::byteswap() +constexpr auto _bswap(std::uint64_t v) noexcept { + return ((v & UINT64_C(0x0000'0000'0000'00FF)) << 56) | ((v & UINT64_C(0x0000'0000'0000'FF00)) << 40) | + ((v & UINT64_C(0x0000'0000'00FF'0000)) << 24) | ((v & UINT64_C(0x0000'0000'FF00'0000)) << 8) | + ((v & UINT64_C(0x0000'00FF'0000'0000)) >> 8) | ((v & UINT64_C(0x0000'FF00'0000'0000)) >> 24) | + ((v & UINT64_C(0x00FF'0000'0000'0000)) >> 40) | ((v & UINT64_C(0xFF00'0000'0000'0000)) >> 56); +} + +constexpr auto _bswap(std::uint32_t v) noexcept { + return ((v & UINT32_C(0x0000'00FF)) << 24) | ((v & UINT32_C(0x0000'FF00)) << 8) | ((v & UINT32_C(0x00FF'0000)) >> 8) | + ((v & UINT32_C(0xFF00'0000)) >> 24); +} + +constexpr auto _bswap(std::uint16_t v) noexcept { + return ((v & UINT16_C(0x00FF)) << 8) | ((v & UINT16_C(0xFF00)) >> 8); +} + + +template +inline std::string to_binary(const T& value) { + std::string result; + + auto ptr = reinterpret_cast(&value); + for (std::size_t idx=0; idx +inline std::string to_binary(const std::string& value) { + std::string result; + + auto ptr = reinterpret_cast(value.c_str()); + std::size_t length = std::size(value); + + // We are using Pascal strings as serialization format. + auto len_bin = to_binary(length); + std::copy(len_bin.begin(), len_bin.end(), std::back_inserter(result)); + for (std::size_t idx=0; idx +inline T from_binary(const std::string& buf, std::size_t offset) { + return *reinterpret_cast(&buf[offset]); +} + +template<> +inline std::string from_binary(const std::string& buf, std::size_t offset) { + auto length = from_binary(buf, offset); + std::string result(length, '\0'); + auto start = buf.cbegin() + offset + sizeof(std::size_t); + + std::copy(start, start + length, std::back_inserter(result)); + + return result; +} + #endif // __HELPER_HPP diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index 4b927b3..d924142 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -9,6 +9,8 @@ #include #include + #include "helper.hpp" + const std::map> TYPE_MAP = { { "U8", { 0, 1 }}, { "I8", { 1, 1 }}, @@ -20,6 +22,13 @@ const std::map> TYPE { "I64", { 7, 8 }}, { "F32", { 8, 4 }}, { "F64", { 9, 8 }}, +#if HAS_FLOAT16 + { "F16", { 10, 2 }}, +#endif +#if HAS_BFLOAT16 + { "BF16", { 11, 2 }}, +#endif + }; class McObject { @@ -116,6 +125,24 @@ class McObject { (std::equal(m_components.begin(), m_components.end(), other.m_components.begin(), other.m_components.end())); } + std::string dumps() const { + std::stringstream ss; + + ss << to_binary(m_name); + ss << to_binary(m_address); + ss << to_binary(m_ext); + ss << to_binary(m_length); + ss << to_binary(m_data_type); + ss << to_binary(m_type_index); + + std::size_t ccount = m_components.size(); + ss << to_binary(ccount); + for (const auto& obj : m_components) { + ss << obj.dumps(); + } + return ss.str(); + } + private: std::string m_name; diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index d135d6f..43dd442 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -11,6 +11,7 @@ #include #include "daqlist.hpp" +#include "helper.hpp" #include "mcobject.hpp" @@ -18,25 +19,6 @@ using measurement_value_t = std::variant>; using measurement_callback_t = std::function)>; - -// NOTE: C++23 has std::byteswap() -constexpr auto _bswap(std::uint64_t v) noexcept { - return ((v & UINT64_C(0x0000'0000'0000'00FF)) << 56) | ((v & UINT64_C(0x0000'0000'0000'FF00)) << 40) | - ((v & UINT64_C(0x0000'0000'00FF'0000)) << 24) | ((v & UINT64_C(0x0000'0000'FF00'0000)) << 8) | - ((v & UINT64_C(0x0000'00FF'0000'0000)) >> 8) | ((v & UINT64_C(0x0000'FF00'0000'0000)) >> 24) | - ((v & UINT64_C(0x00FF'0000'0000'0000)) >> 40) | ((v & UINT64_C(0xFF00'0000'0000'0000)) >> 56); -} - -constexpr auto _bswap(std::uint32_t v) noexcept { - return ((v & UINT32_C(0x0000'00FF)) << 24) | ((v & UINT32_C(0x0000'FF00)) << 8) | ((v & UINT32_C(0x00FF'0000)) >> 8) | - ((v & UINT32_C(0xFF00'0000)) >> 24); -} - -constexpr auto _bswap(std::uint16_t v) noexcept { - return ((v & UINT16_C(0x00FF)) << 8) | ((v & UINT16_C(0xFF00)) >> 8); -} - - template auto get_value(blob_t const * buf, std::uint32_t offset) -> Ty { return *reinterpret_cast(&buf[offset]); @@ -47,6 +29,25 @@ auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> Ty { return _bswap(get_value(buf, offset)); } +#if HAS_FLOAT16==1 +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> std::float16_t { + auto tmp = get_value(buf, offset); + + return *(reinterpret_cast(&tmp)); +} +#endif + +#if HAS_BFLOAT16==1 +template<> +auto get_value(blob_t const * buf, std::uint32_t offset) -> std::bfloat16_t { + auto tmp = get_value(buf, offset); + + return *(reinterpret_cast(&tmp)); +} +#endif + + template<> auto get_value(blob_t const * buf, std::uint32_t offset) -> float { auto tmp = get_value(buf, offset); @@ -61,6 +62,25 @@ auto get_value(blob_t const * buf, std::uint32_t offset) -> double { return *(reinterpret_cast(&tmp)); } +#if HAS_FLOAT16==1 +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::float16_t { + auto tmp = get_value_swapped(buf, offset); + + return *(reinterpret_cast(&tmp)); +} +#endif + +#if HAS_BFLOAT16==1 +template<> +auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::bfloat16_t { + auto tmp = get_value_swapped(buf, offset); + + return *(reinterpret_cast(&tmp)); +} +#endif + + template<> auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> float { auto tmp = get_value_swapped(buf, offset); @@ -158,6 +178,30 @@ void set_value_swapped(blob_t * buf, std::uint32_t offset, std::in set_value_swapped(buf, offset, static_cast(value)); } +#if HAS_FLOAT16==1 +template<> +void set_value(blob_t * buf, std::uint32_t offset, std::float16_t value) { + set_value(buf, offset, *reinterpret_cast(&value)); +} + +template<> +void set_value_swapped(blob_t * buf, std::uint32_t offset, std::float16_t value) { + set_value_swapped(buf, offset, *reinterpret_cast(&value)); +} +#endif + +#if HAS_BFLOAT16==1 +template<> +void set_value(blob_t * buf, std::uint32_t offset, std::bfloat16_t value) { + set_value(buf, offset, *reinterpret_cast(&value)); +} + +template<> +void set_value_swapped(blob_t * buf, std::uint32_t offset, std::bfloat16_t value) { + set_value_swapped(buf, offset, *reinterpret_cast(&value)); +} +#endif + template<> void set_value(blob_t * buf, std::uint32_t offset, float value) { set_value(buf, offset, *reinterpret_cast(&value)); @@ -198,6 +242,12 @@ struct Getter { uint64 = get_value_swapped; float_ = get_value_swapped; double_ = get_value_swapped; +#if HAS_FLOAT16==1 + float16 = get_value_swapped; +#endif +#if HAS_BFLOAT16==1 + bfloat16 = get_value_swapped; +#endif } else { int16 = get_value; int32 = get_value; @@ -207,6 +257,12 @@ struct Getter { uint64 = get_value; float_ = get_value; double_ = get_value; +#if HAS_FLOAT16==1 + float16 = get_value; +#endif +#if HAS_BFLOAT16==1 + bfloat16 = get_value; +#endif } } @@ -247,6 +303,14 @@ struct Getter { return float_(buf, offset); case 9: return double_(buf, offset); +#if HAS_FLOAT16==1 + case 10: + return float16(buf, offset); +#endif +#if HAS_BFLOAT16==1 + case 11: + return bfloat16(buf, offset); +#endif default: throw std::runtime_error("Unsupported data type: " + std::to_string(tp)); } @@ -299,6 +363,12 @@ struct Getter { std::function uint64; std::function float_; std::function double_; +#if HAS_FLOAT16==1 + std::function float16; +#endif +#if HAS_BFLOAT16==1 + std::function bfloat16; +#endif std::vector m_first_pids; std::map> m_odt_to_daq_map; }; @@ -320,6 +390,12 @@ struct Setter { uint64 = set_value_swapped; float_ = set_value_swapped; double_ = set_value_swapped; +#if HAS_FLOAT16==1 + float16 = set_value_swapped; +#endif +#if HAS_BFLOAT16==1 + bfloat16 = set_value_swapped; +#endif } else { int16 = set_value; int32 = set_value; @@ -329,10 +405,16 @@ struct Setter { uint64 = set_value; float_ = set_value; double_ = set_value; +#if HAS_FLOAT16==1 + float16 = set_value; +#endif +#if HAS_BFLOAT16==1 + bfloat16 = set_value; +#endif } } - /*std::uint32_t*/void set_timestamp(blob_t * buf, std::uint32_t timestamp) { + void set_timestamp(blob_t * buf, std::uint32_t timestamp) { switch (m_ts_size) { case 0: break; @@ -382,6 +464,16 @@ struct Setter { case 9: double_(buf, offset, static_cast(std::get(value))); break; +#if HAS_FLOAT16==1 + case 10: + float16(buf, offset, static_cast(std::get(value))); + break; +#endif +#if HAS_BFLOAT16==1 + case 11: + bfloat16(buf, offset, static_cast(std::get(value))); + break; +#endif default: throw std::runtime_error("Unsupported data type: " + std::to_string(tp)); } @@ -437,6 +529,12 @@ struct Setter { std::function uint64; std::function float_; std::function double_; +#if HAS_FLOAT16==1 + std::function float16; +#endif +#if HAS_BFLOAT16==1 + std::function bfloat16; +#endif std::vector m_first_pids; std::map> m_odt_to_daq_map; }; @@ -462,6 +560,27 @@ struct MeasurementParameters { m_daq_lists(daq_lists) { } + std::string dumps() const { + std::stringstream ss; + + ss << to_binary(m_byte_order); + ss << to_binary(m_id_field_size); + ss << to_binary(m_timestamps_supported); + ss << to_binary(m_ts_fixed); + ss << to_binary(m_prescaler_supported); + ss << to_binary(m_selectable_timestamps); + ss << to_binary(m_ts_scale_factor); + ss << to_binary(m_ts_size); + ss << to_binary(m_min_daq); + + std::size_t dl_count = m_daq_lists.size(); + ss << to_binary(dl_count); + for (const auto& daq_list : m_daq_lists) { + ss << daq_list.dumps(); + } + return ss.str(); + } + std::uint8_t m_byte_order; std::uint8_t m_id_field_size; bool m_timestamps_supported; diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index d87b9c3..8afe61e 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -46,7 +46,9 @@ PYBIND11_MODULE(rekorder, m) { py::class_(m, "_MeasurementParameters") .def(py::init< std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector &>( - )); + )) + .def("dumps", &MeasurementParameters::dumps) + ; py::class_(m, "DAQParser", py::dynamic_attr()) .def(py::init<>()) From c8a72e60ab276c7024070b5cc80bfad8ebba7857 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 21 Feb 2024 22:31:56 +0100 Subject: [PATCH 28/99] today() --- pyxcp/daq_stim/__init__.py | 10 +++++++--- pyxcp/examples/run_daq.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 2bf84db..678b462 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -25,11 +25,9 @@ "S4": 4, } - class DAQParser(_DAQParser): - def __init__(self, file_name: str, daq_lists: List[DaqList]): + def __init__(self, daq_lists: List[DaqList]): super().__init__() - self.file_name = file_name self.daq_lists = daq_lists self.setup_called = False self.log = get_application().log @@ -153,6 +151,12 @@ def stop(self): self.xcp_master.startStopSynch(0x00) +class DaqRecorder(DAQParser): + + def __init__(self, daq_lists: List[DaqList], file_name: str): + super().__init__(daq_lists) + self.file_name = file_name + class DaqToCsv(DAQParser): """Save a measurement as CSV files (one per DAQ-list).""" diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index e452c9a..4c60c56 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -121,7 +121,7 @@ ), ] -daq_parser = DaqToCsv(None, DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. +daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. with ap.run(policy=daq_parser) as x: x.connect() From 4ecfb5865bb025094b34ff605adead6e490ec17c Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 22 Feb 2024 14:27:39 +0100 Subject: [PATCH 29/99] Small fixes --- pyxcp/cpp_ext/daqlist.hpp | 2 +- pyxcp/daq_stim/stim.hpp | 2 +- pyxcp/recorder/unfolder.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index ddc0da3..e57e012 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -21,7 +21,7 @@ class DaqList { //std::cout << "DAQ-List: " << meas_name << " " << event_num << " " << stim << " " << enable_timestamps << std::endl; for (const auto& measurement : measurements) { auto const& [name, address, ext, dt_name] = measurement; - m_measurements.emplace_back(McObject(name, address, ext, 0, dt_name)); + m_measurements.emplace_back(McObject(name, address, static_cast(ext), 0, dt_name)); } } diff --git a/pyxcp/daq_stim/stim.hpp b/pyxcp/daq_stim/stim.hpp index 9289cba..d6675e6 100644 --- a/pyxcp/daq_stim/stim.hpp +++ b/pyxcp/daq_stim/stim.hpp @@ -23,7 +23,7 @@ #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64) ) #include - #include #endif namespace py = pybind11; diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 43dd442..bebf936 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -803,7 +803,7 @@ class UnfolderBase { void create_state_vars(const MeasurementParameters& params) noexcept { m_getter = Getter(requires_swap(params.m_byte_order), params.m_id_field_size, params.m_ts_size); - for (std::size_t idx = 0; idx < params.m_daq_lists.size(); ++idx) { + for (std::uint16_t idx = 0; idx < params.m_daq_lists.size(); ++idx) { m_state.emplace_back(DaqListState( idx, params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), params.m_daq_lists[idx].get_enable_timestamps(), params.m_id_field_size, params.m_daq_lists[idx].get_flatten_odts(), From ec8b9524a9a1e5c65d6fd3d60a8e69409e83c8cb Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 3 Mar 2024 15:08:32 +0100 Subject: [PATCH 30/99] today() --- build_ext.py | 24 ++- poetry.lock | 82 +++---- pyxcp/aml/EtasCANMonitoring.a2l | 165 +++++++------- pyxcp/aml/XCP_Common.aml | 1 - pyxcp/aml/XCPonUSB.aml | 2 +- pyxcp/aml/ifdata_CAN.a2l | 1 - pyxcp/aml/ifdata_Eth.a2l | 1 - pyxcp/aml/ifdata_Flx.a2l | 1 - pyxcp/aml/ifdata_SxI.a2l | 1 - pyxcp/aml/ifdata_USB.a2l | 1 - pyxcp/cmdline.py | 4 +- pyxcp/cpp_ext/bin.hpp | 8 + pyxcp/cpp_ext/daqlist.hpp | 4 +- pyxcp/cpp_ext/helper.hpp | 16 -- pyxcp/daq_stim/__init__.py | 43 ++-- pyxcp/daq_stim/optimize/binpacking.py | 2 +- pyxcp/daq_stim/scheduler.cpp | 1 - pyxcp/examples/conf_can.json | 36 ++-- pyxcp/examples/conf_can_vector.json | 18 +- pyxcp/examples/conf_eth.json | 12 +- pyxcp/examples/conf_nixnet.json | 36 ++-- pyxcp/examples/conf_sxi.json | 14 +- pyxcp/examples/run_daq.py | 10 +- pyxcp/examples/xcp_policy.py | 2 +- pyxcp/examples/xcphello.py | 4 +- pyxcp/examples/xcphello_recorder.py | 4 +- pyxcp/recorder/__init__.py | 22 +- pyxcp/recorder/reader.hpp | 20 +- pyxcp/recorder/reco.py | 2 +- pyxcp/recorder/rekorder.hpp | 32 +-- pyxcp/recorder/unfolder.hpp | 300 ++++++++++++++++++++++++-- pyxcp/recorder/wrap.cpp | 76 +++++-- pyxcp/recorder/writer.hpp | 19 +- pyxcp/scripts/xcp_id_scanner.py | 1 - pyxcp/scripts/xcp_info.py | 3 +- pyxcp/transport/base.py | 3 +- pyxcp/transport/cxx_ext/setup.py | 4 +- pyxcp/transport/eth.py | 6 +- pyxcp/utils.py | 4 +- setup.py | 2 +- 40 files changed, 670 insertions(+), 317 deletions(-) diff --git a/build_ext.py b/build_ext.py index 4bacb37..2373d72 100644 --- a/build_ext.py +++ b/build_ext.py @@ -1,27 +1,27 @@ import os -from pathlib import Path import platform import subprocess # nosec import sys +from pathlib import Path from typing import Any, Dict import setuptools.command.build_py import setuptools.command.develop +from pybind11.setup_helpers import ( + ParallelCompile, + Pybind11Extension, + build_ext, + naive_recompile, +) from setuptools import Distribution + ROOT_DIRPATH = Path(".") if sys.platform == "darwin": os.environ["CC"] = "clang++" os.environ["CXX"] = "clang++" -from pybind11.setup_helpers import ( - ParallelCompile, - Pybind11Extension, - naive_recompile, -build_ext, -) - ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() PYB11_INCLUDE_DIRS = subprocess.check_output(["pybind11-config", "--includes"]) # nosec EXT_NAMES = ["pyxcp.recorder.rekorder", "pyxcp.cpp_ext.cpp_ext", "pyxcp.daq_stim.stim"] @@ -53,6 +53,7 @@ ), ] + class AsamKeyDllAutogen(setuptools.Command): """Custom command to compile `asamkeydll.exe`.""" @@ -74,10 +75,9 @@ def run(self): gccCmd = ["gcc", "-m32", "-O3", "-Wall"] self.announce(" ".join(gccCmd + self.arguments)) try: - print("running: ", gccCmd + self.arguments) subprocess.check_call(gccCmd + self.arguments) # nosec except Exception as e: - print(f"Building pyxcp/asamkeydll.exe failed: '{str(e)}'") + print(f"Building pyxcp/asamkeydll.exe failed: {e!r}") else: print("Successfully build pyxcp/asamkeydll.exe") @@ -103,6 +103,7 @@ def build(setup_kwargs: Dict[str, Any]) -> None: } ) + def invoke_command(distribution: Distribution, name: str) -> None: cmd = distribution.cmdclass.get(name)(distribution) print(f"Building target {name!r}...") @@ -110,6 +111,7 @@ def invoke_command(distribution: Distribution, name: str) -> None: cmd.ensure_finalized() cmd.run() + ### if __name__ == "__main__": distribution = Distribution( @@ -124,4 +126,4 @@ def invoke_command(distribution: Distribution, name: str) -> None: } ) invoke_command(distribution, "CXX_extensions") - invoke_command(distribution, "asam_key_dll") \ No newline at end of file + invoke_command(distribution, "asam_key_dll") diff --git a/poetry.lock b/poetry.lock index 137c8a0..0d28ca0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -494,43 +494,43 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "42.0.2" +version = "42.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, - {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, - {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, - {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, - {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, - {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, - {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, - {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, ] [package.dependencies] @@ -731,13 +731,13 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.41" +version = "3.1.42" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.41-py3-none-any.whl", hash = "sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c"}, - {file = "GitPython-3.1.41.tar.gz", hash = "sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048"}, + {file = "GitPython-3.1.42-py3-none-any.whl", hash = "sha256:1bf9cd7c9e7255f77778ea54359e54ac22a72a5b51288c457c881057b7bb9ecd"}, + {file = "GitPython-3.1.42.tar.gz", hash = "sha256:2d99869e0fef71a73cbd242528105af1d6c1b108c60dfabd994bf292f76c3ceb"}, ] [package.dependencies] @@ -745,7 +745,7 @@ gitdb = ">=4.0.1,<5" typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} [package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "sumtypes"] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar"] [[package]] name = "identify" @@ -1756,13 +1756,13 @@ docutils = ">=0.11,<1.0" [[package]] name = "rich" -version = "13.7.0" +version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [package.dependencies] diff --git a/pyxcp/aml/EtasCANMonitoring.a2l b/pyxcp/aml/EtasCANMonitoring.a2l index 4f33787..6852a20 100644 --- a/pyxcp/aml/EtasCANMonitoring.a2l +++ b/pyxcp/aml/EtasCANMonitoring.a2l @@ -1,83 +1,82 @@ -ASAP2_VERSION 1 30 -/begin PROJECT - aProjectName - "description of project" - - /begin HEADER - "project" - VERSION "1.0" - PROJECT_NO "1.0" - /end HEADER - - /begin MODULE - aModuleName - "description of module" - - /begin MOD_PAR - "" - /end MOD_PAR - - /begin IF_DATA CAN_MONITORING - /begin TP_BLOB - 500 - /end TP_BLOB - /end IF_DATA - - /begin MEASUREMENT - aMeasurementName - "description of measurement" - ULONG - aConversionName - 0 - 0.0 - 0 - 1000 - /begin IF_DATA CAN_MONITORING - /begin KP_BLOB - 0x0 32 - /end KP_BLOB - /end IF_DATA - FORMAT "" - BYTE_ORDER MSB_LAST - BIT_MASK 0xFFFFFFFF - /end MEASUREMENT - - /begin COMPU_METHOD - aConversionName - "description of conversion" - RAT_FUNC - "%f5.2" - "" - COEFFS 0 1.0 0.0 0 0 1 - /end COMPU_METHOD - - - - - - - - - /begin FRAME - aFrameName - "description of frame" - 0 - 0 - /begin IF_DATA CAN_MONITORING - QP_BLOB 0x0200 0 8 - /end IF_DATA - FRAME_MEASUREMENT aMeasurementName - /end FRAME - - /begin FUNCTION - aFunctionName - "description of function" - /begin OUT_MEASUREMENT - aMeasurementName - /end OUT_MEASUREMENT - /end FUNCTION - - /end MODULE - -/end PROJECT - +ASAP2_VERSION 1 30 +/begin PROJECT + aProjectName + "description of project" + + /begin HEADER + "project" + VERSION "1.0" + PROJECT_NO "1.0" + /end HEADER + + /begin MODULE + aModuleName + "description of module" + + /begin MOD_PAR + "" + /end MOD_PAR + + /begin IF_DATA CAN_MONITORING + /begin TP_BLOB + 500 + /end TP_BLOB + /end IF_DATA + + /begin MEASUREMENT + aMeasurementName + "description of measurement" + ULONG + aConversionName + 0 + 0.0 + 0 + 1000 + /begin IF_DATA CAN_MONITORING + /begin KP_BLOB + 0x0 32 + /end KP_BLOB + /end IF_DATA + FORMAT "" + BYTE_ORDER MSB_LAST + BIT_MASK 0xFFFFFFFF + /end MEASUREMENT + + /begin COMPU_METHOD + aConversionName + "description of conversion" + RAT_FUNC + "%f5.2" + "" + COEFFS 0 1.0 0.0 0 0 1 + /end COMPU_METHOD + + + + + + + + + /begin FRAME + aFrameName + "description of frame" + 0 + 0 + /begin IF_DATA CAN_MONITORING + QP_BLOB 0x0200 0 8 + /end IF_DATA + FRAME_MEASUREMENT aMeasurementName + /end FRAME + + /begin FUNCTION + aFunctionName + "description of function" + /begin OUT_MEASUREMENT + aMeasurementName + /end OUT_MEASUREMENT + /end FUNCTION + + /end MODULE + +/end PROJECT diff --git a/pyxcp/aml/XCP_Common.aml b/pyxcp/aml/XCP_Common.aml index 01eac0e..879198a 100644 --- a/pyxcp/aml/XCP_Common.aml +++ b/pyxcp/aml/XCP_Common.aml @@ -406,4 +406,3 @@ taggedstruct Common_Parameters { block "PGM" struct Pgm; block "DAQ_EVENT" taggedunion Daq_Event; }; /******************** end of Common Parameters *****************************/ - diff --git a/pyxcp/aml/XCPonUSB.aml b/pyxcp/aml/XCPonUSB.aml index cdcf6ec..e5470ce 100644 --- a/pyxcp/aml/XCPonUSB.aml +++ b/pyxcp/aml/XCPonUSB.aml @@ -103,4 +103,4 @@ struct USB_Parameters { }; /* end of optional */ }; /************************* end of USB ***********************************/ -/end A2ML \ No newline at end of file +/end A2ML diff --git a/pyxcp/aml/ifdata_CAN.a2l b/pyxcp/aml/ifdata_CAN.a2l index fd0fd27..55553cb 100644 --- a/pyxcp/aml/ifdata_CAN.a2l +++ b/pyxcp/aml/ifdata_CAN.a2l @@ -18,4 +18,3 @@ begin XCP_ON_CAN FIXED 0x330 /end DAQ_LIST_CAN_ID /end XCP_ON_CAN - diff --git a/pyxcp/aml/ifdata_Eth.a2l b/pyxcp/aml/ifdata_Eth.a2l index d9a0415..98e3219 100644 --- a/pyxcp/aml/ifdata_Eth.a2l +++ b/pyxcp/aml/ifdata_Eth.a2l @@ -9,4 +9,3 @@ 0x5555 /* PORT */ "127.0.0.1" /* ADDRESS */ /end XCP_ON_UDP_IP - diff --git a/pyxcp/aml/ifdata_Flx.a2l b/pyxcp/aml/ifdata_Flx.a2l index a809e1a..a702ec4 100644 --- a/pyxcp/aml/ifdata_Flx.a2l +++ b/pyxcp/aml/ifdata_Flx.a2l @@ -92,4 +92,3 @@ STIM VARIABLE /end XCP_PACKET /end POOL_BUFFER /end XCP_ON_FLX - diff --git a/pyxcp/aml/ifdata_SxI.a2l b/pyxcp/aml/ifdata_SxI.a2l index 329dfda..80a1145 100644 --- a/pyxcp/aml/ifdata_SxI.a2l +++ b/pyxcp/aml/ifdata_SxI.a2l @@ -11,4 +11,3 @@ HEADER_LEN_CTR_WORD NO_CHECKSUM /end XCP_ON_SxI - diff --git a/pyxcp/aml/ifdata_USB.a2l b/pyxcp/aml/ifdata_USB.a2l index fa0eb25..2d5b5f0 100644 --- a/pyxcp/aml/ifdata_USB.a2l +++ b/pyxcp/aml/ifdata_USB.a2l @@ -79,4 +79,3 @@ FIXED_OUT 0x02 /* uses Endpoint 2 OUT. */ /end DAQ_LIST_USB_ENDPOINT /end XCP_ON_USB - diff --git a/pyxcp/cmdline.py b/pyxcp/cmdline.py index 27ddd4c..875874f 100644 --- a/pyxcp/cmdline.py +++ b/pyxcp/cmdline.py @@ -15,7 +15,7 @@ class FakeParser: def __getattr__(self, key): if key == "add_argument": - warnings.warn("Argument parser extension is currently not supported.", DeprecationWarning) + warnings.warn("Argument parser extension is currently not supported.", DeprecationWarning, 2) return lambda *args, **kws: None @@ -23,7 +23,7 @@ class ArgumentParser: def __init__(self, callout=None, *args, **kws): self._parser = FakeParser() if callout is not None: - warnings.warn("callout argument is not supported anymore", DeprecationWarning) + warnings.warn("callout argument is not supported anymore", DeprecationWarning, 2) def run(self, policy=None): application = create_application() diff --git a/pyxcp/cpp_ext/bin.hpp b/pyxcp/cpp_ext/bin.hpp index 8a1fd6c..dfdee91 100644 --- a/pyxcp/cpp_ext/bin.hpp +++ b/pyxcp/cpp_ext/bin.hpp @@ -17,10 +17,18 @@ class Bin { Bin(std::uint16_t size) : m_size(size), m_residual_capacity(size) { } + Bin(std::uint16_t size, uint16_t residual_capacity, const std::vector& entries) : + m_size(size), m_residual_capacity(residual_capacity), m_entries(entries) { + } + void append(const McObject& bin) { m_entries.emplace_back(bin); } + void set_entries(std::vector&& entries) { + m_entries = std::move(entries); + } + std::uint16_t get_size() const { return m_size; } diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index e57e012..e20afe6 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -6,12 +6,12 @@ #include "helper.hpp" #include "mcobject.hpp" +using flatten_odts_t = std::vector>>; + class DaqList { public: using daq_list_initialzer_t = std::tuple; - using flatten_odts_t = - std::vector>>; DaqList( std::string_view meas_name, std::uint16_t event_num, bool stim, bool enable_timestamps, diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 884b10f..95b2d9c 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -79,20 +79,4 @@ inline std::string to_binary(const std::string& value) { return result; } -template -inline T from_binary(const std::string& buf, std::size_t offset) { - return *reinterpret_cast(&buf[offset]); -} - -template<> -inline std::string from_binary(const std::string& buf, std::size_t offset) { - auto length = from_binary(buf, offset); - std::string result(length, '\0'); - auto start = buf.cbegin() + offset + sizeof(std::size_t); - - std::copy(start, start + length, std::back_inserter(result)); - - return result; -} - #endif // __HELPER_HPP diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 678b462..5e24dc2 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -5,10 +5,11 @@ from pyxcp import types from pyxcp.config import get_application -from pyxcp.cpp_ext import DaqList # , StimList +from pyxcp.cpp_ext import DaqList from pyxcp.daq_stim.optimize import make_continuous_blocks from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing -from pyxcp.recorder import DAQParser as _DAQParser +from pyxcp.recorder import DaqOnlinePolicy as _DaqOnlinePolicy +from pyxcp.recorder import DaqRecorderPolicy as _DaqRecorderPolicy from pyxcp.recorder import MeasurementParameters @@ -25,9 +26,10 @@ "S4": 4, } -class DAQParser(_DAQParser): + +class DaqProcessor: def __init__(self, daq_lists: List[DaqList]): - super().__init__() + # super().__init__() self.daq_lists = daq_lists self.setup_called = False self.log = get_application().log @@ -87,7 +89,7 @@ def setup(self, write_multiple: bool = True): ttt = make_continuous_blocks(daq_list.measurements, max_payload_size, max_payload_size_first) daq_list.measurements_opt = first_fit_decreasing(ttt, max_payload_size, max_payload_size_first) byte_order = 0 if self.xcp_master.slaveProperties.byteOrder == "INTEL" else 1 - measurement_params = MeasurementParameters( + self.measurement_params = MeasurementParameters( byte_order, header_len, self.supports_timestampes, @@ -99,8 +101,7 @@ def setup(self, write_multiple: bool = True): self.min_daq, self.daq_lists, ) - self.set_parameters(measurement_params) - + self.set_parameters(self.measurement_params) self.first_pids = [] daq_count = len(self.daq_lists) self.xcp_master.freeDaq() @@ -151,16 +152,34 @@ def stop(self): self.xcp_master.startStopSynch(0x00) -class DaqRecorder(DAQParser): +class DaqRecorder(DaqProcessor, _DaqRecorderPolicy): - def __init__(self, daq_lists: List[DaqList], file_name: str): - super().__init__(daq_lists) + def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 10, chunk_size: int = 1): + DaqProcessor.__init__(self, daq_lists) + _DaqRecorderPolicy.__init__(self) self.file_name = file_name + self.prealloc = prealloc + self.chunk_size = chunk_size + + def initialize(self): + metadata = self.measurement_params.dumps() + print(metadata) + _DaqRecorderPolicy.create_writer(self, self.file_name, self.prealloc, self.chunk_size, metadata) + print("After initialization") + _DaqRecorderPolicy.initialize(self) + + def finalize(self): + _DaqRecorderPolicy.finalize(self) + + +# DaqRecorder +# DaqOnline + -class DaqToCsv(DAQParser): +class DaqToCsv(DaqProcessor, _DaqOnlinePolicy): """Save a measurement as CSV files (one per DAQ-list).""" - def Initialize(self): + def initialize(self): self.log.debug("DaqCsv::Initialize()") self.files = {} for num, daq_list in enumerate(self.daq_lists): diff --git a/pyxcp/daq_stim/optimize/binpacking.py b/pyxcp/daq_stim/optimize/binpacking.py index 6dea443..98c4476 100644 --- a/pyxcp/daq_stim/optimize/binpacking.py +++ b/pyxcp/daq_stim/optimize/binpacking.py @@ -27,7 +27,7 @@ def first_fit_decreasing(items, bin_size: int, initial_bin_size: Optional[int] = bins = [Bin(size=initial_bin_size)] # Initial bin for item in sorted(items, key=lambda x: x.length, reverse=True): if item.length > bin_size: - raise ValueError(f"Item '{item}' is too large to fit in a {bin_size} byte sized bin.") + raise ValueError(f"Item {item!r} is too large to fit in a {bin_size} byte sized bin.") for bin in bins: if bin.residual_capacity >= item.length: bin.append(item) diff --git a/pyxcp/daq_stim/scheduler.cpp b/pyxcp/daq_stim/scheduler.cpp index ee0fd80..439d701 100644 --- a/pyxcp/daq_stim/scheduler.cpp +++ b/pyxcp/daq_stim/scheduler.cpp @@ -26,4 +26,3 @@ void mul4_vectorized(float* ptr) { _mm_storeu_ps(ptr, f); } #endif - diff --git a/pyxcp/examples/conf_can.json b/pyxcp/examples/conf_can.json index 2b58798..af0c241 100644 --- a/pyxcp/examples/conf_can.json +++ b/pyxcp/examples/conf_can.json @@ -1,20 +1,20 @@ { - "TRANSPORT": "CAN", - "CAN_DRIVER": "Kvaser", - "CAN_USE_DEFAULT_LISTENER": true, - "CHANNEL": 0, - "ACCEPT_VIRTUAL": true, - "BAUDRATE_PRESET": true, - "CAN_ID_MASTER": 257, - "CAN_ID_SLAVE": 258, - "CAN_ID_BROADCAST": 256, - "MAX_DLC_REQUIRED": false, - "BITRATE": 250000, - "BTL_CYCLES": 16, - "SAMPLE_RATE": 1, - "SAMPLE_POINT": 87.5, - "SJW": 2, - "TSEG1": 5, - "TSEG2": 2, - "CREATE_DAQ_TIMESTAMPS": false + "TRANSPORT": "CAN", + "CAN_DRIVER": "Kvaser", + "CAN_USE_DEFAULT_LISTENER": true, + "CHANNEL": 0, + "ACCEPT_VIRTUAL": true, + "BAUDRATE_PRESET": true, + "CAN_ID_MASTER": 257, + "CAN_ID_SLAVE": 258, + "CAN_ID_BROADCAST": 256, + "MAX_DLC_REQUIRED": false, + "BITRATE": 250000, + "BTL_CYCLES": 16, + "SAMPLE_RATE": 1, + "SAMPLE_POINT": 87.5, + "SJW": 2, + "TSEG1": 5, + "TSEG2": 2, + "CREATE_DAQ_TIMESTAMPS": false } diff --git a/pyxcp/examples/conf_can_vector.json b/pyxcp/examples/conf_can_vector.json index 6c19a8b..4eda476 100644 --- a/pyxcp/examples/conf_can_vector.json +++ b/pyxcp/examples/conf_can_vector.json @@ -1,11 +1,11 @@ { - "TRANSPORT": "CAN", - "CAN_DRIVER": "Vector", - "CAN_USE_DEFAULT_LISTENER": true, - "CHANNEL": "1", - "CAN_ID_MASTER": 2, - "CAN_ID_SLAVE": 1, - "CAN_ID_BROADCAST": 256, - "MAX_DLC_REQUIRED": false, - "CREATE_DAQ_TIMESTAMPS": false + "TRANSPORT": "CAN", + "CAN_DRIVER": "Vector", + "CAN_USE_DEFAULT_LISTENER": true, + "CHANNEL": "1", + "CAN_ID_MASTER": 2, + "CAN_ID_SLAVE": 1, + "CAN_ID_BROADCAST": 256, + "MAX_DLC_REQUIRED": false, + "CREATE_DAQ_TIMESTAMPS": false } diff --git a/pyxcp/examples/conf_eth.json b/pyxcp/examples/conf_eth.json index 8a60025..e9b235c 100644 --- a/pyxcp/examples/conf_eth.json +++ b/pyxcp/examples/conf_eth.json @@ -1,8 +1,8 @@ { - "TRANSPORT": "ETH", - "HOST": "localhost", - "PORT": 5555, - "PROTOCOL": "TCP", - "IPV6": false, - "CREATE_DAQ_TIMESTAMPS": false + "TRANSPORT": "ETH", + "HOST": "localhost", + "PORT": 5555, + "PROTOCOL": "TCP", + "IPV6": false, + "CREATE_DAQ_TIMESTAMPS": false } diff --git a/pyxcp/examples/conf_nixnet.json b/pyxcp/examples/conf_nixnet.json index 7223d61..a4fbed4 100644 --- a/pyxcp/examples/conf_nixnet.json +++ b/pyxcp/examples/conf_nixnet.json @@ -1,20 +1,20 @@ { - "TRANSPORT": "CAN", - "CAN_DRIVER": "NiXnet", - "CAN_USE_DEFAULT_LISTENER": true, - "CHANNEL": "CAN4", - "ACCEPT_VIRTUAL": true, - "BAUDRATE_PRESET": true, - "CAN_ID_MASTER": 1911, - "CAN_ID_SLAVE": 819, - "CAN_ID_BROADCAST": 256, - "MAX_DLC_REQUIRED": false, - "BITRATE": 500000, - "BTL_CYCLES": 16, - "SAMPLE_RATE": 1, - "SAMPLE_POINT": 87.5, - "SJW": 2, - "TSEG1": 5, - "TSEG2": 2, - "CREATE_DAQ_TIMESTAMPS": false + "TRANSPORT": "CAN", + "CAN_DRIVER": "NiXnet", + "CAN_USE_DEFAULT_LISTENER": true, + "CHANNEL": "CAN4", + "ACCEPT_VIRTUAL": true, + "BAUDRATE_PRESET": true, + "CAN_ID_MASTER": 1911, + "CAN_ID_SLAVE": 819, + "CAN_ID_BROADCAST": 256, + "MAX_DLC_REQUIRED": false, + "BITRATE": 500000, + "BTL_CYCLES": 16, + "SAMPLE_RATE": 1, + "SAMPLE_POINT": 87.5, + "SJW": 2, + "TSEG1": 5, + "TSEG2": 2, + "CREATE_DAQ_TIMESTAMPS": false } diff --git a/pyxcp/examples/conf_sxi.json b/pyxcp/examples/conf_sxi.json index 2c94135..29776af 100644 --- a/pyxcp/examples/conf_sxi.json +++ b/pyxcp/examples/conf_sxi.json @@ -1,9 +1,9 @@ { - "TRANSPORT": "SXI", - "PORT": "COM10", - "BITRATE": 38400, - "BYTESIZE": 8, - "PARITY": "N", - "STOPBITS": 1, - "CREATE_DAQ_TIMESTAMPS": false + "TRANSPORT": "SXI", + "PORT": "COM10", + "BITRATE": 38400, + "BYTESIZE": 8, + "PARITY": "N", + "STOPBITS": 1, + "CREATE_DAQ_TIMESTAMPS": false } diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 4c60c56..fb4cacf 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -3,15 +3,15 @@ import time from pyxcp.cmdline import ArgumentParser -from pyxcp.daq_stim import DaqList, DaqToCsv +from pyxcp.daq_stim import DaqList, DaqRecorder, DaqToCsv # noqa: F401 # RECORDER_FILE_NAME = "daq_test" ap = ArgumentParser(description="DAQ test") -XCP_LITE = True - +# XCP_LITE = True +XCP_LITE = False # Completly random configurations, only for illustrative purposes. # @@ -121,7 +121,9 @@ ), ] -daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. +# daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. + +daq_parser = DaqRecorder(DAQ_LISTS, "run_daq") with ap.run(policy=daq_parser) as x: x.connect() diff --git a/pyxcp/examples/xcp_policy.py b/pyxcp/examples/xcp_policy.py index edcb497..4c939f6 100644 --- a/pyxcp/examples/xcp_policy.py +++ b/pyxcp/examples/xcp_policy.py @@ -24,7 +24,7 @@ identifier = x.identifier(0x01) print("\nSlave Properties:") print("=================") - print(f"ID: '{identifier}'") + print(f"ID: {identifier!r}") pprint(x.slaveProperties) x.disconnect() diff --git a/pyxcp/examples/xcphello.py b/pyxcp/examples/xcphello.py index 8b49c5a..9c02ae2 100644 --- a/pyxcp/examples/xcphello.py +++ b/pyxcp/examples/xcphello.py @@ -33,7 +33,7 @@ def callout(master, args): identifier = x.identifier(0x01) print("\nSlave Properties:") print("=================") - print(f"ID: '{identifier}'") + print(f"ID: {identifier!r}") pprint(x.slaveProperties) cps = x.getCurrentProtectionStatus() print("\nProtection Status") @@ -54,7 +54,7 @@ def callout(master, args): dq = "DAQ" if evt.daqEventProperties.daq else "" st = "STIM" if evt.daqEventProperties.stim else "" dq_st = dq + " " + st - print(f' [{idx:04}] "{name:s}"') + print(f" [{idx:04}] {name:r}") print(f" dir: {dq_st}") print(f" packed: {evt.daqEventProperties.packed}") PFX_CONS = "CONSISTENCY_" diff --git a/pyxcp/examples/xcphello_recorder.py b/pyxcp/examples/xcphello_recorder.py index 647f7bf..f7be7db 100644 --- a/pyxcp/examples/xcphello_recorder.py +++ b/pyxcp/examples/xcphello_recorder.py @@ -39,7 +39,7 @@ def callout(master, args): identifier = x.identifier(0x01) print("\nSlave Properties:") print("=================") - print(f"ID: '{identifier}'") + print(f"ID: {identifier!r}") pprint(x.slaveProperties) cps = x.getCurrentProtectionStatus() print("\nProtection Status") @@ -60,7 +60,7 @@ def callout(master, args): dq = "DAQ" if evt.daqEventProperties.daq else "" st = "STIM" if evt.daqEventProperties.stim else "" dq_st = dq + " " + st - print(f' [{idx:04}] "{name:s}"') + print(f" [{idx:04}] {name:r}") print(f" dir: {dq_st}") print(f" packed: {evt.daqEventProperties.packed}") PFX_CONS = "CONSISTENCY_" diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 95485a3..f7ff5de 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -14,17 +14,22 @@ else: HAS_PANDAS = True -import pyxcp.recorder.rekorder as rec - - -MeasurementParameters = rec._MeasurementParameters -DAQParser = rec.DAQParser +from pyxcp.recorder.rekorder import ( # noqa: F401 + DaqOnlinePolicy, + DaqRecorderPolicy, + Deserializer, + MeasurementParameters, + _PyXcpLogFileReader, + _PyXcpLogFileWriter, +) @dataclass class XcpLogFileHeader: """ """ + version: int + options: int num_containers: int record_count: int size_uncompressed: int @@ -39,11 +44,14 @@ class XcpLogFileReader: """ """ def __init__(self, file_name): - self._reader = rec._PyXcpLogFileReader(file_name) + self._reader = _PyXcpLogFileReader(file_name) def get_header(self): return XcpLogFileHeader(*self._reader.get_header_as_tuple()) + def get_metadata(self): + return self._reader.get_metadata() + def __iter__(self): while True: frames = self._reader.next_block() @@ -70,7 +78,7 @@ class XcpLogFileWriter: """ """ def __init__(self, file_name: str, prealloc=10, chunk_size=1): - self._writer = rec._PyXcpLogFileWriter(file_name, prealloc, chunk_size) + self._writer = _PyXcpLogFileWriter(file_name, prealloc, chunk_size) self._finalized = False def __del__(self): diff --git a/pyxcp/recorder/reader.hpp b/pyxcp/recorder/reader.hpp index ba6efd8..d6ce3ec 100644 --- a/pyxcp/recorder/reader.hpp +++ b/pyxcp/recorder/reader.hpp @@ -2,6 +2,8 @@ #ifndef RECORDER_READER_HPP #define RECORDER_READER_HPP +#include + class XcpLogFileReader { public: @@ -37,6 +39,17 @@ class XcpLogFileReader { } m_offset += detail::FILE_HEADER_SIZE; + + if ((m_header.options & XMRAW_HAS_METADATA) == XMRAW_HAS_METADATA) { + std::size_t metadata_length = 0; + std::size_t data_start = m_offset + sizeof(std::size_t); + + read_bytes(m_offset, sizeof(std::size_t), reinterpret_cast(&metadata_length)); + + std::copy(ptr(data_start), ptr(data_start + metadata_length), std::back_inserter(m_metadata)); + // std::cout << "Metadata: " << m_metadata << std::endl; + m_offset += (metadata_length + sizeof(std::size_t)); + } } [[nodiscard]] FileHeaderType get_header() const noexcept { @@ -46,12 +59,16 @@ class XcpLogFileReader { [[nodiscard]] auto get_header_as_tuple() const noexcept -> HeaderTuple { auto hdr = get_header(); - return std::make_tuple( + return std::make_tuple(hdr.version, hdr.options, hdr.num_containers, hdr.record_count, hdr.size_uncompressed, hdr.size_compressed, (double)((std::uint64_t)(((double)hdr.size_uncompressed / (double)hdr.size_compressed * 100.0) + 0.5)) / 100.0 ); } + [[nodiscard]] auto get_metadata() const noexcept { + return m_metadata; + } + void reset() noexcept { m_current_container = 0; m_offset = file_header_size(); @@ -116,6 +133,7 @@ class XcpLogFileReader { std::uint32_t m_current_container{ 0 }; mio::mmap_source *m_mmap{ nullptr }; FileHeaderType m_header; + std::string m_metadata; }; #endif // RECORDER_READER_HPP diff --git a/pyxcp/recorder/reco.py b/pyxcp/recorder/reco.py index 1e58ebf..8697ce8 100644 --- a/pyxcp/recorder/reco.py +++ b/pyxcp/recorder/reco.py @@ -215,7 +215,7 @@ def __init__(self, file_name): self.total_size_uncompressed, ) = FILE_HEADER_STRUCT.unpack(self.get(0, FILE_HEADER_STRUCT.size)) if magic != MAGIC: - raise XcpLogFileParseError(f"Invalid file magic: '{magic}'.") + raise XcpLogFileParseError(f"Invalid file magic: {magic!r}.") def __del__(self): if not self._is_closed: diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index da58f56..9dd1dab 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -64,7 +64,9 @@ constexpr auto megabytes(std::uint32_t value) -> std::uint32_t { return kilobytes(value) * 1024; } -constexpr uint16_t XCP_PAYLOAD_MAX = 0xFFFF; +constexpr std::uint16_t XCP_PAYLOAD_MAX = 0xFFFFUL; + +constexpr std::uint16_t XMRAW_HAS_METADATA = 0x0004UL; /* byte-order is, where applicable little ending (LSB first). @@ -73,23 +75,23 @@ constexpr uint16_t XCP_PAYLOAD_MAX = 0xFFFF; #pragma pack(1) struct FileHeaderType { - uint16_t hdr_size; - uint16_t version; - uint16_t options; - uint32_t num_containers; - uint32_t record_count; - uint32_t size_compressed; - uint32_t size_uncompressed; + std::uint16_t hdr_size; + std::uint16_t version; + std::uint16_t options; + std::uint32_t num_containers; + std::uint32_t record_count; + std::uint32_t size_compressed; + std::uint32_t size_uncompressed; }; -using HeaderTuple = std::tuple; +using HeaderTuple = std::tuple; static_assert(sizeof(FileHeaderType) == 22); struct ContainerHeaderType { - uint32_t record_count; - uint32_t size_compressed; - uint32_t size_uncompressed; + std::uint32_t record_count; + std::uint32_t size_compressed; + std::uint32_t size_uncompressed; }; using blob_t = unsigned char; @@ -102,10 +104,10 @@ using payload_t = py::array_t; #endif /* STANDALONE_REKORDER */ struct frame_header_t { - uint8_t category{ 0 }; - uint16_t counter{ 0 }; + std::uint8_t category{ 0 }; + std::uint16_t counter{ 0 }; double timestamp{ 0.0 }; - uint16_t length{ 0 }; + std::uint16_t length{ 0 }; }; #pragma pack(pop) diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index bebf936..6c5a411 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -13,7 +13,7 @@ #include "daqlist.hpp" #include "helper.hpp" #include "mcobject.hpp" - +#include "writer.hpp" using measurement_value_t = std::variant; using measurement_tuple_t = std::tuple>; @@ -541,7 +541,12 @@ struct Setter { ////////////////////////////////////////////////////////////////////////////////////////////// struct MeasurementParameters { - MeasurementParameters() = delete; + MeasurementParameters() = default; + + MeasurementParameters(MeasurementParameters&& ) = default; + MeasurementParameters(const MeasurementParameters& ) = default; + MeasurementParameters& operator=(MeasurementParameters&& ) = default; + MeasurementParameters& operator=(const MeasurementParameters& ) = default; explicit MeasurementParameters( std::uint8_t byte_order, std::uint8_t id_field_size, bool timestamps_supported, bool ts_fixed, bool prescaler_supported, @@ -578,7 +583,8 @@ struct MeasurementParameters { for (const auto& daq_list : m_daq_lists) { ss << daq_list.dumps(); } - return ss.str(); + + return to_binary(std::size(ss.str())) + ss.str(); } std::uint8_t m_byte_order; @@ -593,6 +599,205 @@ struct MeasurementParameters { std::vector m_daq_lists; }; + +class Deserializer { +public: + explicit Deserializer(const std::string& buf) : m_buf(buf) { + } + + MeasurementParameters run() { + std::uint8_t byte_order; + std::uint8_t id_field_size; + bool timestamps_supported; + bool ts_fixed; + bool prescaler_supported; + bool selectable_timestamps; + double ts_scale_factor; + std::uint8_t ts_size; + std::uint16_t min_daq; + std::size_t dl_count; + std::vector daq_lists; + + byte_order = from_binary(); + id_field_size = from_binary(); + timestamps_supported = from_binary(); + ts_fixed = from_binary(); + prescaler_supported = from_binary(); + selectable_timestamps = from_binary(); + ts_scale_factor = from_binary(); + ts_size = from_binary(); + min_daq = from_binary(); + dl_count = from_binary(); + std::cout << (int)byte_order << " " << (int)id_field_size << " " << timestamps_supported << " " << ts_fixed << " " << prescaler_supported << + " " << selectable_timestamps << " " << ts_scale_factor << " " << (int)ts_size << " " << min_daq << std::endl; + std::cout << "DL-count: " << (int)dl_count << std::endl; + + for (std::size_t i = 0; i < dl_count; i++) { + daq_lists.push_back(create_daq_list()); + } + return MeasurementParameters(byte_order, id_field_size, timestamps_supported, ts_fixed, prescaler_supported, selectable_timestamps, + ts_scale_factor, ts_size, min_daq, daq_lists); + } + +protected: + + DaqList create_daq_list() { + std::string name; + std::uint16_t event_num; + bool stim; + bool enable_timestamps; + std::vector measurements; + std::vector measurements_opt; + std::vector header_names; + + std::uint16_t odt_count; + std::uint16_t total_entries; + std::uint16_t total_length; + + flatten_odts_t flatten_odts; + + std::vector initializer_list{}; + + name = from_binary(); + event_num = from_binary(); + stim = from_binary(); + enable_timestamps = from_binary(); + + odt_count = from_binary(); + total_entries = from_binary(); + total_length = from_binary(); + + std::cout << "Name: " << name << " " << event_num << " " << stim << " " << enable_timestamps << std::endl; + std::cout << "Odt-count: " << (int)odt_count << " " << "Total-entries: " << (int)total_entries << " " << "Total-length: " << (int)total_length << std::endl; + + std::size_t meas_size = from_binary(); + std::cout << "Meas-size: " << (int)meas_size << std::endl; + + for (auto i = 0; i < meas_size; ++i) { + // name, address, ext, dt_name + auto meas = create_mc_object(); + measurements.push_back(meas); + initializer_list.push_back({meas.get_name(), meas.get_address(), meas.get_ext(), meas.get_data_type()}); + } + + std::size_t meas_opt_size = from_binary(); + std::cout << "Meas-opt-size: " << (int)meas_opt_size << std::endl; + for (auto i = 0; i < meas_opt_size; ++i) { + measurements_opt.emplace_back(create_bin()); + } + + std::size_t hname_size = from_binary(); + std::cout << "Hname-size: " << (int)hname_size << std::endl; + for (auto i = 0; i < hname_size; ++i) { + auto header = from_binary(); + header_names.push_back(header); + std::cout << header << " "; + } + std::cout << std::endl << std::endl; + + auto odts = create_flatten_odts(); + + auto result = DaqList(name, event_num, stim, enable_timestamps, initializer_list); + result.set_measurements_opt(measurements_opt); + return result; + } + + flatten_odts_t create_flatten_odts() { + std::string name; + std::uint32_t address; + std::uint8_t ext; + std::uint16_t size; + std::int16_t type_index; + + flatten_odts_t odts; + + std::size_t odt_count = from_binary(); + std::cout << "Odt-count: " << (int)odt_count << std::endl; + for (auto i = 0; i < odt_count; ++i) { + std::vector> flatten_odt{}; + std::size_t odt_entry_count = from_binary(); + std::cout << "Odt-entry-count: " << (int)odt_entry_count << std::endl; + for (auto j = 0; j < odt_entry_count; ++j) { + name = from_binary(); + address = from_binary(); + ext = from_binary(); + size = from_binary(); + type_index = from_binary(); + std::cout << "\t" << name << " " << address << " " << (int)ext << " " << (int)size << " " << (int)type_index << std::endl; + flatten_odt.push_back(std::make_tuple(name, address, ext, size, type_index)); + } + odts.push_back(flatten_odt); + } + + return odts; + } + + McObject create_mc_object() { + std::string name; + std::uint32_t address; + std::uint8_t ext; + std::uint16_t length; + std::string data_type; + std::int16_t type_index; + std::vector components{}; + + name = from_binary(); + address = from_binary(); + ext = from_binary(); + length = from_binary(); + data_type = from_binary(); + type_index = from_binary(); + std::cout << "Name: " << name << " " << address << " " << (int)ext << " " << (int)length << " " << data_type << " " << (int)type_index << std::endl; + std::size_t comp_size = from_binary(); + std::cout << "Comp-size: " << (int)comp_size << std::endl; + for (auto i=0U; i < comp_size; i++) { + components.push_back(create_mc_object()); + } + + return McObject(name, address, ext, length, data_type, components); + } + + Bin create_bin() { + std::uint16_t size; + std::uint16_t residual_capacity; + std::vector entries{}; + + size = from_binary(); + residual_capacity = from_binary(); + std::cout << "BinSize: " << (int)size << " " << "Residual-capacity: " << (int)residual_capacity << std::endl; + std::size_t entry_size = from_binary(); + std::cout << "\tEntry-count: " << (int)entry_size << std::endl; + for (auto i=0U; i < entry_size; i++) { + entries.push_back(create_mc_object()); + } + + return Bin(size, residual_capacity, entries); + } + + template + inline T from_binary() { + auto tmp = *reinterpret_cast(&m_buf[m_offset]); + m_offset += sizeof(T); + return tmp; + } + + template<> + inline std::string from_binary() { + auto length = from_binary(); + std::string result; + auto start = m_buf.cbegin() + m_offset; + + std::copy(start, start + length, std::back_inserter(result)); + m_offset += length; + return result; + } + +private: + std::string m_buf; + std::size_t m_offset = 0; +}; + + class DaqListState { public: @@ -606,7 +811,7 @@ class DaqListState { DaqListState( std::uint16_t daq_list_num, std::uint16_t num_odts, std::uint16_t total_entries, bool enable_timestamps, - std::uint16_t initial_offset, const DaqList::flatten_odts_t& flatten_odts, const Getter& getter, + std::uint16_t initial_offset, const flatten_odts_t& flatten_odts, const Getter& getter, MeasurementParameters params ) : m_daq_list_num(daq_list_num), @@ -729,7 +934,7 @@ class DaqListState { double m_timestamp1 = 0.0; state_t m_state = state_t::IDLE; std::vector m_buffer; - DaqList::flatten_odts_t m_flatten_odts; + flatten_odts_t m_flatten_odts; Getter m_getter; MeasurementParameters m_params; }; @@ -745,15 +950,15 @@ auto requires_swap(std::uint8_t byte_order) -> bool { //////////////////////////////////////////////////////////////////////////////////////////////////////////// -class UnfolderBase { +class DAQProcessor { public: - explicit UnfolderBase(const MeasurementParameters& params) : m_params(params) { + explicit DAQProcessor(const MeasurementParameters& params) : m_params(params) { create_state_vars(params); } - UnfolderBase() = delete; - virtual ~UnfolderBase() = default; + DAQProcessor() = delete; + virtual ~DAQProcessor() = default; void start(const std::vector& first_pids) noexcept { m_getter.set_first_pids(m_params.m_daq_lists, first_pids); @@ -881,20 +1086,79 @@ class XcpLogFileUnfolder { }; #endif -// startMeasurement -// stopMeasurement +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +class DAQPolicyBase { + public: + + virtual ~DAQPolicyBase() {} + + virtual void set_parameters(const MeasurementParameters& params) noexcept { + initialize(); + } + + virtual void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) = 0; + + virtual void initialize() = 0; + + virtual void finalize() = 0; + +}; + + +class DaqRecorderPolicy : public DAQPolicyBase { +public: + + ~DaqRecorderPolicy() {} + + DaqRecorderPolicy() = default; + + void set_parameters(const MeasurementParameters& params) noexcept override { + m_params = params; + DAQPolicyBase::set_parameters(params); + } + + void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) { + if (frame_cat != static_cast(FrameCategory::DAQ)) { + // Only record DAQ frames for now. + return; + } + m_writer->add_frame(frame_cat, counter, timestamp, static_cast(payload.size()), payload.c_str()); + } + + void create_writer(const std::string& file_name, std::uint32_t prealloc, std::uint32_t chunk_size, std::string_view metadata) { + m_writer = std::make_unique(file_name, prealloc, chunk_size, metadata); + } + + void initialize() { + // TODO: Save meta-data. + } + + void finalize() { + m_writer->finalize(); + } + +private: + + std::unique_ptr m_writer; + MeasurementParameters m_params; +}; + -class DAQParser { +class DaqOnlinePolicy : public DAQPolicyBase { public: - virtual ~DAQParser() { + ~DaqOnlinePolicy() { } - DAQParser() = default; + DaqOnlinePolicy() = default; void set_parameters(const MeasurementParameters& params) noexcept { - m_unfolder = std::make_unique(params); - Initialize(); + m_unfolder = std::make_unique(params); + DAQPolicyBase::set_parameters(params); } virtual void on_daq_list( @@ -912,7 +1176,7 @@ class DAQParser { } } - virtual void Initialize() { + virtual void initialize() { } virtual void finalize() { @@ -920,7 +1184,7 @@ class DAQParser { private: - std::unique_ptr m_unfolder; + std::unique_ptr m_unfolder; }; #endif // RECORDER_UNFOLDER_HPP diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 8afe61e..c5c7e8c 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -11,50 +11,90 @@ namespace py = pybind11; using namespace pybind11::literals; -class PyDAQParser : public DAQParser { +class PyDaqOnlinePolicy : public DaqOnlinePolicy { public: - using DAQParser::DAQParser; + using DaqOnlinePolicy::DaqOnlinePolicy; void on_daq_list( std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector &measurement ) override { - PYBIND11_OVERRIDE_PURE(void, DAQParser, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); + PYBIND11_OVERRIDE_PURE(void, DaqOnlinePolicy, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); } - void Initialize() override { - PYBIND11_OVERRIDE(void, DAQParser, Initialize); + void initialize() override { + PYBIND11_OVERRIDE(void, DaqOnlinePolicy, initialize); } void finalize() override { - PYBIND11_OVERRIDE(void, DAQParser, finalize); + PYBIND11_OVERRIDE(void, DaqOnlinePolicy, finalize); } }; +class PyDaqRecorderPolicy : public DaqRecorderPolicy { + public: + + using DaqRecorderPolicy::DaqRecorderPolicy; + + void initialize() override { + PYBIND11_OVERRIDE(void, DaqRecorderPolicy, initialize); + } + + void finalize() override { + PYBIND11_OVERRIDE(void, DaqRecorderPolicy, finalize); + } +}; + + PYBIND11_MODULE(rekorder, m) { - m.doc() = "XCP raw frame recorder."; + m.doc() = "XCP raw frame recorder." + ; + /// m.def("create_measurement_parameters", &create_measurement_parameters); + py::class_(m, "Deserializer") + .def(py::init()) + .def("run", &Deserializer::run) + ; + py::class_(m, "_PyXcpLogFileReader") .def(py::init()) .def("next_block", &XcpLogFileReader::next_block) .def("reset", &XcpLogFileReader::reset) - .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple); + .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple) + .def("get_metadata",[](const XcpLogFileReader& self) { + return py::bytes(self.get_metadata()); + }) + ; + py::class_(m, "_PyXcpLogFileWriter") - .def(py::init()) + .def(py::init(), + py::arg("filename"), py::arg("prealloc"), py::arg("chunk_size"), py::arg("metadata")="") .def("finalize", &XcpLogFileWriter::finalize) - .def("add_frame", &XcpLogFileWriter::add_frame); + .def("add_frame", &XcpLogFileWriter::add_frame) + ; - py::class_(m, "_MeasurementParameters") + py::class_(m, "MeasurementParameters") .def(py::init< std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector &>( )) - .def("dumps", &MeasurementParameters::dumps) + .def("dumps", [](const MeasurementParameters& self) { + return py::bytes(self.dumps()); + }) + ; + + py::class_(m, "DaqRecorderPolicy", py::dynamic_attr()) + .def(py::init<>()) + .def("create_writer", &DaqRecorderPolicy::create_writer) + .def("feed", &DaqRecorderPolicy::feed) + .def("set_parameters", &DaqRecorderPolicy::set_parameters) + .def("initialize", &DaqRecorderPolicy::initialize) + .def("finalize", &DaqRecorderPolicy::finalize) ; - py::class_(m, "DAQParser", py::dynamic_attr()) + py::class_(m, "DaqOnlinePolicy", py::dynamic_attr()) .def(py::init<>()) - .def("on_daq_list", &DAQParser::on_daq_list) - .def("feed", &DAQParser::feed) - .def("finalize", &DAQParser::finalize) - .def("set_parameters", &DAQParser::set_parameters) - .def("Initialize", &DAQParser::Initialize); + .def("on_daq_list", &DaqOnlinePolicy::on_daq_list) + .def("feed", &DaqOnlinePolicy::feed) + .def("finalize", &DaqOnlinePolicy::finalize) + .def("set_parameters", &DaqOnlinePolicy::set_parameters) + .def("initialize", &DaqOnlinePolicy::initialize); } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index d5080f5..62707ef 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -5,7 +5,7 @@ class XcpLogFileWriter { public: - explicit XcpLogFileWriter(const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1) noexcept { + explicit XcpLogFileWriter(const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1, std::string_view metadata="") noexcept { if (!file_name.ends_with(detail::FILE_EXTENSION)) { m_file_name = file_name + detail::FILE_EXTENSION; } else { @@ -25,6 +25,12 @@ class XcpLogFileWriter { m_chunk_size = megabytes(chunk_size); m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)]; m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; + m_metadata = metadata; + + if (!metadata.empty()) { + std::cout << "XMRAW_HAS_METADATA: " << std::size(metadata) << std::endl; + m_offset += std::size(metadata); + } start_thread(); } @@ -45,8 +51,11 @@ class XcpLogFileWriter { if (m_container_record_count) { compress_frames(); } + + std::uint16_t options = m_metadata.empty() ? 0 : XMRAW_HAS_METADATA; + write_header( - detail::VERSION, 0x0000, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed + detail::VERSION, options, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed ); m_mmap->unmap(); truncate(m_offset); @@ -135,6 +144,7 @@ class XcpLogFileWriter { uint32_t size_uncompressed ) noexcept { auto header = FileHeaderType{}; + auto has_metadata =!m_metadata.empty(); write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); header.hdr_size = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; header.version = version; @@ -144,6 +154,10 @@ class XcpLogFileWriter { header.size_compressed = size_compressed; header.size_uncompressed = size_uncompressed; write_bytes(0x00000000UL + detail::MAGIC_SIZE, detail::FILE_HEADER_SIZE, reinterpret_cast(&header)); + if (has_metadata) { + //std::cout << "MD-offset:" << detail::MAGIC_SIZE + detail::FILE_HEADER_SIZE << std::endl; + write_bytes(detail::MAGIC_SIZE + detail::FILE_HEADER_SIZE, m_metadata.size(), m_metadata.c_str()); + } } bool start_thread() noexcept { @@ -192,6 +206,7 @@ class XcpLogFileWriter { std::string m_file_name; std::uint32_t m_offset{ 0 }; std::uint32_t m_chunk_size{ 0 }; + std::string m_metadata; std::uint32_t m_num_containers{ 0 }; std::uint32_t m_record_count{ 0UL }; std::uint32_t m_container_record_count{ 0UL }; diff --git a/pyxcp/scripts/xcp_id_scanner.py b/pyxcp/scripts/xcp_id_scanner.py index 24f60cc..27353c6 100644 --- a/pyxcp/scripts/xcp_id_scanner.py +++ b/pyxcp/scripts/xcp_id_scanner.py @@ -1,7 +1,6 @@ #!/usr/bin/env python """Scan for available IDs. """ -from pprint import pprint from pyxcp.cmdline import ArgumentParser diff --git a/pyxcp/scripts/xcp_info.py b/pyxcp/scripts/xcp_info.py index 2affde0..8917ee4 100644 --- a/pyxcp/scripts/xcp_info.py +++ b/pyxcp/scripts/xcp_info.py @@ -23,7 +23,8 @@ print("\n") print("Implemented IDs:") print("================") - pprint(result) + for key, value in result.items(): + print(f"{key}: {value}", end="\n\n") cps = x.getCurrentProtectionStatus() print("\nProtection Status") print("=================") diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 689f53e..a8074d1 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -34,8 +34,7 @@ def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] def filtered_out(self) -> typing.Set[types.FrameCategory]: return self._frame_types_to_filter_out - def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None: - ... + def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None: ... # noqa: E704 def finalize(self, *args) -> None: """ diff --git a/pyxcp/transport/cxx_ext/setup.py b/pyxcp/transport/cxx_ext/setup.py index 199796b..9142207 100644 --- a/pyxcp/transport/cxx_ext/setup.py +++ b/pyxcp/transport/cxx_ext/setup.py @@ -9,7 +9,7 @@ try: INCLUDE_DIRS = subprocess.getoutput("pybind11-config --include") # nosec except Exception as e: - print(f"Error while executing pybind11-config ('{str(e)}').\npybind11 probably not installed?") + print(f"Error while executing pybind11-config ({e!r}).\npybind11 probably not installed?") sys.exit(1) pf = sys.platform @@ -18,7 +18,7 @@ elif pf.startswith("linux"): LIBS = ["pthread", "rt"] else: - raise RuntimeError(f"Platform '{pf}' currently not supported.") + raise RuntimeError(f"Platform {pf!r} currently not supported.") os.environ["CFLAGS"] = "" diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 8f06e83..8f9c227 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -51,7 +51,7 @@ def __init__(self, config=None, policy=None): self.canonname, self.sockaddr, ) = addrinfo[0] - except BaseException as ex: + except BaseException as ex: # noqa: B036 msg = f"Failed to resolve address {self.host}:{self.port}" self.logger.critical(msg) raise Exception(msg) from ex @@ -69,7 +69,7 @@ def __init__(self, config=None, policy=None): if self._local_address: try: self.sock.bind(self._local_address) - except BaseException as ex: + except BaseException as ex: # noqa: B036 msg = f"Failed to bind socket to given address {self._local_address}" self.logger.critical(msg) raise Exception(msg) from ex @@ -142,7 +142,7 @@ def _packet_listen(self): break else: _packets.append((response, recv_timestamp)) - except BaseException: + except BaseException: # noqa: B036 self.status = 0 # disconnected break diff --git a/pyxcp/utils.py b/pyxcp/utils.py index 6220176..7eb3949 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -13,7 +13,7 @@ def hexDump(arr): size = len(arr) try: arr = arr.hex() - except BaseException: + except BaseException: # noqa: B036 arr = hexlify(arr).decode("ascii") return "[{}]".format(" ".join([arr[i * 2 : (i + 1) * 2] for i in range(size)])) elif isinstance(arr, (list, tuple)): @@ -21,7 +21,7 @@ def hexDump(arr): size = len(arr) try: arr = arr.hex() - except BaseException: + except BaseException: # noqa: B036 arr = hexlify(arr).decode("ascii") return "[{}]".format(" ".join([arr[i * 2 : (i + 1) * 2] for i in range(size)])) else: diff --git a/setup.py b/setup.py index de53835..ec075dc 100644 --- a/setup.py +++ b/setup.py @@ -111,7 +111,7 @@ def run(self): try: subprocess.check_call(gccCmd + self.arguments) # nosec except Exception as e: - print(f"Building pyxcp/asamkeydll.exe failed: '{str(e)}'") + print(f"Building pyxcp/asamkeydll.exe failed: {e!r}") else: print("Successfully build pyxcp/asamkeydll.exe") From 9c6f382aa7f2f7490fdfaa57e117719a9634e9ca Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 20 Mar 2024 13:52:56 +0200 Subject: [PATCH 31/99] today() --- pyxcp/config.py | 57 ------------------------------------ pyxcp/config/__init__.py | 7 +++-- pyxcp/examples/conf_eth.json | 8 ----- 3 files changed, 4 insertions(+), 68 deletions(-) delete mode 100644 pyxcp/config.py delete mode 100644 pyxcp/examples/conf_eth.json diff --git a/pyxcp/config.py b/pyxcp/config.py deleted file mode 100644 index a597113..0000000 --- a/pyxcp/config.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -import json -import pathlib - - -try: - import toml -except ImportError: - HAS_TOML = False -else: - HAS_TOML = True - - -def readConfiguration(conf): - """Read a configuration file either in JSON or TOML format.""" - if conf: - if isinstance(conf, dict): - return dict(conf) - pth = pathlib.Path(conf.name) - suffix = pth.suffix.lower() - if suffix == ".json": - reader = json - elif suffix == ".toml" and HAS_TOML: - reader = toml - else: - reader = None - if reader: - return reader.loads(conf.read()) - else: - return {} - else: - return {} - - -class Configuration: - """""" - - def __init__(self, parameters, config): - self.parameters = parameters - self.config = config - for key, (tp, required, default) in self.parameters.items(): - if key in self.config: - if not isinstance(self.config[key], tp): - raise TypeError(f"Parameter {key} requires {tp}") - else: - if required: - raise AttributeError(f"{key} must be specified in config!") - else: - self.config[key] = default - - def get(self, key): - return self.config.get(key) - - def __repr__(self): - return f"{self.config:s}" - - __str__ = __repr__ diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 21669f4..54edc9f 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -782,7 +782,7 @@ class Transport(SingletonConfigurable): classes = List([Can, Eth, SxI, Usb]) layer = Enum( - ["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=False, help="Choose one of the supported XCP transport layers." + ["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=True, help="Choose one of the supported XCP transport layers." ).tag(config=True) create_daq_timestamps = Bool(False, help="Record time of frame reception or set timestamp to 0.").tag(config=True) timeout = Float( @@ -829,6 +829,7 @@ class ProfileCreate(Application): aliases = Dict( # type:ignore[assignment] dict( d="ProfileCreate.dest_file", + o="ProfileCreate.dest_file", ) ) @@ -849,6 +850,7 @@ class ProfileConvert(Application): dict( c="ProfileConvert.config_file", d="ProfileConvert.dest_file", + o="ProfileConvert.dest_file", ) ) @@ -861,12 +863,11 @@ def start(self): if not Confirm.ask(f"Destination file [green]{dest.name!r}[/green] already exists. do you want to overwrite it?"): print("Aborting...") self.exit(1) - with dest.open("w") as out_file: + with dest.open("w", encoding="latin1") as out_file: pyxcp.generate_config_file(out_file) else: pyxcp.generate_config_file(sys.stdout) - class ProfileApp(Application): subcommands = Dict( dict( diff --git a/pyxcp/examples/conf_eth.json b/pyxcp/examples/conf_eth.json deleted file mode 100644 index e9b235c..0000000 --- a/pyxcp/examples/conf_eth.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "TRANSPORT": "ETH", - "HOST": "localhost", - "PORT": 5555, - "PROTOCOL": "TCP", - "IPV6": false, - "CREATE_DAQ_TIMESTAMPS": false -} From 61712b4c9bef791f44f1fd8656fb56779621b891 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 13 May 2024 09:47:32 +0300 Subject: [PATCH 32/99] today() --- .pre-commit-config.yaml | 14 +- pyxcp/cpp_ext/bin.hpp | 2 +- pyxcp/cpp_ext/daqlist.hpp | 31 ++++ pyxcp/cpp_ext/extension_wrapper.cpp | 3 + pyxcp/cpp_ext/helper.hpp | 17 ++ pyxcp/cpp_ext/mcobject.hpp | 2 +- pyxcp/daq_stim/__init__.py | 62 ++++--- pyxcp/errormatrix.py | 2 + pyxcp/examples/conf_can.json | 20 -- pyxcp/examples/run_daq.py | 13 +- pyxcp/master/master.py | 7 +- pyxcp/recorder/__init__.py | 1 + pyxcp/recorder/rekorder.hpp | 2 + pyxcp/recorder/unfolder.hpp | 279 ++++++++++++++++------------ pyxcp/recorder/wrap.cpp | 65 ++++++- pyxcp/recorder/writer.hpp | 2 +- pyxcp/scripts/xcp_info.py | 23 ++- pyxcp/transport/can.py | 2 +- pyxcp/transport/sxi.py | 2 +- pyxcp/types.py | 6 + 20 files changed, 359 insertions(+), 196 deletions(-) delete mode 100644 pyxcp/examples/conf_can.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fe230c8..fff4ed0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -107,13 +107,13 @@ repos: language: system types_or: [cython, pyi, python] args: ["--filter-files"] - - id: pyupgrade - name: pyupgrade - description: Automatically upgrade syntax for newer versions. - entry: pyupgrade - language: system - types: [python] - args: [--py37-plus] + #- id: pyupgrade + #name: pyupgrade + #description: Automatically upgrade syntax for newer versions. + #entry: pyupgrade + #language: system + #types: [python] + #args: [--py37-plus] - id: trailing-whitespace name: Trim Trailing Whitespace entry: trailing-whitespace-fixer diff --git a/pyxcp/cpp_ext/bin.hpp b/pyxcp/cpp_ext/bin.hpp index dfdee91..e94c864 100644 --- a/pyxcp/cpp_ext/bin.hpp +++ b/pyxcp/cpp_ext/bin.hpp @@ -89,7 +89,7 @@ std::string bin_entries_to_string(const std::vector& entries) { std::stringstream ss; for (const auto& entry : entries) { - ss << to_string(entry) << ", "; + ss << to_string(entry) << ",\n "; } return ss.str(); } diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index e20afe6..248f991 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -140,6 +140,37 @@ class DaqList { return ss.str(); } + + std::string to_string() const { + std::stringstream ss; + + ss << "DaqList("; + ss << "name=\"" << m_name << "\", "; + ss << "event_num=" << static_cast(m_event_num) << ", "; + ss << "stim=" << bool_to_string(m_stim) << ", "; + ss << "enable_timestamps" << bool_to_string(m_enable_timestamps) << ", "; + ss << "measurements=[\n"; + for (const auto& meas: m_measurements) { + ss << ::to_string(meas) << ",\n"; + } + ss << "],\n"; + ss << "measurements_opt=[\n"; + for (const auto& meas: m_measurements_opt) { + ss << ::to_string(meas) << ",\n"; + } + ss << "],\n"; + ss << "header_names=[\n"; + for (const auto& header: m_header_names) { + ss << "\"" << header << "\","; + } + ss << "\n]"; + + // using flatten_odts_t = std::vector>>; + ss << ")"; + return ss.str(); + } + + static void loads(std::string_view buffer) { } diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index d34a544..16c1f5c 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -50,6 +50,9 @@ PYBIND11_MODULE(cpp_ext, m) { py::init&>(), "name"_a, "event_num"_a, "stim"_a, "enable_timestamps"_a, "measurements"_a ) + .def("__repr__", [](const DaqList& self) { + return self.to_string(); + }) .def_property("name", &DaqList::get_name, nullptr) .def_property("event_num", &DaqList::get_event_num, nullptr) .def_property("stim", &DaqList::get_stim, nullptr) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 95b2d9c..5f638b2 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -79,4 +79,21 @@ inline std::string to_binary(const std::string& value) { return result; } + +inline auto bool_to_string(bool value) { + return (value == true) ? "True" : "False"; +} + +inline auto byte_order_to_string(int value) { + switch (value) { + case 0: + return "INTEL"; + case 1: + return "MOTOROLA"; + default: + return ""; + } + return ""; +} + #endif // __HELPER_HPP diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index d924142..d8f94b2 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -169,7 +169,7 @@ std::string mc_components_to_string(const std::vector& components) { std::stringstream ss; for (const auto& obj : components) { - ss << to_string(obj) << ", "; + ss << to_string(obj) << ",\n "; } return ss.str(); } diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 5e24dc2..24800f9 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -31,7 +31,6 @@ class DaqProcessor: def __init__(self, daq_lists: List[DaqList]): # super().__init__() self.daq_lists = daq_lists - self.setup_called = False self.log = get_application().log def setup(self, write_multiple: bool = True): @@ -89,20 +88,7 @@ def setup(self, write_multiple: bool = True): ttt = make_continuous_blocks(daq_list.measurements, max_payload_size, max_payload_size_first) daq_list.measurements_opt = first_fit_decreasing(ttt, max_payload_size, max_payload_size_first) byte_order = 0 if self.xcp_master.slaveProperties.byteOrder == "INTEL" else 1 - self.measurement_params = MeasurementParameters( - byte_order, - header_len, - self.supports_timestampes, - self.ts_fixed, - self.supports_prescaler, - self.selectable_timestamps, - self.ts_scale_factor, - self.ts_size, - self.min_daq, - self.daq_lists, - ) - self.set_parameters(self.measurement_params) - self.first_pids = [] + self._first_pids = [] daq_count = len(self.daq_lists) self.xcp_master.freeDaq() @@ -126,11 +112,8 @@ def setup(self, write_multiple: bool = True): self.xcp_master.setDaqPtr(i, j, 0) for entry in measurement.entries: self.xcp_master.writeDaq(0xFF, entry.length, entry.ext, entry.address) - self.setup_called = True - def start(self): - if not self.setup_called: - raise RuntimeError("please run setup() before start()") + # arm DAQ lists -- this is technically a function on its own. for i, daq_list in enumerate(self.daq_lists, self.min_daq): # print(daq_list.name, daq_list.event_num, daq_list.stim) mode = 0x00 @@ -145,12 +128,31 @@ def start(self): daqListNumber=i, mode=mode, eventChannelNumber=daq_list.event_num, prescaler=1, priority=0xFF ) res = self.xcp_master.startStopDaqList(0x02, i) - self.first_pids.append(res.firstPid) + self._first_pids.append(res.firstPid) + self.measurement_params = MeasurementParameters( + byte_order, + header_len, + self.supports_timestampes, + self.ts_fixed, + self.supports_prescaler, + self.selectable_timestamps, + self.ts_scale_factor, + self.ts_size, + self.min_daq, + self.daq_lists, + self._first_pids, + ) + self.set_parameters(self.measurement_params) + + def start(self): self.xcp_master.startStopSynch(0x01) def stop(self): self.xcp_master.startStopSynch(0x00) + def first_pids(self): + return self._first_pids + class DaqRecorder(DaqProcessor, _DaqRecorderPolicy): @@ -163,20 +165,30 @@ def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 10, def initialize(self): metadata = self.measurement_params.dumps() - print(metadata) _DaqRecorderPolicy.create_writer(self, self.file_name, self.prealloc, self.chunk_size, metadata) - print("After initialization") _DaqRecorderPolicy.initialize(self) def finalize(self): _DaqRecorderPolicy.finalize(self) + def start(self): + DaqProcessor.start(self) + + +class DaqOnlinePolicy(DaqProcessor, _DaqOnlinePolicy): + """Base class for on-line measurements. + Handles multiple inheritence. + """ -# DaqRecorder -# DaqOnline + def __init__(self, daq_lists: List[DaqList]): + DaqProcessor.__init__(self, daq_lists) + _DaqOnlinePolicy.__init__(self) + + def start(self): + DaqProcessor.start(self) -class DaqToCsv(DaqProcessor, _DaqOnlinePolicy): +class DaqToCsv(DaqOnlinePolicy): """Save a measurement as CSV files (one per DAQ-list).""" def initialize(self): diff --git a/pyxcp/errormatrix.py b/pyxcp/errormatrix.py index c58ce47..1c8f2ba 100644 --- a/pyxcp/errormatrix.py +++ b/pyxcp/errormatrix.py @@ -257,6 +257,7 @@ class Severity(enum.IntEnum): ), }, Command.TRANSPORT_LAYER_CMD: { + XcpError.ERR_CMD_UNKNOWN: ((PreAction.NONE), Action.DISPLAY_ERROR), XcpError.ERR_TIMEOUT: ((PreAction.SYNCH,), Action.REPEAT_2_TIMES), XcpError.ERR_CMD_BUSY: ((PreAction.WAIT_T7), Action.REPEAT_INF_TIMES), XcpError.ERR_PGM_ACTIVE: ((PreAction.WAIT_T7), Action.REPEAT_INF_TIMES), @@ -268,6 +269,7 @@ class Severity(enum.IntEnum): ), }, Command.USER_CMD: { + XcpError.ERR_CMD_UNKNOWN: ((PreAction.NONE), Action.DISPLAY_ERROR), XcpError.ERR_TIMEOUT: ((PreAction.SYNCH,), Action.REPEAT_2_TIMES), XcpError.ERR_CMD_BUSY: ((PreAction.WAIT_T7), Action.REPEAT_INF_TIMES), XcpError.ERR_PGM_ACTIVE: ((PreAction.WAIT_T7), Action.REPEAT_INF_TIMES), diff --git a/pyxcp/examples/conf_can.json b/pyxcp/examples/conf_can.json deleted file mode 100644 index af0c241..0000000 --- a/pyxcp/examples/conf_can.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "TRANSPORT": "CAN", - "CAN_DRIVER": "Kvaser", - "CAN_USE_DEFAULT_LISTENER": true, - "CHANNEL": 0, - "ACCEPT_VIRTUAL": true, - "BAUDRATE_PRESET": true, - "CAN_ID_MASTER": 257, - "CAN_ID_SLAVE": 258, - "CAN_ID_BROADCAST": 256, - "MAX_DLC_REQUIRED": false, - "BITRATE": 250000, - "BTL_CYCLES": 16, - "SAMPLE_RATE": 1, - "SAMPLE_POINT": 87.5, - "SJW": 2, - "TSEG1": 5, - "TSEG2": 2, - "CREATE_DAQ_TIMESTAMPS": false -} diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index fb4cacf..69820b1 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -121,9 +121,9 @@ ), ] -# daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. +daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. -daq_parser = DaqRecorder(DAQ_LISTS, "run_daq") +# daq_parser = DaqRecorder(DAQ_LISTS, "run_daq") with ap.run(policy=daq_parser) as x: x.connect() @@ -141,7 +141,8 @@ print("finalize DAQ lists.\n") x.disconnect() -print("Data written to:") -print("================") -for fl in daq_parser.files.values(): # `files` attribute is specific to `DaqToCsv`. - print(fl.name) +if hasattr(daq_parser, "files"): + print("Data written to:") + print("================") + for fl in daq_parser.files.values(): # `files` attribute is specific to `DaqToCsv`. + print(fl.name) diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index a1e047f..6036a75 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -1626,7 +1626,8 @@ def timeCorrelationProperties(self, setProperties, getPropertiesRequest, cluster @broadcasted @wrapped def getSlaveID(self, mode: int): - self.transportLayerCmd(types.TransportLayerCommands.GET_SLAVE_ID, "X", "C", "P", mode) + response = self.transportLayerCmd(types.TransportLayerCommands.GET_SLAVE_ID, ord("X"), ord("C"), ord("P"), mode) + return types.GetSlaveIdResponse.parse(response, byteOrder=self.slaveProperties.byteOrder) def getDaqId(self, daqListNumber: int): response = self.transportLayerCmd(types.TransportLayerCommands.GET_DAQ_ID, *self.WORD_pack(daqListNumber)) @@ -1791,7 +1792,8 @@ def cond_unlock(self, resources=None): MAX_PAYLOAD = self.slaveProperties["maxCto"] - 2 - if not (self.seed_n_key_dll or self.seed_n_key_function): + protection_status = self.getCurrentProtectionStatus() + if any(protection_status.values()) and (not (self.seed_n_key_dll or self.seed_n_key_function)): raise RuntimeError("Neither seed and key DLL nor function specified, cannot proceed.") # TODO: ConfigurationError if resources is None: result = [] @@ -1804,7 +1806,6 @@ def cond_unlock(self, resources=None): if self.slaveProperties["supportsPgm"]: result.append("pgm") resources = ",".join(result) - protection_status = self.getCurrentProtectionStatus() resource_names = [r.lower() for r in re.split(r"[ ,]", resources) if r] for name in resource_names: if name not in types.RESOURCE_VALUES: diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index f7ff5de..34a35b9 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -19,6 +19,7 @@ DaqRecorderPolicy, Deserializer, MeasurementParameters, + XcpLogFileUnfolder, _PyXcpLogFileReader, _PyXcpLogFileWriter, ) diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 9dd1dab..e7235ed 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -178,6 +178,7 @@ inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { } #endif /* STANDALONE_REKORDER */ + inline void hexdump(blob_t const * buf, std::uint16_t sz) { for (std::uint16_t idx = 0; idx < sz; ++idx) { printf("%02X ", buf[idx]); @@ -185,6 +186,7 @@ inline void hexdump(blob_t const * buf, std::uint16_t sz) { printf("\n\r"); } + #include "reader.hpp" #include "unfolder.hpp" #include "writer.hpp" diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 6c5a411..62ad72e 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -319,6 +319,8 @@ struct Getter { void set_first_pids(const std::vector& daq_lists, const std::vector& first_pids) { m_first_pids = first_pids; + std::cout << "set_first_pids()\n"; + if (m_id_size == 1) { // In case of 1-byte ID field (absolute ODT number) we need a mapping. std::uint16_t daq_list_num = 0; @@ -479,25 +481,6 @@ struct Setter { } } -#if 0 - void set_first_pids(const std::vector& daq_lists, const std::vector& first_pids) { - m_first_pids = first_pids; - - if (m_id_size == 1) { - // In case of 1-byte ID field (absolute ODT number) we need a mapping. - std::uint16_t daq_list_num = 0; - for (const auto& daq_list : daq_lists) { - auto first_pid = m_first_pids[daq_list_num]; - - for (std::uint16_t idx = first_pid; idx < daq_list.set_odt_count() + first_pid; ++idx) { - m_odt_to_daq_map[idx] = { daq_list_num, (idx - first_pid) }; - } - daq_list_num++; - } - } - } -#endif - #if 0 std::tuple set_id(blob_t const * buf) { std::uint16_t odt_num = 0; @@ -535,7 +518,6 @@ struct Setter { #if HAS_BFLOAT16==1 std::function bfloat16; #endif - std::vector m_first_pids; std::map> m_odt_to_daq_map; }; ////////////////////////////////////////////////////////////////////////////////////////////// @@ -551,7 +533,7 @@ struct MeasurementParameters { explicit MeasurementParameters( std::uint8_t byte_order, std::uint8_t id_field_size, bool timestamps_supported, bool ts_fixed, bool prescaler_supported, bool selectable_timestamps, double ts_scale_factor, std::uint8_t ts_size, std::uint16_t min_daq, - const std::vector& daq_lists + const std::vector& daq_lists, const std::vector& first_pids ) : m_byte_order(byte_order), m_id_field_size(id_field_size), @@ -562,7 +544,8 @@ struct MeasurementParameters { m_ts_scale_factor(ts_scale_factor), m_ts_size(ts_size), m_min_daq(min_daq), - m_daq_lists(daq_lists) { + m_daq_lists(daq_lists), + m_first_pids(first_pids) { } std::string dumps() const { @@ -584,9 +567,59 @@ struct MeasurementParameters { ss << daq_list.dumps(); } + std::size_t fp_count = m_first_pids.size(); + ss << to_binary(fp_count); + for (const auto& fp : m_first_pids) { + ss << to_binary(fp); + } + return to_binary(std::size(ss.str())) + ss.str(); } + auto get_byte_order() const noexcept { + return m_byte_order; + } + + auto get_id_field_size() const noexcept { + return m_id_field_size; + } + + auto get_timestamps_supported() const noexcept { + return m_timestamps_supported; + } + + auto get_ts_fixed() const noexcept { + return m_ts_fixed; + } + + auto get_prescaler_supported() const noexcept { + return m_prescaler_supported; + } + + auto get_selectable_timestamps() const noexcept { + return m_selectable_timestamps; + } + + auto get_ts_scale_factor() const noexcept { + return m_ts_scale_factor; + } + + auto get_ts_size() const noexcept { + return m_ts_size; + } + + auto get_min_daq() const noexcept { + return m_min_daq; + } + + auto get_daq_lists() const noexcept { + return m_daq_lists; + } + + auto get_first_pids() const noexcept { + return m_first_pids; + } + std::uint8_t m_byte_order; std::uint8_t m_id_field_size; bool m_timestamps_supported; @@ -597,6 +630,7 @@ struct MeasurementParameters { std::uint8_t m_ts_size; std::uint16_t m_min_daq; std::vector m_daq_lists; + std::vector m_first_pids; }; @@ -617,6 +651,8 @@ class Deserializer { std::uint16_t min_daq; std::size_t dl_count; std::vector daq_lists; + std::size_t fp_count; + std::vector first_pids; byte_order = from_binary(); id_field_size = from_binary(); @@ -635,8 +671,14 @@ class Deserializer { for (std::size_t i = 0; i < dl_count; i++) { daq_lists.push_back(create_daq_list()); } + + fp_count = from_binary(); + for (std::size_t i = 0; i < fp_count; i++) { + first_pids.push_back(from_binary()); + } + return MeasurementParameters(byte_order, id_field_size, timestamps_supported, ts_fixed, prescaler_supported, selectable_timestamps, - ts_scale_factor, ts_size, min_daq, daq_lists); + ts_scale_factor, ts_size, min_daq, daq_lists, first_pids); } protected: @@ -945,10 +987,6 @@ auto requires_swap(std::uint8_t byte_order) -> bool { return (target_byte_order != std::endian::native) ? true : false; } -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// - class DAQProcessor { public: @@ -960,35 +998,6 @@ class DAQProcessor { DAQProcessor() = delete; virtual ~DAQProcessor() = default; - void start(const std::vector& first_pids) noexcept { - m_getter.set_first_pids(m_params.m_daq_lists, first_pids); - } - -#if 0 - std::optional> next_block() { - std::vector result{}; - - const auto& block = m_reader.next_block(); - - if (!block) { - return std::nullopt; - } - - for (const auto& frame : block.value()) { - if (std::get<0>(frame) != static_cast(FrameCategory::DAQ)) { - continue; - } - const auto& [category, counter, frame_timestamp, frame_length, payload] = frame; - auto [daq_num, odt_num] = m_getter.get_id(payload.data()); - - if (m_state[daq_num].feed(odt_num, frame_timestamp, payload)) { - m_state[daq_num].add_result(result); - } - } - return result; - } - -#endif std::optional feed(double timestamp, const std::string& payload) noexcept { const auto data = reinterpret_cast(payload.data()); @@ -1015,6 +1024,7 @@ class DAQProcessor { m_getter, params )); } + m_getter.set_first_pids(m_params.m_daq_lists, m_params.m_first_pids); } MeasurementParameters m_params; @@ -1023,73 +1033,6 @@ class DAQProcessor { std::vector m_state; }; -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#if 0 -class XcpLogFileUnfolder { - public: - - explicit XcpLogFileUnfolder(const std::string& file_name, const MeasurementParameters& params) : - m_reader(file_name), m_params(params) { - create_state_vars(params); - } - - XcpLogFileUnfolder() = delete; - - void start(const std::vector& first_pids) { - m_getter.set_first_pids(m_params.m_daq_lists, first_pids); - } - - std::optional> next_block() { - std::vector result{}; - - const auto& block = m_reader.next_block(); - - if (!block) { - return std::nullopt; - } - - for (const auto& frame : block.value()) { - if (std::get<0>(frame) != static_cast(FrameCategory::DAQ)) { - continue; - } - const auto& [category, counter, frame_timestamp, frame_length, payload] = frame; - auto [daq_num, odt_num] = m_getter.get_id(payload.data()); - - if (m_state[daq_num].feed(odt_num, frame_timestamp, payload)) { - m_state[daq_num].add_result(result); - } - } - return result; - } - - private: - - void create_state_vars(const MeasurementParameters& params) { - m_getter = Getter(requires_swap(params.m_byte_order), params.m_id_field_size, params.m_ts_size); - for (auto idx = 0; idx < params.m_daq_lists.size(); ++idx) { - m_state.emplace_back(DaqListState( - idx, params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), - params.m_daq_lists[idx].get_enable_timestamps(), params.m_id_field_size, params.m_daq_lists[idx].get_flatten_odts(), - m_getter, params - )); - } - } - - XcpLogFileReader m_reader; - MeasurementParameters m_params; - Getter m_getter; - std::map m_first_pids; - std::vector m_state; -}; -#endif - -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// - class DAQPolicyBase { public: @@ -1187,4 +1130,94 @@ class DaqOnlinePolicy : public DAQPolicyBase { std::unique_ptr m_unfolder; }; + +class XcpLogFileUnfolder { + public: + + explicit XcpLogFileUnfolder(const std::string& file_name) : + m_reader(file_name) { + + auto metadata = m_reader.get_metadata(); + if (metadata != "") { + auto des = Deserializer(metadata); + m_params = des.run(); + m_unfolder = std::make_unique(m_params); + } else { + // cannot proceed!!! + } + + } + + XcpLogFileUnfolder() = delete; + + + void run() { + + const auto converter = [](const blob_t * in_str, std::size_t length) -> std::string { + std::string result; + result.resize(length); + + for (auto idx=0; idx < length; ++idx) { + result[idx] = static_cast(in_str[idx]); + } + + return result; + }; + + while (true) { + const auto& block = m_reader.next_block(); + if (!block) { + return; + } + + for (const auto& [frame_cat, counter, timestamp, length, payload] : block.value()) { + auto str_data = converter(payload.data(), std::size(payload)); + + if (frame_cat != static_cast(FrameCategory::DAQ)) { + continue; + } + // std::cout << static_cast(frame_cat) << " " << counter << " " << timestamp << " " << length << ": " << std::size(str_data) << " ==> " << str_data << std::endl; + + auto result = m_unfolder->feed(timestamp, str_data); + if (result) { + const auto& [daq_list, ts0, ts1, meas] = *result; + on_daq_list(daq_list, ts0, ts1, meas); + + // std::cout << daq_list << " " << ts0 << " " << ts1 << " " << std::endl; + } + } + } + } + + + virtual void on_daq_list( + std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + ) = 0; + + + MeasurementParameters get_parameters() const noexcept { + return m_params; + } + + auto get_daq_lists() const noexcept { + return m_params.m_daq_lists; + } + + + private: + + XcpLogFileReader m_reader; + std::unique_ptr m_unfolder; + MeasurementParameters m_params; +}; + +#if 0 + wrap.obj : error LNK2001: Nicht aufgel”stes externes Symbol ""public: virtual void __cdecl XcpLogFileUnfolder::on_daq_list( + unsigned short,double,double, + class std::vector,class std::allocator > >, + class std::allocator,class std::allocator > > > > const &) + " (?on_daq_list@XcpLogFileUnfolder@@UEAAXGNNAEBV?$vector@V?$variant@_J_KOV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@std@@V?$allocator@V?$variant@_J_KOV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@std@@@2@@std@@@Z)". +#endif + + #endif // RECORDER_UNFOLDER_HPP diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index c5c7e8c..dace200 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -11,6 +11,7 @@ namespace py = pybind11; using namespace pybind11::literals; + class PyDaqOnlinePolicy : public DaqOnlinePolicy { public: @@ -46,10 +47,22 @@ class PyDaqRecorderPolicy : public DaqRecorderPolicy { }; +class PyXcpLogFileUnfolder : public XcpLogFileUnfolder { + public: + + using XcpLogFileUnfolder::XcpLogFileUnfolder; + + void on_daq_list(std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement) override { + PYBIND11_OVERRIDE_PURE(void, XcpLogFileUnfolder, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); + } + +}; + + PYBIND11_MODULE(rekorder, m) { m.doc() = "XCP raw frame recorder." ; - /// m.def("create_measurement_parameters", &create_measurement_parameters); + py::class_(m, "Deserializer") .def(py::init()) .def("run", &Deserializer::run) @@ -63,7 +76,7 @@ PYBIND11_MODULE(rekorder, m) { .def("get_metadata",[](const XcpLogFileReader& self) { return py::bytes(self.get_metadata()); }) - ; + ; py::class_(m, "_PyXcpLogFileWriter") .def(py::init(), @@ -74,11 +87,46 @@ PYBIND11_MODULE(rekorder, m) { py::class_(m, "MeasurementParameters") .def(py::init< - std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector &>( + std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector&, const std::vector&>( )) .def("dumps", [](const MeasurementParameters& self) { return py::bytes(self.dumps()); }) + .def("__repr__",[](const MeasurementParameters& self) { + std::stringstream ss; + ss << "MeasurementParameters("; + ss << "byte_order=\"" << byte_order_to_string(self.m_byte_order) << "\", "; + ss << "id_field_size=" << static_cast(self.m_id_field_size) << ", "; + ss << "timestamps_supported=" << bool_to_string(self.m_timestamps_supported) << ", "; + ss << "ts_fixed=" << bool_to_string(self.m_ts_fixed) << ", "; + ss << "prescaler_supported=" << bool_to_string(self.m_prescaler_supported) << ", "; + ss << "selectable_timestamps=" << bool_to_string(self.m_selectable_timestamps) << ", "; + ss << "ts_scale_factor=" << self.m_ts_scale_factor << ", "; + ss << "ts_size=" << static_cast(self.m_ts_size) << ", "; + ss << "min_daq=" << static_cast(self.m_min_daq) << ", "; + ss << "daq_lists=[\n"; + for (const auto& dl: self.m_daq_lists) { + ss << dl.to_string() << ",\n"; + } + ss << "],\n"; + ss << "first_pids=["; + for (auto fp: self.m_first_pids) { + ss << fp << ", "; + } + ss << "]"; + return ss.str(); + }) + .def_property_readonly("byte_order", &MeasurementParameters::get_byte_order) + .def_property_readonly("id_field_size", &MeasurementParameters::get_id_field_size) + .def_property_readonly("timestamps_supported", &MeasurementParameters::get_timestamps_supported) + .def_property_readonly("ts_fixed", &MeasurementParameters::get_ts_fixed) + .def_property_readonly("prescaler_supported", &MeasurementParameters::get_prescaler_supported) + .def_property_readonly("selectable_timestamps", &MeasurementParameters::get_selectable_timestamps) + .def_property_readonly("ts_scale_factor", &MeasurementParameters::get_ts_scale_factor) + .def_property_readonly("ts_size", &MeasurementParameters::get_ts_size) + .def_property_readonly("min_daq", &MeasurementParameters::get_min_daq) + .def_property_readonly("daq_lists", &MeasurementParameters::get_daq_lists) + .def_property_readonly("first_pids", &MeasurementParameters::get_first_pids) ; py::class_(m, "DaqRecorderPolicy", py::dynamic_attr()) @@ -96,5 +144,14 @@ PYBIND11_MODULE(rekorder, m) { .def("feed", &DaqOnlinePolicy::feed) .def("finalize", &DaqOnlinePolicy::finalize) .def("set_parameters", &DaqOnlinePolicy::set_parameters) - .def("initialize", &DaqOnlinePolicy::initialize); + .def("initialize", &DaqOnlinePolicy::initialize) + ; + + py::class_(m, "XcpLogFileUnfolder", py::dynamic_attr()) + .def(py::init()) + .def("run", &XcpLogFileUnfolder::run) + .def("on_daq_list", &XcpLogFileUnfolder::on_daq_list) + .def_property_readonly("parameters", &XcpLogFileUnfolder::get_parameters) + .def_property_readonly("daq_lists", &XcpLogFileUnfolder::get_daq_lists) + ; } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 62707ef..dbfbda9 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -28,7 +28,7 @@ class XcpLogFileWriter { m_metadata = metadata; if (!metadata.empty()) { - std::cout << "XMRAW_HAS_METADATA: " << std::size(metadata) << std::endl; + // std::cout << "XMRAW_HAS_METADATA: " << std::size(metadata) << std::endl; m_offset += std::size(metadata); } diff --git a/pyxcp/scripts/xcp_info.py b/pyxcp/scripts/xcp_info.py index 8917ee4..52e9fed 100644 --- a/pyxcp/scripts/xcp_info.py +++ b/pyxcp/scripts/xcp_info.py @@ -80,9 +80,26 @@ print("*** FLASH PROGRAMMING IS NOT SUPPORTED.") if x.slaveProperties.transport_layer == "CAN": - # print("OK, CAN!!!") - status, res = x.try_command(x.getDaqId, 0) - print(status, res) + print("\nTransport-Layer CAN:") + print("====================") + status, res = x.try_command(x.getSlaveID, 0) + if status == TryCommandResult.OK: + print("CAN identifier for CMD/STIM:\n", res) + else: + print("*** GET_SLAVE_ID() IS NOT SUPPORTED.") # no response from bc address ??? + + print("\nPer DAQ-list Identifier") + print("-----------------------") + daq_id = 0 + while True: + status, res = x.try_command(x.getDaqId, daq_id) + if status == TryCommandResult.OK: + print(f"DAQ-list #{daq_id}:", res) + daq_id += 1 + else: + break + if daq_id == 0: + print("N/A") x.disconnect() print("\nDone.") diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index 59ac304..9b2b80d 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -363,7 +363,7 @@ def dataReceived(self, payload: bytes, recv_timestamp: float = None): self.processResponse( payload, len(payload), - counter=self.counterReceived + 1, + counter=(self.counterReceived + 1) & 0xFFFF, recv_timestamp=recv_timestamp, ) diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index ed3c5ef..74246ee 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -63,7 +63,7 @@ def flush(self): def listen(self): while True: - if self.closeEvent.isSet(): + if self.closeEvent.is_set(): return if not self.comm_port.inWaiting(): continue diff --git a/pyxcp/types.py b/pyxcp/types.py index 2434dfb..a9e8338 100644 --- a/pyxcp/types.py +++ b/pyxcp/types.py @@ -6,6 +6,7 @@ from construct import ( BitsInteger, BitStruct, + Bytes, Enum, Flag, GreedyBytes, @@ -736,6 +737,11 @@ class Event(enum.IntEnum): "eventChannelPriority" / Int8ul, ) +GetSlaveIdResponse = Struct( + "magic" / Bytes(3), + "identifier" / Int32u, +) + GetDaqIdResponse = Struct( "canIdFixed" / Enum( From d2540c6d7f847c73988101f3cd97870cf1788e5b Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 20 May 2024 01:42:09 -0400 Subject: [PATCH 33/99] today() --- pyxcp/examples/run_daq.py | 6 +++--- pyxcp/scripts/xcp_profile.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 pyxcp/scripts/xcp_profile.py diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 69820b1..0e88e8c 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -121,9 +121,9 @@ ), ] -daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. +# daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. -# daq_parser = DaqRecorder(DAQ_LISTS, "run_daq") +daq_parser = DaqRecorder(DAQ_LISTS, "run_daq") with ap.run(policy=daq_parser) as x: x.connect() @@ -136,7 +136,7 @@ daq_parser.setup() # Run internal and XCP setup procedures. print("start DAQ lists.") daq_parser.start() # Start DAQ lists. - time.sleep(5.0) # Arbitrary termination condition. + time.sleep(12 * 5.0) # Arbitrary termination condition. daq_parser.stop() # Stop DAQ lists. print("finalize DAQ lists.\n") x.disconnect() diff --git a/pyxcp/scripts/xcp_profile.py b/pyxcp/scripts/xcp_profile.py new file mode 100644 index 0000000..03226f6 --- /dev/null +++ b/pyxcp/scripts/xcp_profile.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +"""Create / convert pyxcp profiles (configurations). +""" + +from pyxcp.cmdline import ArgumentParser + + +ap = ArgumentParser(description="Create / convert pyxcp profiles (configurations).") + +with ap.run() as x: + pass From 4b35d1b53e30bdae47e6ab079b133fd4d508c4d8 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 30 May 2024 11:13:41 +0300 Subject: [PATCH 34/99] today() --- pyxcp/daq_stim/__init__.py | 8 +++- pyxcp/recorder/__init__.py | 2 +- pyxcp/recorder/rekorder.hpp | 46 ++++++++++++++++++++++ pyxcp/recorder/unfolder.hpp | 5 ++- pyxcp/recorder/writer.hpp | 76 +++++++++++++++++++++++++++++-------- pyxcp/transport/sxi.py | 3 +- 6 files changed, 120 insertions(+), 20 deletions(-) diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 24800f9..0e469bf 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -109,6 +109,8 @@ def setup(self, write_multiple: bool = True): for i, daq_list in enumerate(self.daq_lists, self.min_daq): measurements = daq_list.measurements_opt for j, measurement in enumerate(measurements): + if len(measurement.entries) == 0: + continue # CAN special case: No room for data in first ODT. self.xcp_master.setDaqPtr(i, j, 0) for entry in measurement.entries: self.xcp_master.writeDaq(0xFF, entry.length, entry.ext, entry.address) @@ -156,19 +158,23 @@ def first_pids(self): class DaqRecorder(DaqProcessor, _DaqRecorderPolicy): - def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 10, chunk_size: int = 1): + def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 200, chunk_size: int = 1): DaqProcessor.__init__(self, daq_lists) _DaqRecorderPolicy.__init__(self) self.file_name = file_name self.prealloc = prealloc self.chunk_size = chunk_size + def __del__(self): + print("DaqRecorder::__del__()") + def initialize(self): metadata = self.measurement_params.dumps() _DaqRecorderPolicy.create_writer(self, self.file_name, self.prealloc, self.chunk_size, metadata) _DaqRecorderPolicy.initialize(self) def finalize(self): + print("DaqRecorder::finalize()") _DaqRecorderPolicy.finalize(self) def start(self): diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 34a35b9..9967443 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -78,7 +78,7 @@ def as_dataframe(self): class XcpLogFileWriter: """ """ - def __init__(self, file_name: str, prealloc=10, chunk_size=1): + def __init__(self, file_name: str, prealloc=500, chunk_size=1): self._writer = _PyXcpLogFileWriter(file_name, prealloc, chunk_size) self._finalized = False diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index e7235ed..2fffc68 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -11,6 +11,7 @@ #include #include #include + #include #include #include #include @@ -21,6 +22,7 @@ #include #include #include + #include #include #include #include @@ -178,6 +180,50 @@ inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { } #endif /* STANDALONE_REKORDER */ +//////////////////////////////////// +#ifdef _WIN32 +inline std::string error_string(std::string_view func, std::error_code error_code) +{ + LPSTR messageBuffer = nullptr; + std::ostringstream ss; + + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, static_cast(error_code.value()), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + + std::string message(messageBuffer, size); + LocalFree(messageBuffer); + + ss << "[ERROR] "; + ss << func << ": "; + ss << message; + return ss.str(); +} + +std::error_code get_last_error() { + return std::error_code(GetLastError(), std::system_category()); +} + +#else +inline error_string(std::string_view func, std::error_code error_code) { + std::ostringstream ss; + + auto message = strerror(static_cast(error_code.value())); + + ss << "[ERROR] "; + ss << func << ": "; + ss << message; + return ss.str(); + +} + +std::error_code get_last_error() { + return std::error_code(errno, std::system_category()); +} + + +#endif // _WIN32 +//////////////////////////////////// + inline void hexdump(blob_t const * buf, std::uint16_t sz) { for (std::uint16_t idx = 0; idx < sz; ++idx) { diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 62ad72e..31bb644 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -1055,7 +1055,9 @@ class DAQPolicyBase { class DaqRecorderPolicy : public DAQPolicyBase { public: - ~DaqRecorderPolicy() {} + ~DaqRecorderPolicy() { + finalize(); + } DaqRecorderPolicy() = default; @@ -1081,6 +1083,7 @@ class DaqRecorderPolicy : public DAQPolicyBase { } void finalize() { + std::cout << "DaqRecorderPolicy::finalize()\n"; m_writer->finalize(); } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index dbfbda9..54408ef 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -5,7 +5,7 @@ class XcpLogFileWriter { public: - explicit XcpLogFileWriter(const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1, std::string_view metadata="") noexcept { + explicit XcpLogFileWriter(const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1, std::string_view metadata="") { if (!file_name.ends_with(detail::FILE_EXTENSION)) { m_file_name = file_name + detail::FILE_EXTENSION; } else { @@ -20,9 +20,10 @@ class XcpLogFileWriter { #else m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666); #endif - truncate(megabytes(prealloc)); + m_hard_limit = megabytes(prealloc); + truncate(m_hard_limit); m_mmap = new mio::mmap_sink(m_fd); - m_chunk_size = megabytes(chunk_size); + m_chunk_size = 512 * 1024; //megabytes(chunk_size); m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)]; m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; m_metadata = metadata; @@ -31,11 +32,10 @@ class XcpLogFileWriter { // std::cout << "XMRAW_HAS_METADATA: " << std::size(metadata) << std::endl; m_offset += std::size(metadata); } - start_thread(); } - ~XcpLogFileWriter() noexcept { + ~XcpLogFileWriter() { finalize(); #ifdef __APPLE__ if (collector_thread.joinable()) { @@ -45,7 +45,10 @@ class XcpLogFileWriter { } void finalize() { + std::error_code ec; + std::cout << "finalize?\n"; if (!m_finalized) { + std::cout << "\tYES!!!\n"; m_finalized = true; stop_thread(); if (m_container_record_count) { @@ -58,18 +61,27 @@ class XcpLogFileWriter { detail::VERSION, options, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed ); m_mmap->unmap(); + ec = mio::detail::last_error(); + if (ec.value() != 0) { + std::cout << error_string("mio::unmap", ec); + } + truncate(m_offset); #if defined(_WIN32) - CloseHandle(m_fd); + if (!CloseHandle(m_fd)) { + std::cout << error_string("CloseHandle", get_last_error()); + } #else - close(m_fd); + if (close(m_fd) == -1) { + std::cout << error_string("close", get_last_error()); + } #endif delete m_mmap; delete[] m_intermediate_storage; } } - void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const *data) noexcept { + void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const *data) { auto payload = new char[length]; // auto payload = mem.acquire(); @@ -79,25 +91,45 @@ class XcpLogFileWriter { protected: - void truncate(off_t size) const noexcept { + void truncate(off_t size, bool remap = false) { + std::error_code ec; + + if (remap) { + m_mmap->unmap(); + ec = mio::detail::last_error(); + if (ec.value() != 0) { + std::cout << error_string("mio::unmap", ec); + } + } + #if defined(_WIN32) if (SetFilePointer(m_fd, size, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { // TODO: Errorhandling. + std::cout << error_string("SetFilePointer", get_last_error()); } if (SetEndOfFile(m_fd) == 0) { // TODO: Errorhandling. + std::cout << error_string("SetEndOfFile", get_last_error()); } #else - ftruncate(m_fd, size); + if (ftruncate(m_fd, size) == -1) { + std::cout << error_string("ftruncate", get_last_error()); + } #endif + if (remap) { + m_mmap->map(m_fd, 0, size, ec); + if (ec.value() != 0) { + std::cout << error_string("mio::map", ec); + } + } } - blob_t *ptr(std::uint32_t pos = 0) const noexcept { + blob_t *ptr(std::uint32_t pos = 0) const { return (blob_t *)(m_mmap->data() + pos); } template - void store_im(T const *data, std::uint32_t length) noexcept { + void store_im(T const *data, std::uint32_t length) { _fcopy( reinterpret_cast(m_intermediate_storage + m_intermediate_storage_offset), reinterpret_cast(data), length @@ -108,6 +140,7 @@ class XcpLogFileWriter { void compress_frames() { auto container = ContainerHeaderType{}; // printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); + const int cp_size = ::LZ4_compress_default( reinterpret_cast(m_intermediate_storage), reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), m_intermediate_storage_offset, @@ -118,10 +151,18 @@ class XcpLogFileWriter { } // printf("comp: %d %d [%f]\n", m_intermediate_storage_offset, cp_size, double(m_intermediate_storage_offset) / // double(cp_size)); + if (m_offset > (m_hard_limit >> 1)) { + //std::cout << "Offset " << m_offset << " execceds the hard limit of " << (m_hard_limit >> 1) << std::endl; + std::cout <<"[INFO] " << std::chrono::system_clock::now() << ": Doubling measurement file size." << std::endl; + m_hard_limit <<= 1; + truncate(m_hard_limit, true); + } container.record_count = m_container_record_count; container.size_compressed = cp_size; container.size_uncompressed = m_container_size_uncompressed; + _fcopy(reinterpret_cast(ptr(m_offset)), reinterpret_cast(&container), detail::CONTAINER_SIZE); + m_offset += (detail::CONTAINER_SIZE + cp_size); m_total_size_uncompressed += m_container_size_uncompressed; m_total_size_compressed += cp_size; @@ -133,7 +174,7 @@ class XcpLogFileWriter { m_num_containers += 1; } - void write_bytes(std::uint32_t pos, std::uint32_t count, char const *buf) const noexcept { + void write_bytes(std::uint32_t pos, std::uint32_t count, char const *buf) const { auto addr = reinterpret_cast(ptr(pos)); _fcopy(addr, buf, count); @@ -142,7 +183,7 @@ class XcpLogFileWriter { void write_header( uint16_t version, uint16_t options, uint32_t num_containers, uint32_t record_count, uint32_t size_compressed, uint32_t size_uncompressed - ) noexcept { + ) { auto header = FileHeaderType{}; auto has_metadata =!m_metadata.empty(); write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); @@ -160,7 +201,7 @@ class XcpLogFileWriter { } } - bool start_thread() noexcept { + bool start_thread() { if (collector_thread.joinable()) { return false; } @@ -187,11 +228,13 @@ class XcpLogFileWriter { compress_frames(); } } + std::cout << "Exiting collector_thread()\n"; }); + return true; } - bool stop_thread() noexcept { + bool stop_thread() { if (!collector_thread.joinable()) { return false; } @@ -216,6 +259,7 @@ class XcpLogFileWriter { std::uint32_t m_container_size_compressed{ 0UL }; __ALIGN blob_t *m_intermediate_storage{ nullptr }; std::uint32_t m_intermediate_storage_offset{ 0 }; + std::uint32_t m_hard_limit{0}; mio::file_handle_type m_fd{ INVALID_HANDLE_VALUE }; mio::mmap_sink *m_mmap{ nullptr }; bool m_finalized{ false }; diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index 74246ee..fd27e8a 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python +# -*- coding: utf-8 -*- + import struct from time import time From a464469c18301857fc946e7e9a6fc022fafe0c09 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 2 Jun 2024 21:18:03 +0300 Subject: [PATCH 35/99] today() --- build_ext.py | 2 +- pyxcp/examples/run_daq.py | 5 +- pyxcp/recorder/lz4.c | 180 ++++++--- pyxcp/recorder/lz4.h | 73 ++-- pyxcp/recorder/lz4hc.c | 716 ++++++++++++++++++++++++++++-------- pyxcp/recorder/lz4hc.h | 2 +- pyxcp/recorder/rekorder.hpp | 2 +- pyxcp/recorder/unfolder.hpp | 42 +-- pyxcp/recorder/wrap.cpp | 28 ++ pyxcp/recorder/writer.hpp | 51 ++- setup.py | 2 +- 11 files changed, 822 insertions(+), 281 deletions(-) diff --git a/build_ext.py b/build_ext.py index 2373d72..491e7d5 100644 --- a/build_ext.py +++ b/build_ext.py @@ -30,7 +30,7 @@ Pybind11Extension( EXT_NAMES[0], include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/recorder", "pyxcp/cpp_ext"], - sources=["pyxcp/recorder/lz4.c", "pyxcp/recorder/wrap.cpp"], + sources=["pyxcp/recorder/lz4.c", "pyxcp/recorder/lz4hc.c", "pyxcp/recorder/wrap.cpp"], define_macros=[("EXTENSION_NAME", EXT_NAMES[0]), ("NDEBUG", 1)], optional=False, cxx_std=20, diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 0e88e8c..506555b 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -123,7 +123,7 @@ # daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. -daq_parser = DaqRecorder(DAQ_LISTS, "run_daq") +daq_parser = DaqRecorder(DAQ_LISTS, "run_daq", 2) with ap.run(policy=daq_parser) as x: x.connect() @@ -136,7 +136,8 @@ daq_parser.setup() # Run internal and XCP setup procedures. print("start DAQ lists.") daq_parser.start() # Start DAQ lists. - time.sleep(12 * 5.0) # Arbitrary termination condition. + # time.sleep(12 * 5.0) # Arbitrary termination condition.ls *. + time.sleep(6 * 60.0 * 60.0) # Arbitrary termination condition.ls *. daq_parser.stop() # Stop DAQ lists. print("finalize DAQ lists.\n") x.disconnect() diff --git a/pyxcp/recorder/lz4.c b/pyxcp/recorder/lz4.c index 0982f95..a2f7abe 100644 --- a/pyxcp/recorder/lz4.c +++ b/pyxcp/recorder/lz4.c @@ -1,6 +1,6 @@ /* LZ4 - Fast LZ compression algorithm - Copyright (C) 2011-2020, Yann Collet. + Copyright (C) 2011-2023, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -79,7 +79,7 @@ ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) # define LZ4_FORCE_MEMORY_ACCESS 2 -# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) || defined(_MSC_VER) # define LZ4_FORCE_MEMORY_ACCESS 1 # endif #endif @@ -106,15 +106,13 @@ # define LZ4_SRC_INCLUDED 1 #endif -#ifndef LZ4_STATIC_LINKING_ONLY -#define LZ4_STATIC_LINKING_ONLY -#endif - #ifndef LZ4_DISABLE_DEPRECATE_WARNINGS -#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ +# define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ #endif -#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ +#ifndef LZ4_STATIC_LINKING_ONLY +# define LZ4_STATIC_LINKING_ONLY +#endif #include "lz4.h" /* see also "memory routines" below */ @@ -126,14 +124,17 @@ # include /* only present in VS2005+ */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */ +# pragma warning(disable : 6239) /* disable: C6239: ( && ) always evaluates to the result of */ +# pragma warning(disable : 6240) /* disable: C6240: ( && ) always evaluates to the result of */ +# pragma warning(disable : 6326) /* disable: C6326: Potential comparison of a constant with another constant */ #endif /* _MSC_VER */ #ifndef LZ4_FORCE_INLINE -# ifdef _MSC_VER /* Visual Studio */ +# if defined (_MSC_VER) && !defined (__clang__) /* MSVC */ # define LZ4_FORCE_INLINE static __forceinline # else # if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ +# if defined (__GNUC__) || defined (__clang__) # define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) # else # define LZ4_FORCE_INLINE static inline @@ -365,6 +366,11 @@ static unsigned LZ4_isLittleEndian(void) return one.c[0]; } +#if defined(__GNUC__) || defined(__INTEL_COMPILER) +#define LZ4_PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#elif defined(_MSC_VER) +#define LZ4_PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) +#endif #if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) /* lie to the compiler about data alignment; use with caution */ @@ -380,9 +386,9 @@ static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */ -typedef struct { U16 u16; } __attribute__((packed)) LZ4_unalign16; -typedef struct { U32 u32; } __attribute__((packed)) LZ4_unalign32; -typedef struct { reg_t uArch; } __attribute__((packed)) LZ4_unalignST; +LZ4_PACK(typedef struct { U16 u16; }) LZ4_unalign16; +LZ4_PACK(typedef struct { U32 u32; }) LZ4_unalign32; +LZ4_PACK(typedef struct { reg_t uArch; }) LZ4_unalignST; static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign16*)ptr)->u16; } static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign32*)ptr)->u32; } @@ -427,10 +433,22 @@ static U16 LZ4_readLE16(const void* memPtr) return LZ4_read16(memPtr); } else { const BYTE* p = (const BYTE*)memPtr; - return (U16)((U16)p[0] + (p[1]<<8)); + return (U16)((U16)p[0] | (p[1]<<8)); } } +#ifdef LZ4_STATIC_LINKING_ONLY_ENDIANNESS_INDEPENDENT_OUTPUT +static U32 LZ4_readLE32(const void* memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read32(memPtr); + } else { + const BYTE* p = (const BYTE*)memPtr; + return (U32)p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); + } +} +#endif + static void LZ4_writeLE16(void* memPtr, U16 value) { if (LZ4_isLittleEndian()) { @@ -512,7 +530,7 @@ LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) /* LZ4_memcpy_using_offset() presumes : * - dstEnd >= dstPtr + MINMATCH - * - there is at least 8 bytes available to write after dstEnd */ + * - there is at least 12 bytes available to write after dstEnd */ LZ4_FORCE_INLINE void LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) { @@ -527,12 +545,12 @@ LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const si case 2: LZ4_memcpy(v, srcPtr, 2); LZ4_memcpy(&v[2], srcPtr, 2); -#if defined(_MSC_VER) && (_MSC_VER <= 1936) /* MSVC 2022 ver 17.6 or earlier */ +#if defined(_MSC_VER) && (_MSC_VER <= 1937) /* MSVC 2022 ver 17.7 or earlier */ # pragma warning(push) # pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */ #endif LZ4_memcpy(&v[4], v, 4); -#if defined(_MSC_VER) && (_MSC_VER <= 1936) /* MSVC 2022 ver 17.6 or earlier */ +#if defined(_MSC_VER) && (_MSC_VER <= 1937) /* MSVC 2022 ver 17.7 or earlier */ # pragma warning(pop) #endif break; @@ -779,7 +797,12 @@ LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType) LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) { if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); + +#ifdef LZ4_STATIC_LINKING_ONLY_ENDIANNESS_INDEPENDENT_OUTPUT + return LZ4_hash4(LZ4_readLE32(p), tableType); +#else return LZ4_hash4(LZ4_read32(p), tableType); +#endif } LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) @@ -898,7 +921,7 @@ LZ4_prepareTable(LZ4_stream_t_internal* const cctx, cctx->dictSize = 0; } -/** LZ4_compress_generic() : +/** LZ4_compress_generic_validated() : * inlined, to ensure branches are decided at compilation time. * The following conditions are presumed already validated: * - source != NULL @@ -1080,7 +1103,10 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated( /* Catch up */ filledIp = ip; - while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } + assert(ip > anchor); /* this is always true as ip has been advanced before entering the main loop */ + if ((match > lowLimit) && unlikely(ip[-1] == match[-1])) { + do { ip--; match--; } while (((ip > anchor) & (match > lowLimit)) && (unlikely(ip[-1] == match[-1]))); + } /* Encode Literals */ { unsigned const litLength = (unsigned)(ip - anchor); @@ -1095,7 +1121,7 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated( goto _last_literals; } if (litLength >= RUN_MASK) { - int len = (int)(litLength - RUN_MASK); + unsigned len = litLength - RUN_MASK; *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; *op++ = (BYTE)len; @@ -1452,22 +1478,30 @@ int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacit /* Note!: This function leaves the stream in an unclean/broken state! * It is not safe to subsequently use the same state with a _fastReset() or * _continue() call without resetting it. */ -static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) +static int LZ4_compress_destSize_extState_internal(LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration) { void* const s = LZ4_initStream(state, sizeof (*state)); assert(s != NULL); (void)s; if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ - return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); + return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, acceleration); } else { if (*srcSizePtr < LZ4_64Klimit) { - return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1); + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, acceleration); } else { tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1); + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, acceleration); } } } +int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration) +{ + int const r = LZ4_compress_destSize_extState_internal((LZ4_stream_t*)state, src, dst, srcSizePtr, targetDstSize, acceleration); + /* clean the state on exit */ + LZ4_initStream(state, sizeof (LZ4_stream_t)); + return r; +} + int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) { @@ -1479,7 +1513,7 @@ int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targe LZ4_stream_t* const ctx = &ctxBody; #endif - int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); + int result = LZ4_compress_destSize_extState_internal(ctx, src, dst, srcSizePtr, targetDstSize, 1); #if (LZ4_HEAPMODE) FREEMEM(ctx); @@ -1548,8 +1582,11 @@ int LZ4_freeStream (LZ4_stream_t* LZ4_stream) #endif +typedef enum { _ld_fast, _ld_slow } LoadDict_mode_e; #define HASH_UNIT sizeof(reg_t) -int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +int LZ4_loadDict_internal(LZ4_stream_t* LZ4_dict, + const char* dictionary, int dictSize, + LoadDict_mode_e _ld) { LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; const tableType_t tableType = byU32; @@ -1585,13 +1622,39 @@ int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) while (p <= dictEnd-HASH_UNIT) { U32 const h = LZ4_hashPosition(p, tableType); + /* Note: overwriting => favors positions end of dictionary */ LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType); p+=3; idx32+=3; } + if (_ld == _ld_slow) { + /* Fill hash table with additional references, to improve compression capability */ + p = dict->dictionary; + idx32 = dict->currentOffset - dict->dictSize; + while (p <= dictEnd-HASH_UNIT) { + U32 const h = LZ4_hashPosition(p, tableType); + U32 const limit = dict->currentOffset - 64 KB; + if (LZ4_getIndexOnHash(h, dict->hashTable, tableType) <= limit) { + /* Note: not overwriting => favors positions beginning of dictionary */ + LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType); + } + p++; idx32++; + } + } + return (int)dict->dictSize; } +int LZ4_loadDict(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_fast); +} + +int LZ4_loadDictSlow(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_slow); +} + void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) { const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : @@ -1923,6 +1986,17 @@ read_variable_length(const BYTE** ip, const BYTE* ilimit, if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */ return rvl_error; } + s = **ip; + (*ip)++; + length += s; + if (unlikely((*ip) > ilimit)) { /* read limit reached */ + return rvl_error; + } + /* accumulator overflow detection (32-bit mode only) */ + if ((sizeof(length) < 8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { + return rvl_error; + } + if (likely(s != 255)) return length; do { s = **ip; (*ip)++; @@ -1931,10 +2005,10 @@ read_variable_length(const BYTE** ip, const BYTE* ilimit, return rvl_error; } /* accumulator overflow detection (32-bit mode only) */ - if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { + if ((sizeof(length) < 8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { return rvl_error; } - } while (s==255); + } while (s == 255); return length; } @@ -2000,7 +2074,7 @@ LZ4_decompress_generic( * note : fast loop may show a regression for some client arm chips. */ #if LZ4_FAST_DEC_LOOP if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { - DEBUGLOG(6, "skip fast decode loop"); + DEBUGLOG(6, "move to safe decode loop"); goto safe_decode; } @@ -2012,6 +2086,7 @@ LZ4_decompress_generic( assert(ip < iend); token = *ip++; length = token >> ML_BITS; /* literal length */ + DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length); /* decode literal length */ if (length == RUN_MASK) { @@ -2025,49 +2100,47 @@ LZ4_decompress_generic( if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ /* copy literals */ - cpy = op+length; LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); - if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } - LZ4_wildCopy32(op, ip, cpy); - ip += length; op = cpy; - } else { - cpy = op+length; - DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); + if ((op+length>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } + LZ4_wildCopy32(op, ip, op+length); + ip += length; op += length; + } else if (ip <= iend-(16 + 1/*max lit + offset + nextToken*/)) { /* We don't need to check oend, since we check it once for each loop below */ - if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } + DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */ LZ4_memcpy(op, ip, 16); - ip += length; op = cpy; + ip += length; op += length; + } else { + goto safe_literal_copy; } /* get offset */ offset = LZ4_readLE16(ip); ip+=2; - DEBUGLOG(6, " offset = %zu", offset); + DEBUGLOG(6, "blockPos%6u: offset = %u", (unsigned)(op-(BYTE*)dst), (unsigned)offset); match = op - offset; assert(match <= op); /* overflow check */ /* get matchlength */ length = token & ML_MASK; + DEBUGLOG(7, " match length token = %u (len==%u)", (unsigned)length, (unsigned)length+MINMATCH); if (length == ML_MASK) { size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); if (addl == rvl_error) { - DEBUGLOG(6, "error reading long match length"); + DEBUGLOG(5, "error reading long match length"); goto _output_error; } length += addl; length += MINMATCH; + DEBUGLOG(7, " long match length == %u", (unsigned)length); if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { - DEBUGLOG(6, "Error : offset outside buffers"); - goto _output_error; - } if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { goto safe_match_copy; } } else { length += MINMATCH; if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + DEBUGLOG(7, "moving to safe_match_copy (ml==%u)", (unsigned)length); goto safe_match_copy; } @@ -2086,7 +2159,7 @@ LZ4_decompress_generic( } } } if ( checkOffset && (unlikely(match + dictSize < lowPrefix)) ) { - DEBUGLOG(6, "Error : pos=%zi, offset=%zi => outside buffers", op-lowPrefix, op-match); + DEBUGLOG(5, "Error : pos=%zi, offset=%zi => outside buffers", op-lowPrefix, op-match); goto _output_error; } /* match starting within external dictionary */ @@ -2143,6 +2216,7 @@ LZ4_decompress_generic( assert(ip < iend); token = *ip++; length = token >> ML_BITS; /* literal length */ + DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length); /* A two-stage shortcut for the most common case: * 1) If the literal length is 0..14, and there is enough space, @@ -2163,6 +2237,7 @@ LZ4_decompress_generic( /* The second stage: prepare for match copying, decode full info. * If it doesn't work out, the info won't be wasted. */ length = token & ML_MASK; /* match length */ + DEBUGLOG(7, "blockPos%6u: matchLength token = %u (len=%u)", (unsigned)(op-(BYTE*)dst), (unsigned)length, (unsigned)length + 4); offset = LZ4_readLE16(ip); ip += 2; match = op - offset; assert(match <= op); /* check overflow */ @@ -2194,11 +2269,12 @@ LZ4_decompress_generic( if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ } - /* copy literals */ - cpy = op+length; #if LZ4_FAST_DEC_LOOP safe_literal_copy: #endif + /* copy literals */ + cpy = op+length; + LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) { /* We've either hit the input parsing restriction or the output parsing restriction. @@ -2234,9 +2310,10 @@ LZ4_decompress_generic( * so check that we exactly consume the input and don't overrun the output buffer. */ if ((ip+length != iend) || (cpy > oend)) { - DEBUGLOG(6, "should have been last run of literals") - DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); - DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); + DEBUGLOG(5, "should have been last run of literals") + DEBUGLOG(5, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); + DEBUGLOG(5, "or cpy(%p) > (oend-MFLIMIT)(%p)", cpy, oend-MFLIMIT); + DEBUGLOG(5, "after writing %u bytes / %i bytes available", (unsigned)(op-(BYTE*)dst), outputSize); goto _output_error; } } @@ -2262,6 +2339,7 @@ LZ4_decompress_generic( /* get matchlength */ length = token & ML_MASK; + DEBUGLOG(7, "blockPos%6u: matchLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length); _copy_match: if (length == ML_MASK) { @@ -2351,7 +2429,7 @@ LZ4_decompress_generic( while (op < cpy) { *op++ = *match++; } } else { LZ4_memcpy(op, match, 8); - if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } + if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } } op = cpy; /* wildcopy correction */ } diff --git a/pyxcp/recorder/lz4.h b/pyxcp/recorder/lz4.h index f85b038..5c79972 100644 --- a/pyxcp/recorder/lz4.h +++ b/pyxcp/recorder/lz4.h @@ -1,7 +1,7 @@ /* * LZ4 - Fast LZ compression algorithm * Header File - * Copyright (C) 2011-2020, Yann Collet. + * Copyright (C) 2011-2023, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -130,7 +130,7 @@ extern "C" { /*------ Version ------*/ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ #define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */ +#define LZ4_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */ #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) @@ -144,23 +144,25 @@ LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; /*-************************************ -* Tuning parameter +* Tuning memory usage **************************************/ -#define LZ4_MEMORY_USAGE_MIN 10 -#define LZ4_MEMORY_USAGE_DEFAULT 14 -#define LZ4_MEMORY_USAGE_MAX 20 - /*! * LZ4_MEMORY_USAGE : - * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; ) - * Increasing memory usage improves compression ratio, at the cost of speed. + * Can be selected at compile time, by setting LZ4_MEMORY_USAGE. + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB) + * Increasing memory usage improves compression ratio, generally at the cost of speed. * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. - * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache + * Default value is 14, for 16KB, which nicely fits into most L1 caches. */ #ifndef LZ4_MEMORY_USAGE # define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT #endif +/* These are absolute limits, they should not be changed by users */ +#define LZ4_MEMORY_USAGE_MIN 10 +#define LZ4_MEMORY_USAGE_DEFAULT 14 +#define LZ4_MEMORY_USAGE_MAX 20 + #if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN) # error "LZ4_MEMORY_USAGE is too small !" #endif @@ -191,7 +193,7 @@ LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int /*! LZ4_decompress_safe() : * @compressedSize : is the exact complete size of the compressed block. * @dstCapacity : is the size of destination buffer (which must be already allocated), - * is an upper bound of decompressed size. + * presumed an upper bound of decompressed size. * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) * If destination buffer is not large enough, decoding will stop and output an error code (negative value). * If the source stream is detected malformed, the function will stop decoding and return a negative result. @@ -243,17 +245,17 @@ LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int d LZ4LIB_API int LZ4_sizeofState(void); LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - /*! LZ4_compress_destSize() : * Reverse the logic : compresses as much data as possible from 'src' buffer - * into already allocated buffer 'dst', of size >= 'targetDestSize'. + * into already allocated buffer 'dst', of size >= 'dstCapacity'. * This function either compresses the entire 'src' content into 'dst' if it's large enough, * or fill 'dst' buffer completely with as much data as possible from 'src'. * note: acceleration parameter is fixed to "default". * - * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. + * *srcSizePtr : in+out parameter. Initially contains size of input. + * Will be modified to indicate how many bytes where read from 'src' to fill 'dst'. * New value is necessarily <= input value. - * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) + * @return : Nb bytes written into 'dst' (necessarily <= dstCapacity) * or 0 if compression fails. * * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed in v1.9.2+): @@ -267,8 +269,7 @@ LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* d * a dstCapacity which is > decompressedSize, by at least 1 byte. * See https://github.com/lz4/lz4/issues/859 for details */ -LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); - +LZ4LIB_API int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize); /*! LZ4_decompress_safe_partial() : * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', @@ -312,7 +313,7 @@ LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcS ***********************************************/ typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ -/** +/*! Note about RC_INVOKED - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio). @@ -362,13 +363,22 @@ LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. * The same dictionary will have to be loaded on decompression side for successful decoding. * Dictionary are useful for better compression of small data (KB range). - * While LZ4 accept any input as dictionary, - * results are generally better when using Zstandard's Dictionary Builder. + * While LZ4 itself accepts any input as dictionary, dictionary efficiency is also a topic. + * When in doubt, employ the Zstandard's Dictionary Builder. * Loading a size of 0 is allowed, and is the same as reset. - * @return : loaded dictionary size, in bytes (necessarily <= 64 KB) + * @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded) */ LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); +/*! LZ4_loadDictSlow() : v1.9.5+ + * Same as LZ4_loadDict(), + * but uses a bit more cpu to reference the dictionary content more thoroughly. + * This is expected to slightly improve compression ratio. + * The extra-cpu cost is likely worth it if the dictionary is re-used across multiple sessions. + * @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded) + */ +LZ4LIB_API int LZ4_loadDictSlow(LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); + /*! LZ4_compress_fast_continue() : * Compress 'src' content using data from previously compressed blocks, for better compression ratio. * 'dst' buffer must be already allocated. @@ -546,9 +556,9 @@ LZ4_decompress_safe_partial_usingDict(const char* src, char* dst, #define LZ4_STATIC_3504398509 #ifdef LZ4_PUBLISH_STATIC_FUNCTIONS -#define LZ4LIB_STATIC_API LZ4LIB_API +# define LZ4LIB_STATIC_API LZ4LIB_API #else -#define LZ4LIB_STATIC_API +# define LZ4LIB_STATIC_API #endif @@ -564,6 +574,12 @@ LZ4_decompress_safe_partial_usingDict(const char* src, char* dst, */ LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); +/*! LZ4_compress_destSize_extState() : + * Same as LZ4_compress_destSize(), but using an externally allocated state. + * Also: exposes @acceleration + */ +int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration); + /*! LZ4_attach_dictionary() : * This is an experimental API that allows * efficient use of a static dictionary many times. @@ -705,7 +721,7 @@ struct LZ4_stream_t_internal { /* Implicit padding to ensure structure is aligned */ }; -#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */ +#define LZ4_STREAM_MINSIZE ((1UL << (LZ4_MEMORY_USAGE)) + 32) /* static size, for inter-version compatibility */ union LZ4_stream_u { char minStateSize[LZ4_STREAM_MINSIZE]; LZ4_stream_t_internal internal_donotuse; @@ -726,7 +742,7 @@ union LZ4_stream_u { * Note2: An LZ4_stream_t structure guarantees correct alignment and size. * Note3: Before v1.9.0, use LZ4_resetStream() instead **/ -LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); +LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* stateBuffer, size_t size); /*! LZ4_streamDecode_t : @@ -838,11 +854,12 @@ LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4 * But they may happen if input data is invalid (error or intentional tampering). * As a consequence, use these functions in trusted environments with trusted data **only**. */ -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial() instead") LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider migrating towards LZ4_decompress_safe_continue() instead. " + "Note that the contract will change (requires block's compressed size, instead of decompressed size)") LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); /*! LZ4_resetStream() : diff --git a/pyxcp/recorder/lz4hc.c b/pyxcp/recorder/lz4hc.c index 651f190..3a266fb 100644 --- a/pyxcp/recorder/lz4hc.c +++ b/pyxcp/recorder/lz4hc.c @@ -52,19 +52,19 @@ /*=== Dependency ===*/ #define LZ4_HC_STATIC_LINKING_ONLY #include "lz4hc.h" +#include -/*=== Common definitions ===*/ -#if defined(__GNUC__) +/*=== Shared lz4.c code ===*/ +#ifndef LZ4_SRC_INCLUDED +# if defined(__GNUC__) # pragma GCC diagnostic ignored "-Wunused-function" -#endif -#if defined (__clang__) +# endif +# if defined (__clang__) # pragma clang diagnostic ignored "-Wunused-function" -#endif - -#define LZ4_COMMONDEFS_ONLY -#ifndef LZ4_SRC_INCLUDED -#include "lz4.c" /* LZ4_count, constants, mem */ +# endif +# define LZ4_COMMONDEFS_ONLY +# include "lz4.c" /* LZ4_count, constants, mem */ #endif @@ -80,18 +80,122 @@ typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive; /*=== Macros ===*/ #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) -#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG)) -#define DELTANEXTMAXD(p) chainTable[(p) & LZ4HC_MAXD_MASK] /* flexible, LZ4HC_MAXD dependent */ #define DELTANEXTU16(table, pos) table[(U16)(pos)] /* faster */ /* Make fields passed to, and updated by LZ4HC_encodeSequence explicit */ #define UPDATABLE(ip, op, anchor) &ip, &op, &anchor + +/*=== Hashing ===*/ #define LZ4HC_HASHSIZE 4 +#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG)) static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); } +#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) +/* lie to the compiler about data alignment; use with caution */ +static U64 LZ4_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) +/* __pack instructions are safer, but compiler specific */ +LZ4_PACK(typedef struct { U64 u64; }) LZ4_unalign64; +static U64 LZ4_read64(const void* ptr) { return ((const LZ4_unalign64*)ptr)->u64; } + +#else /* safe and portable access using memcpy() */ +static U64 LZ4_read64(const void* memPtr) +{ + U64 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +#endif /* LZ4_FORCE_MEMORY_ACCESS */ + +#define LZ4MID_HASHSIZE 8 +#define LZ4MID_HASHLOG (LZ4HC_HASH_LOG-1) +#define LZ4MID_HASHTABLESIZE (1 << LZ4MID_HASHLOG) + +static U32 LZ4MID_hash4(U32 v) { return (v * 2654435761U) >> (32-LZ4MID_HASHLOG); } +static U32 LZ4MID_hash4Ptr(const void* ptr) { return LZ4MID_hash4(LZ4_read32(ptr)); } +/* note: hash7 hashes the lower 56-bits. + * It presumes input was read using little endian.*/ +static U32 LZ4MID_hash7(U64 v) { return (U32)(((v << (64-56)) * 58295818150454627ULL) >> (64-LZ4MID_HASHLOG)) ; } +static U64 LZ4_readLE64(const void* memPtr); +static U32 LZ4MID_hash8Ptr(const void* ptr) { return LZ4MID_hash7(LZ4_readLE64(ptr)); } + +static U64 LZ4_readLE64(const void* memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read64(memPtr); + } else { + const BYTE* p = (const BYTE*)memPtr; + /* note: relies on the compiler to simplify this expression */ + return (U64)p[0] | ((U64)p[1]<<8) | ((U64)p[2]<<16) | ((U64)p[3]<<24) + | ((U64)p[4]<<32) | ((U64)p[5]<<40) | ((U64)p[6]<<48) | ((U64)p[7]<<56); + } +} + + +/*=== Count match length ===*/ +LZ4_FORCE_INLINE +unsigned LZ4HC_NbCommonBytes32(U32 val) +{ + assert(val != 0); + if (LZ4_isLittleEndian()) { +# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanReverse(&r, val); + return (unsigned)((31 - r) >> 3); +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clz(val) >> 3; +# else + val >>= 8; + val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | + (val + 0x00FF0000)) >> 24; + return (unsigned)val ^ 3; +# endif + } else { +# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward(&r, val); + return (unsigned)(r >> 3); +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctz(val) >> 3; +# else + const U32 m = 0x01010101; + return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; +# endif + } +} + +/** LZ4HC_countBack() : + * @return : negative value, nb of common bytes before ip/match */ +LZ4_FORCE_INLINE +int LZ4HC_countBack(const BYTE* const ip, const BYTE* const match, + const BYTE* const iMin, const BYTE* const mMin) +{ + int back = 0; + int const min = (int)MAX(iMin - ip, mMin - match); + assert(min <= 0); + assert(ip >= iMin); assert((size_t)(ip-iMin) < (1U<<31)); + assert(match >= mMin); assert((size_t)(match - mMin) < (1U<<31)); + + while ((back - min) > 3) { + U32 const v = LZ4_read32(ip + back - 4) ^ LZ4_read32(match + back - 4); + if (v) { + return (back - (int)LZ4HC_NbCommonBytes32(v)); + } else back -= 4; /* 4-byte step */ + } + /* check remainder if any */ + while ( (back > min) + && (ip[back-1] == match[back-1]) ) + back--; + return back; +} + /************************************** -* HC Compression +* Init **************************************/ static void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4) { @@ -119,6 +223,394 @@ static void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start) } +/************************************** +* Encode +**************************************/ +/* LZ4HC_encodeSequence() : + * @return : 0 if ok, + * 1 if buffer issue detected */ +LZ4_FORCE_INLINE int LZ4HC_encodeSequence ( + const BYTE** _ip, + BYTE** _op, + const BYTE** _anchor, + int matchLength, + int offset, + limitedOutput_directive limit, + BYTE* oend) +{ +#define ip (*_ip) +#define op (*_op) +#define anchor (*_anchor) + + size_t length; + BYTE* const token = op++; + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6) + static const BYTE* start = NULL; + static U32 totalCost = 0; + U32 const pos = (start==NULL) ? 0 : (U32)(anchor - start); + U32 const ll = (U32)(ip - anchor); + U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0; + U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0; + U32 const cost = 1 + llAdd + ll + 2 + mlAdd; + if (start==NULL) start = anchor; /* only works for single segment */ + /* g_debuglog_enable = (pos >= 2228) & (pos <= 2262); */ + DEBUGLOG(6, "pos:%7u -- literals:%4u, match:%4i, offset:%5i, cost:%4u + %5u", + pos, + (U32)(ip - anchor), matchLength, offset, + cost, totalCost); + totalCost += cost; +#endif + + /* Encode Literal length */ + length = (size_t)(ip - anchor); + LZ4_STATIC_ASSERT(notLimited == 0); + /* Check output limit */ + if (limit && ((op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) { + DEBUGLOG(6, "Not enough room to write %i literals (%i bytes remaining)", + (int)length, (int)(oend - op)); + return 1; + } + if (length >= RUN_MASK) { + size_t len = length - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for(; len >= 255 ; len -= 255) *op++ = 255; + *op++ = (BYTE)len; + } else { + *token = (BYTE)(length << ML_BITS); + } + + /* Copy Literals */ + LZ4_wildCopy8(op, anchor, op + length); + op += length; + + /* Encode Offset */ + assert(offset <= LZ4_DISTANCE_MAX ); + assert(offset > 0); + LZ4_writeLE16(op, (U16)(offset)); op += 2; + + /* Encode MatchLength */ + assert(matchLength >= MINMATCH); + length = (size_t)matchLength - MINMATCH; + if (limit && (op + (length / 255) + (1 + LASTLITERALS) > oend)) { + DEBUGLOG(6, "Not enough room to write match length"); + return 1; /* Check output limit */ + } + if (length >= ML_MASK) { + *token += ML_MASK; + length -= ML_MASK; + for(; length >= 510 ; length -= 510) { *op++ = 255; *op++ = 255; } + if (length >= 255) { length -= 255; *op++ = 255; } + *op++ = (BYTE)length; + } else { + *token += (BYTE)(length); + } + + /* Prepare next loop */ + ip += matchLength; + anchor = ip; + + return 0; + +#undef ip +#undef op +#undef anchor +} + + +typedef struct { + int off; + int len; + int back; /* negative value */ +} LZ4HC_match_t; + +LZ4HC_match_t LZ4HC_searchExtDict(const BYTE* ip, U32 ipIndex, + const BYTE* const iLowLimit, const BYTE* const iHighLimit, + const LZ4HC_CCtx_internal* dictCtx, U32 gDictEndIndex, + int currentBestML, int nbAttempts) +{ + size_t const lDictEndIndex = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; + U32 lDictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; + U32 matchIndex = lDictMatchIndex + gDictEndIndex - (U32)lDictEndIndex; + int offset = 0, sBack = 0; + assert(lDictEndIndex <= 1 GB); + if (lDictMatchIndex>0) + DEBUGLOG(7, "lDictEndIndex = %zu, lDictMatchIndex = %u", lDictEndIndex, lDictMatchIndex); + while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) { + const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + lDictMatchIndex; + + if (LZ4_read32(matchPtr) == LZ4_read32(ip)) { + int mlt; + int back = 0; + const BYTE* vLimit = ip + (lDictEndIndex - lDictMatchIndex); + if (vLimit > iHighLimit) vLimit = iHighLimit; + mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; + back = (ip > iLowLimit) ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0; + mlt -= back; + if (mlt > currentBestML) { + currentBestML = mlt; + offset = (int)(ipIndex - matchIndex); + sBack = back; + DEBUGLOG(7, "found match of length %i within extDictCtx", currentBestML); + } } + + { U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, lDictMatchIndex); + lDictMatchIndex -= nextOffset; + matchIndex -= nextOffset; + } } + + { LZ4HC_match_t md; + md.len = currentBestML; + md.off = offset; + md.back = sBack; + return md; + } +} + +/************************************** +* Mid Compression (level 2) +**************************************/ + +LZ4_FORCE_INLINE void +LZ4MID_addPosition(U32* hTable, U32 hValue, U32 index) +{ + hTable[hValue] = index; +} + +#define ADDPOS8(_p, _idx) LZ4MID_addPosition(hash8Table, LZ4MID_hash8Ptr(_p), _idx) +#define ADDPOS4(_p, _idx) LZ4MID_addPosition(hash4Table, LZ4MID_hash4Ptr(_p), _idx) + +static int LZ4HC_compress_2hashes ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* srcSizePtr, + int const maxOutputSize, + const limitedOutput_directive limit, + const dictCtx_directive dict + ) +{ + U32* const hash4Table = ctx->hashTable; + U32* const hash8Table = hash4Table + LZ4MID_HASHTABLESIZE; + const BYTE* ip = (const BYTE*)src; + const BYTE* anchor = ip; + const BYTE* const iend = ip + *srcSizePtr; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = (iend - LASTLITERALS); + const BYTE* const ilimit = (iend - LZ4MID_HASHSIZE); + BYTE* op = (BYTE*)dst; + BYTE* oend = op + maxOutputSize; + + const BYTE* const prefixPtr = ctx->prefixStart; + const U32 prefixIdx = ctx->dictLimit; + const U32 ilimitIdx = (U32)(ilimit - prefixPtr) + prefixIdx; + const U32 gDictEndIndex = ctx->lowLimit; + unsigned matchLength; + unsigned matchDistance; + + /* input sanitization */ + DEBUGLOG(5, "LZ4HC_compress_2hashes (%i bytes)", *srcSizePtr); + assert(*srcSizePtr >= 0); + if (*srcSizePtr) assert(src != NULL); + if (maxOutputSize) assert(dst != NULL); + if (*srcSizePtr < 0) return 0; /* invalid */ + if (maxOutputSize < 0) return 0; /* invalid */ + if (*srcSizePtr > LZ4_MAX_INPUT_SIZE) { + /* forbidden: no input is allowed to be that large */ + return 0; + } + if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ + if (*srcSizePtr < LZ4_minLength) + goto _lz4mid_last_literals; /* Input too small, no compression (all literals) */ + + /* main loop */ + while (ip <= mflimit) { + const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx; + /* search long match */ + { U32 h8 = LZ4MID_hash8Ptr(ip); + U32 pos8 = hash8Table[h8]; + assert(h8 < LZ4MID_HASHTABLESIZE); + assert(h8 < ipIndex); + LZ4MID_addPosition(hash8Table, h8, ipIndex); + if ( ipIndex - pos8 <= LZ4_DISTANCE_MAX + && pos8 >= prefixIdx /* note: currently only search within prefix */ + ) { + /* match candidate found */ + const BYTE* matchPtr = prefixPtr + pos8 - prefixIdx; + assert(matchPtr < ip); + matchLength = LZ4_count(ip, matchPtr, matchlimit); + if (matchLength >= MINMATCH) { + DEBUGLOG(7, "found candidate match at pos %u (len=%u)", pos8, matchLength); + matchDistance = ipIndex - pos8; + goto _lz4mid_encode_sequence; + } + } } + /* search short match */ + { U32 h4 = LZ4MID_hash4Ptr(ip); + U32 pos4 = hash4Table[h4]; + assert(h4 < LZ4MID_HASHTABLESIZE); + assert(pos4 < ipIndex); + LZ4MID_addPosition(hash4Table, h4, ipIndex); + if (ipIndex - pos4 <= LZ4_DISTANCE_MAX + && pos4 >= prefixIdx /* only search within prefix */ + ) { + /* match candidate found */ + const BYTE* const matchPtr = prefixPtr + (pos4 - prefixIdx); + assert(matchPtr < ip); + assert(matchPtr >= prefixPtr); + matchLength = LZ4_count(ip, matchPtr, matchlimit); + if (matchLength >= MINMATCH) { + /* short match found, let's just check ip+1 for longer */ + U32 const h8 = LZ4MID_hash8Ptr(ip+1); + U32 const pos8 = hash8Table[h8]; + U32 const m2Distance = ipIndex + 1 - pos8; + matchDistance = ipIndex - pos4; + if ( m2Distance <= LZ4_DISTANCE_MAX + && pos8 >= prefixIdx /* only search within prefix */ + && likely(ip < mflimit) + ) { + const BYTE* const m2Ptr = prefixPtr + (pos8 - prefixIdx); + unsigned ml2 = LZ4_count(ip+1, m2Ptr, matchlimit); + if (ml2 > matchLength) { + LZ4MID_addPosition(hash8Table, h8, ipIndex+1); + ip++; + matchLength = ml2; + matchDistance = m2Distance; + } } + goto _lz4mid_encode_sequence; + } + } } + /* no match found in prefix */ + if ( (dict == usingDictCtxHc) + && (ipIndex - gDictEndIndex < LZ4_DISTANCE_MAX - 8) ) { + /* search a match in dictionary */ + LZ4HC_match_t dMatch = LZ4HC_searchExtDict(ip, ipIndex, + anchor, matchlimit, + ctx->dictCtx, gDictEndIndex, + 0, 2); + if (dMatch.len >= MINMATCH) { + DEBUGLOG(7, "found Dictionary match (offset=%i)", dMatch.off); + ip += dMatch.back; + assert(ip >= anchor); + matchLength = (unsigned)dMatch.len; + matchDistance = (unsigned)dMatch.off; + goto _lz4mid_encode_sequence; + } + } + /* no match found */ + ip += 1 + ((ip-anchor) >> 9); /* skip faster over incompressible data */ + continue; + +_lz4mid_encode_sequence: + /* catch back */ + while (((ip > anchor) & ((U32)(ip-prefixPtr) > matchDistance)) && (unlikely(ip[-1] == ip[-(int)matchDistance-1]))) { + ip--; matchLength++; + }; + + /* fill table with beginning of match */ + ADDPOS8(ip+1, ipIndex+1); + ADDPOS8(ip+2, ipIndex+2); + ADDPOS4(ip+1, ipIndex+1); + + /* encode */ + { BYTE* const saved_op = op; + /* LZ4HC_encodeSequence always updates @op; on success, it updates @ip and @anchor */ + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + (int)matchLength, (int)matchDistance, + limit, oend) ) { + op = saved_op; /* restore @op value before failed LZ4HC_encodeSequence */ + goto _lz4mid_dest_overflow; + } + } + + /* fill table with end of match */ + { U32 endMatchIdx = (U32)(ip-prefixPtr) + prefixIdx; + U32 pos_m2 = endMatchIdx - 2; + if (pos_m2 < ilimitIdx) { + if (likely(ip - prefixPtr > 5)) { + ADDPOS8(ip-5, endMatchIdx - 5); + } + ADDPOS8(ip-3, endMatchIdx - 3); + ADDPOS8(ip-2, endMatchIdx - 2); + ADDPOS4(ip-2, endMatchIdx - 2); + ADDPOS4(ip-1, endMatchIdx - 1); + } + } + } + +_lz4mid_last_literals: + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ + size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255; + size_t const totalSize = 1 + llAdd + lastRunSize; + if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ + if (limit && (op + totalSize > oend)) { + if (limit == limitedOutput) return 0; /* not enough space in @dst */ + /* adapt lastRunSize to fill 'dest' */ + lastRunSize = (size_t)(oend - op) - 1 /*token*/; + llAdd = (lastRunSize + 256 - RUN_MASK) / 256; + lastRunSize -= llAdd; + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize); + ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = (RUN_MASK << ML_BITS); + for(; accumulator >= 255 ; accumulator -= 255) + *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + assert(lastRunSize <= (size_t)(oend - op)); + LZ4_memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + DEBUGLOG(5, "compressed %i bytes into %i bytes", *srcSizePtr, (int)((char*)op - dst)); + assert(ip >= (const BYTE*)src); + assert(ip <= iend); + *srcSizePtr = (int)(ip - (const BYTE*)src); + assert((char*)op >= dst); + assert(op <= oend); + assert((char*)op - dst < INT_MAX); + return (int)((char*)op - dst); + +_lz4mid_dest_overflow: + if (limit == fillOutput) { + /* Assumption : @ip, @anchor, @optr and @matchLength must be set correctly */ + size_t const ll = (size_t)(ip - anchor); + size_t const ll_addbytes = (ll + 240) / 255; + size_t const ll_totalCost = 1 + ll_addbytes + ll; + BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */ + DEBUGLOG(6, "Last sequence is overflowing : %u literals, %u remaining space", + (unsigned)ll, (unsigned)(oend-op)); + if (op + ll_totalCost <= maxLitPos) { + /* ll validated; now adjust match length */ + size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost)); + size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255); + assert(maxMlSize < INT_MAX); + if ((size_t)matchLength > maxMlSize) matchLength= (unsigned)maxMlSize; + if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + matchLength >= MFLIMIT) { + DEBUGLOG(6, "Let's encode a last sequence (ll=%u, ml=%u)", (unsigned)ll, matchLength); + LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + (int)matchLength, (int)matchDistance, + notLimited, oend); + } } + DEBUGLOG(6, "Let's finish with a run of literals (%u bytes left)", (unsigned)(oend-op)); + goto _lz4mid_last_literals; + } + /* compression failed */ + return 0; +} + + +/************************************** +* HC Compression - Search +**************************************/ + /* Update chains up to ip (excluded) */ LZ4_FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip) { @@ -143,23 +635,6 @@ LZ4_FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip) hc4->nextToUpdate = target; } -/** LZ4HC_countBack() : - * @return : negative value, nb of common bytes before ip/match */ -LZ4_FORCE_INLINE -int LZ4HC_countBack(const BYTE* const ip, const BYTE* const match, - const BYTE* const iMin, const BYTE* const mMin) -{ - int back = 0; - int const min = (int)MAX(iMin - ip, mMin - match); - assert(min <= 0); - assert(ip >= iMin); assert((size_t)(ip-iMin) < (1U<<31)); - assert(match >= mMin); assert((size_t)(match - mMin) < (1U<<31)); - while ( (back > min) - && (ip[back-1] == match[back-1]) ) - back--; - return back; -} - #if defined(_MSC_VER) # define LZ4HC_rotl32(x,r) _rotl(x,r) #else @@ -239,10 +714,6 @@ static int LZ4HC_protectDictEnd(U32 const dictLimit, U32 const matchIndex) typedef enum { rep_untested, rep_not, rep_confirmed } repeat_state_e; typedef enum { favorCompressionRatio=0, favorDecompressionSpeed } HCfavor_e; -typedef struct { - int off; - int len; -} LZ4HC_match_t; LZ4_FORCE_INLINE LZ4HC_match_t LZ4HC_InsertAndGetWiderMatch ( @@ -250,7 +721,6 @@ LZ4HC_InsertAndGetWiderMatch ( const BYTE* const ip, const BYTE* const iLowLimit, const BYTE* const iHighLimit, int longest, - const BYTE** startpos, const int maxNbAttempts, const int patternAnalysis, const int chainSwap, const dictCtx_directive dict, @@ -258,7 +728,7 @@ LZ4HC_InsertAndGetWiderMatch ( { U16* const chainTable = hc4->chainTable; U32* const hashTable = hc4->hashTable; - const LZ4HC_CCtx_internal * const dictCtx = hc4->dictCtx; + const LZ4HC_CCtx_internal* const dictCtx = hc4->dictCtx; const BYTE* const prefixPtr = hc4->prefixStart; const U32 prefixIdx = hc4->dictLimit; const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx; @@ -274,11 +744,9 @@ LZ4HC_InsertAndGetWiderMatch ( U32 matchIndex; repeat_state_e repeat = rep_untested; size_t srcPatternLength = 0; - int offset = 0; + int offset = 0, sBack = 0; DEBUGLOG(7, "LZ4HC_InsertAndGetWiderMatch"); - assert(startpos != NULL); - *startpos = ip; /* in case there is no solution */ /* First Match */ LZ4HC_Insert(hc4, ip); /* insert all prior positions up to ip (excluded) */ matchIndex = hashTable[LZ4HC_hashPtr(ip)]; @@ -304,7 +772,7 @@ LZ4HC_InsertAndGetWiderMatch ( if (matchLength > longest) { longest = matchLength; offset = (int)(ipIndex - matchIndex); - *startpos = ip + back; + sBack = back; DEBUGLOG(7, "Found match of len=%i within prefix, offset=%i, back=%i", longest, offset, -back); } } } } else { /* lowestMatchIndex <= matchIndex < dictLimit : within Ext Dict */ @@ -323,7 +791,7 @@ LZ4HC_InsertAndGetWiderMatch ( if (matchLength > longest) { longest = matchLength; offset = (int)(ipIndex - matchIndex); - *startpos = ip + back; + sBack = back; DEBUGLOG(7, "Found match of len=%i within dict, offset=%i, back=%i", longest, offset, -back); } } } @@ -413,7 +881,7 @@ LZ4HC_InsertAndGetWiderMatch ( assert(maxML < 2 GB); longest = (int)maxML; offset = (int)(ipIndex - matchIndex); - *startpos = ip; + assert(sBack == 0); DEBUGLOG(7, "Found repeat pattern match of len=%i, offset=%i", longest, offset); } { U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex); @@ -431,7 +899,7 @@ LZ4HC_InsertAndGetWiderMatch ( if ( dict == usingDictCtxHc && nbAttempts > 0 - && ipIndex - lowestMatchIndex < LZ4_DISTANCE_MAX) { + && withinStartDistance) { size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; assert(dictEndOffset <= 1 GB); @@ -451,7 +919,7 @@ LZ4HC_InsertAndGetWiderMatch ( if (mlt > longest) { longest = mlt; offset = (int)(ipIndex - matchIndex); - *startpos = ip + back; + sBack = back; DEBUGLOG(7, "found match of length %i within extDictCtx", longest); } } @@ -464,6 +932,7 @@ LZ4HC_InsertAndGetWiderMatch ( assert(longest >= 0); md.len = longest; md.off = offset; + md.back = sBack; return md; } } @@ -475,103 +944,13 @@ LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4, /* Index table wi const int patternAnalysis, const dictCtx_directive dict) { - const BYTE* uselessPtr = ip; DEBUGLOG(7, "LZ4HC_InsertAndFindBestMatch"); /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), * but this won't be the case here, as we define iLowLimit==ip, * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ - return LZ4HC_InsertAndGetWiderMatch(hc4, ip, ip, iLimit, MINMATCH-1, &uselessPtr, maxNbAttempts, patternAnalysis, 0 /*chainSwap*/, dict, favorCompressionRatio); + return LZ4HC_InsertAndGetWiderMatch(hc4, ip, ip, iLimit, MINMATCH-1, maxNbAttempts, patternAnalysis, 0 /*chainSwap*/, dict, favorCompressionRatio); } -/* LZ4HC_encodeSequence() : - * @return : 0 if ok, - * 1 if buffer issue detected */ -LZ4_FORCE_INLINE int LZ4HC_encodeSequence ( - const BYTE** _ip, - BYTE** _op, - const BYTE** _anchor, - int matchLength, - int offset, - limitedOutput_directive limit, - BYTE* oend) -{ -#define ip (*_ip) -#define op (*_op) -#define anchor (*_anchor) - - size_t length; - BYTE* const token = op++; - -#if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6) - static const BYTE* start = NULL; - static U32 totalCost = 0; - U32 const pos = (start==NULL) ? 0 : (U32)(anchor - start); - U32 const ll = (U32)(ip - anchor); - U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0; - U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0; - U32 const cost = 1 + llAdd + ll + 2 + mlAdd; - if (start==NULL) start = anchor; /* only works for single segment */ - /* g_debuglog_enable = (pos >= 2228) & (pos <= 2262); */ - DEBUGLOG(6, "pos:%7u -- literals:%4u, match:%4i, offset:%5i, cost:%4u + %5u", - pos, - (U32)(ip - anchor), matchLength, offset, - cost, totalCost); - totalCost += cost; -#endif - - /* Encode Literal length */ - length = (size_t)(ip - anchor); - LZ4_STATIC_ASSERT(notLimited == 0); - /* Check output limit */ - if (limit && ((op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) { - DEBUGLOG(6, "Not enough room to write %i literals (%i bytes remaining)", - (int)length, (int)(oend - op)); - return 1; - } - if (length >= RUN_MASK) { - size_t len = length - RUN_MASK; - *token = (RUN_MASK << ML_BITS); - for(; len >= 255 ; len -= 255) *op++ = 255; - *op++ = (BYTE)len; - } else { - *token = (BYTE)(length << ML_BITS); - } - - /* Copy Literals */ - LZ4_wildCopy8(op, anchor, op + length); - op += length; - - /* Encode Offset */ - assert(offset <= LZ4_DISTANCE_MAX ); - assert(offset > 0); - LZ4_writeLE16(op, (U16)(offset)); op += 2; - - /* Encode MatchLength */ - assert(matchLength >= MINMATCH); - length = (size_t)matchLength - MINMATCH; - if (limit && (op + (length / 255) + (1 + LASTLITERALS) > oend)) { - DEBUGLOG(6, "Not enough room to write match length"); - return 1; /* Check output limit */ - } - if (length >= ML_MASK) { - *token += ML_MASK; - length -= ML_MASK; - for(; length >= 510 ; length -= 510) { *op++ = 255; *op++ = 255; } - if (length >= 255) { length -= 255; *op++ = 255; } - *op++ = (BYTE)length; - } else { - *token += (BYTE)(length); - } - - /* Prepare next loop */ - ip += matchLength; - anchor = ip; - - return 0; -} -#undef ip -#undef op -#undef anchor LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( LZ4HC_CCtx_internal* const ctx, @@ -601,7 +980,7 @@ LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( const BYTE* start2 = NULL; const BYTE* start3 = NULL; LZ4HC_match_t m0, m1, m2, m3; - const LZ4HC_match_t nomatch = {0, 0}; + const LZ4HC_match_t nomatch = {0, 0, 0}; /* init */ DEBUGLOG(5, "LZ4HC_compress_hashChain (dict?=>%i)", dict); @@ -620,16 +999,21 @@ LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( _Search2: DEBUGLOG(7, "_Search2 (currently found match of size %i)", m1.len); if (ip+m1.len <= mflimit) { + start2 = ip + m1.len - 2; m2 = LZ4HC_InsertAndGetWiderMatch(ctx, - ip + m1.len - 2, ip + 0, matchlimit, m1.len, &start2, + start2, ip + 0, matchlimit, m1.len, maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio); + start2 += m2.back; } else { m2 = nomatch; /* do not search further */ } if (m2.len <= m1.len) { /* No better match => encode ML1 immediately */ optr = op; - if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, limit, oend)) goto _dest_overflow; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m1.len, m1.off, + limit, oend) ) + goto _dest_overflow; continue; } @@ -660,9 +1044,11 @@ LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( } if (start2 + m2.len <= mflimit) { + start3 = start2 + m2.len - 3; m3 = LZ4HC_InsertAndGetWiderMatch(ctx, - start2 + m2.len - 3, start2, matchlimit, m2.len, &start3, + start3, start2, matchlimit, m2.len, maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio); + start3 += m3.back; } else { m3 = nomatch; /* do not search further */ } @@ -672,11 +1058,15 @@ LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( if (start2 < ip+m1.len) m1.len = (int)(start2 - ip); /* Now, encode 2 sequences */ optr = op; - if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, limit, oend)) + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m1.len, m1.off, + limit, oend) ) goto _dest_overflow; ip = start2; optr = op; - if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m2.len, m2.off, limit, oend)) { + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m2.len, m2.off, + limit, oend) ) { m1 = m2; goto _dest_overflow; } @@ -696,7 +1086,10 @@ LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( } optr = op; - if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, limit, oend)) goto _dest_overflow; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m1.len, m1.off, + limit, oend) ) + goto _dest_overflow; ip = start3; m1 = m3; @@ -731,7 +1124,10 @@ LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( } } optr = op; - if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), m1.len, m1.off, limit, oend)) goto _dest_overflow; + if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), + m1.len, m1.off, + limit, oend) ) + goto _dest_overflow; /* ML2 becomes ML1 */ ip = start2; m1 = m2; @@ -777,7 +1173,7 @@ LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( _dest_overflow: if (limit == fillOutput) { - /* Assumption : ip, anchor, ml and ref must be set correctly */ + /* Assumption : @ip, @anchor, @optr and @m1 must be set correctly */ size_t const ll = (size_t)(ip - anchor); size_t const ll_addbytes = (ll + 240) / 255; size_t const ll_totalCost = 1 + ll_addbytes + ll; @@ -821,16 +1217,16 @@ LZ4HC_compress_generic_internal ( const dictCtx_directive dict ) { - typedef enum { lz4hc, lz4opt } lz4hc_strat_e; + typedef enum { lz4mid, lz4hc, lz4opt } lz4hc_strat_e; typedef struct { lz4hc_strat_e strat; int nbSearches; U32 targetLength; } cParams_t; static const cParams_t clTable[LZ4HC_CLEVEL_MAX+1] = { - { lz4hc, 2, 16 }, /* 0, unused */ - { lz4hc, 2, 16 }, /* 1, unused */ - { lz4hc, 2, 16 }, /* 2, unused */ + { lz4mid, 2, 16 }, /* 0, unused */ + { lz4mid, 2, 16 }, /* 1, unused */ + { lz4mid, 2, 16 }, /* 2 */ { lz4hc, 4, 16 }, /* 3 */ { lz4hc, 8, 16 }, /* 4 */ { lz4hc, 16, 16 }, /* 5 */ @@ -850,13 +1246,20 @@ LZ4HC_compress_generic_internal ( if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size (too large or negative) */ ctx->end += *srcSizePtr; - if (cLevel < 1) cLevel = LZ4HC_CLEVEL_DEFAULT; /* note : convention is different from lz4frame, maybe something to review */ + /* note : clevel convention is a bit different from lz4frame, + * possibly something worth revisiting for consistency */ + if (cLevel < 1) + cLevel = LZ4HC_CLEVEL_DEFAULT; cLevel = MIN(LZ4HC_CLEVEL_MAX, cLevel); { cParams_t const cParam = clTable[cLevel]; HCfavor_e const favor = ctx->favorDecSpeed ? favorDecompressionSpeed : favorCompressionRatio; int result; - if (cParam.strat == lz4hc) { + if (cParam.strat == lz4mid) { + result = LZ4HC_compress_2hashes(ctx, + src, dst, srcSizePtr, dstCapacity, + limit, dict); + } else if (cParam.strat == lz4hc) { result = LZ4HC_compress_hashChain(ctx, src, dst, srcSizePtr, dstCapacity, cParam.nbSearches, limit, dict); @@ -1321,14 +1724,15 @@ LZ4HC_FindLongerMatch(LZ4HC_CCtx_internal* const ctx, const dictCtx_directive dict, const HCfavor_e favorDecSpeed) { - LZ4HC_match_t const match0 = { 0 , 0 }; + LZ4HC_match_t const match0 = { 0 , 0, 0 }; /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), * but this won't be the case here, as we define iLowLimit==ip, ** so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ - LZ4HC_match_t md = LZ4HC_InsertAndGetWiderMatch(ctx, ip, ip, iHighLimit, minLen, &ip, nbSearches, 1 /*patternAnalysis*/, 1 /*chainSwap*/, dict, favorDecSpeed); + LZ4HC_match_t md = LZ4HC_InsertAndGetWiderMatch(ctx, ip, ip, iHighLimit, minLen, nbSearches, 1 /*patternAnalysis*/, 1 /*chainSwap*/, dict, favorDecSpeed); + assert(md.back == 0); if (md.len <= minLen) return match0; if (favorDecSpeed) { - if ((md.len>18) & (md.len<=36)) md.len=18; /* favor shortcut */ + if ((md.len>18) & (md.len<=36)) md.len=18; /* favor dec.speed (shortcut) */ } return md; } @@ -1407,11 +1811,11 @@ static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx, rPos, cost, opt[rPos].litlen); } } /* set prices using initial match */ - { int mlen = MINMATCH; - int const matchML = firstMatch.len; /* necessarily < sufficient_len < LZ4_OPT_NUM */ + { int const matchML = firstMatch.len; /* necessarily < sufficient_len < LZ4_OPT_NUM */ int const offset = firstMatch.off; + int mlen; assert(matchML < LZ4_OPT_NUM); - for ( ; mlen <= matchML ; mlen++) { + for (mlen = MINMATCH ; mlen <= matchML ; mlen++) { int const cost = LZ4HC_sequencePrice(llen, mlen); opt[mlen].mlen = mlen; opt[mlen].off = offset; @@ -1631,7 +2035,7 @@ if (limit == fillOutput) { } _return_label: #if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 - FREEMEM(opt); + if (opt) FREEMEM(opt); #endif return retval; } diff --git a/pyxcp/recorder/lz4hc.h b/pyxcp/recorder/lz4hc.h index e937acf..f23db5f 100644 --- a/pyxcp/recorder/lz4hc.h +++ b/pyxcp/recorder/lz4hc.h @@ -44,7 +44,7 @@ extern "C" { /* --- Useful constants --- */ -#define LZ4HC_CLEVEL_MIN 3 +#define LZ4HC_CLEVEL_MIN 2 #define LZ4HC_CLEVEL_DEFAULT 9 #define LZ4HC_CLEVEL_OPT_MIN 10 #define LZ4HC_CLEVEL_MAX 12 diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 2fffc68..051cc01 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -38,7 +38,7 @@ #include #endif /* _WIN32 */ - #include "lz4.h" + #include "lz4hc.h" #include "mio.hpp" #if STANDALONE_REKORDER == 0 diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 31bb644..ca3275d 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -319,8 +319,6 @@ struct Getter { void set_first_pids(const std::vector& daq_lists, const std::vector& first_pids) { m_first_pids = first_pids; - std::cout << "set_first_pids()\n"; - if (m_id_size == 1) { // In case of 1-byte ID field (absolute ODT number) we need a mapping. std::uint16_t daq_list_num = 0; @@ -664,9 +662,6 @@ class Deserializer { ts_size = from_binary(); min_daq = from_binary(); dl_count = from_binary(); - std::cout << (int)byte_order << " " << (int)id_field_size << " " << timestamps_supported << " " << ts_fixed << " " << prescaler_supported << - " " << selectable_timestamps << " " << ts_scale_factor << " " << (int)ts_size << " " << min_daq << std::endl; - std::cout << "DL-count: " << (int)dl_count << std::endl; for (std::size_t i = 0; i < dl_count; i++) { daq_lists.push_back(create_daq_list()); @@ -709,12 +704,7 @@ class Deserializer { total_entries = from_binary(); total_length = from_binary(); - std::cout << "Name: " << name << " " << event_num << " " << stim << " " << enable_timestamps << std::endl; - std::cout << "Odt-count: " << (int)odt_count << " " << "Total-entries: " << (int)total_entries << " " << "Total-length: " << (int)total_length << std::endl; - std::size_t meas_size = from_binary(); - std::cout << "Meas-size: " << (int)meas_size << std::endl; - for (auto i = 0; i < meas_size; ++i) { // name, address, ext, dt_name auto meas = create_mc_object(); @@ -723,19 +713,15 @@ class Deserializer { } std::size_t meas_opt_size = from_binary(); - std::cout << "Meas-opt-size: " << (int)meas_opt_size << std::endl; for (auto i = 0; i < meas_opt_size; ++i) { measurements_opt.emplace_back(create_bin()); } std::size_t hname_size = from_binary(); - std::cout << "Hname-size: " << (int)hname_size << std::endl; for (auto i = 0; i < hname_size; ++i) { auto header = from_binary(); header_names.push_back(header); - std::cout << header << " "; } - std::cout << std::endl << std::endl; auto odts = create_flatten_odts(); @@ -754,18 +740,15 @@ class Deserializer { flatten_odts_t odts; std::size_t odt_count = from_binary(); - std::cout << "Odt-count: " << (int)odt_count << std::endl; for (auto i = 0; i < odt_count; ++i) { std::vector> flatten_odt{}; std::size_t odt_entry_count = from_binary(); - std::cout << "Odt-entry-count: " << (int)odt_entry_count << std::endl; for (auto j = 0; j < odt_entry_count; ++j) { name = from_binary(); address = from_binary(); ext = from_binary(); size = from_binary(); type_index = from_binary(); - std::cout << "\t" << name << " " << address << " " << (int)ext << " " << (int)size << " " << (int)type_index << std::endl; flatten_odt.push_back(std::make_tuple(name, address, ext, size, type_index)); } odts.push_back(flatten_odt); @@ -789,9 +772,7 @@ class Deserializer { length = from_binary(); data_type = from_binary(); type_index = from_binary(); - std::cout << "Name: " << name << " " << address << " " << (int)ext << " " << (int)length << " " << data_type << " " << (int)type_index << std::endl; std::size_t comp_size = from_binary(); - std::cout << "Comp-size: " << (int)comp_size << std::endl; for (auto i=0U; i < comp_size; i++) { components.push_back(create_mc_object()); } @@ -806,9 +787,7 @@ class Deserializer { size = from_binary(); residual_capacity = from_binary(); - std::cout << "BinSize: " << (int)size << " " << "Residual-capacity: " << (int)residual_capacity << std::endl; std::size_t entry_size = from_binary(); - std::cout << "\tEntry-count: " << (int)entry_size << std::endl; for (auto i=0U; i < entry_size; i++) { entries.push_back(create_mc_object()); } @@ -893,7 +872,6 @@ class DaqListState { return state_t::FINISHED; } } else { - // std::cout << "\t\tODT num out of order: " << odt_num << " -- expected: " << m_next_odt << std::endl; resetSM(); return state_t::_ERROR; } @@ -921,7 +899,6 @@ class DaqListState { } void add_result(measurement_tuple_t& result_buffer) { - //std::cout << "add_result: " << m_daq_list_num << " " << m_timestamp0 << " " << m_timestamp1 << std::endl; result_buffer = { m_daq_list_num, m_timestamp0, m_timestamp1, m_buffer }; } @@ -1083,7 +1060,6 @@ class DaqRecorderPolicy : public DAQPolicyBase { } void finalize() { - std::cout << "DaqRecorderPolicy::finalize()\n"; m_writer->finalize(); } @@ -1175,18 +1151,13 @@ class XcpLogFileUnfolder { for (const auto& [frame_cat, counter, timestamp, length, payload] : block.value()) { auto str_data = converter(payload.data(), std::size(payload)); - if (frame_cat != static_cast(FrameCategory::DAQ)) { continue; } - // std::cout << static_cast(frame_cat) << " " << counter << " " << timestamp << " " << length << ": " << std::size(str_data) << " ==> " << str_data << std::endl; - auto result = m_unfolder->feed(timestamp, str_data); if (result) { const auto& [daq_list, ts0, ts1, meas] = *result; on_daq_list(daq_list, ts0, ts1, meas); - - // std::cout << daq_list << " " << ts0 << " " << ts1 << " " << std::endl; } } } @@ -1198,20 +1169,23 @@ class XcpLogFileUnfolder { ) = 0; - MeasurementParameters get_parameters() const noexcept { + MeasurementParameters get_parameters() const { return m_params; } - auto get_daq_lists() const noexcept { + auto get_daq_lists() const { return m_params.m_daq_lists; } + auto get_header() const { + return m_reader.get_header(); + } private: - XcpLogFileReader m_reader; - std::unique_ptr m_unfolder; - MeasurementParameters m_params; + XcpLogFileReader m_reader; + std::unique_ptr m_unfolder; + MeasurementParameters m_params; }; #if 0 diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index dace200..e20d830 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -63,6 +63,33 @@ PYBIND11_MODULE(rekorder, m) { m.doc() = "XCP raw frame recorder." ; + #if 0 + version; + options; + num_containers; + record_count; + size_compressed; + size_uncompressed; + #endif + + py::class_(m, "FileHeaderType") + .def(py::init()) + .def("__repr__",[](const FileHeaderType& self) { + std::stringstream ss; + ss << "FileHeaderType(" << std::endl; + ss << " hdr_size=" << self.hdr_size << "," << std::endl; + ss << " version=" << self.version << "," << std::endl; + ss << " options=" << self.options << "," << std::endl; + ss << " num_containers=" << self.num_containers << "," << std::endl; + ss << " record_count=" << self.record_count << "," << std::endl; + ss << " size_compressed=" << self.size_compressed << "," << std::endl; + ss << " size_uncompressed=" << self.size_uncompressed << "," << std::endl; + ss << " compression_ratio=" << (double)((std::uint64_t)(((double)self.size_uncompressed / (double)self.size_compressed * 100.0) + 0.5)) / 100.0 << std::endl; + ss << ")" << std::endl; + return ss.str(); + }) + ; + py::class_(m, "Deserializer") .def(py::init()) .def("run", &Deserializer::run) @@ -153,5 +180,6 @@ PYBIND11_MODULE(rekorder, m) { .def("on_daq_list", &XcpLogFileUnfolder::on_daq_list) .def_property_readonly("parameters", &XcpLogFileUnfolder::get_parameters) .def_property_readonly("daq_lists", &XcpLogFileUnfolder::get_daq_lists) + .def("get_header", &XcpLogFileUnfolder::get_header) ; } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 54408ef..37c6817 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -31,6 +31,7 @@ class XcpLogFileWriter { if (!metadata.empty()) { // std::cout << "XMRAW_HAS_METADATA: " << std::size(metadata) << std::endl; m_offset += std::size(metadata); + write_metadata(); } start_thread(); } @@ -124,6 +125,33 @@ class XcpLogFileWriter { } } + void flush() { + std::chrono::time_point start_time = std::chrono::system_clock::now(); + + + #ifdef __APPLE__ + flushing_thread = std::thread([this]() { +#else + flushing_thread = std::jthread([this]() { +#endif + #if defined(_WIN32) + if (!FlushFileBuffers(m_fd)) { + std::cout << error_string("FlushFileBuffers", get_last_error()); + } + #else + if (fflush(m_fd); == -1) { + std::cout << error_string("fflush", get_last_error()); + } + #endif + }); + + std::chrono::time_point stop_time = std::chrono::system_clock::now(); + auto eta = std::chrono::duration_cast(stop_time - start_time); + + std::cout <<"[DEBUG] " << "Flushing took " << static_cast(eta.count()) / 1000000.0 << " seconds." << std::endl; + + } + blob_t *ptr(std::uint32_t pos = 0) const { return (blob_t *)(m_mmap->data() + pos); } @@ -140,12 +168,14 @@ class XcpLogFileWriter { void compress_frames() { auto container = ContainerHeaderType{}; // printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); - - const int cp_size = ::LZ4_compress_default( + const int cp_size = ::LZ4_compress_HC( reinterpret_cast(m_intermediate_storage), reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), m_intermediate_storage_offset, - LZ4_COMPRESSBOUND(m_intermediate_storage_offset) + LZ4_COMPRESSBOUND(m_intermediate_storage_offset), LZ4HC_CLEVEL_MAX ); + + + if (cp_size < 0) { throw std::runtime_error("LZ4 compression failed."); } @@ -156,6 +186,11 @@ class XcpLogFileWriter { std::cout <<"[INFO] " << std::chrono::system_clock::now() << ": Doubling measurement file size." << std::endl; m_hard_limit <<= 1; truncate(m_hard_limit, true); + write_header( + detail::VERSION, m_metadata.empty() ? 0 : XMRAW_HAS_METADATA, m_num_containers, m_record_count, + m_total_size_compressed, m_total_size_uncompressed + ); + flush(); } container.record_count = m_container_record_count; container.size_compressed = cp_size; @@ -185,7 +220,6 @@ class XcpLogFileWriter { uint32_t size_uncompressed ) { auto header = FileHeaderType{}; - auto has_metadata =!m_metadata.empty(); write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); header.hdr_size = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; header.version = version; @@ -195,9 +229,12 @@ class XcpLogFileWriter { header.size_compressed = size_compressed; header.size_uncompressed = size_uncompressed; write_bytes(0x00000000UL + detail::MAGIC_SIZE, detail::FILE_HEADER_SIZE, reinterpret_cast(&header)); - if (has_metadata) { - //std::cout << "MD-offset:" << detail::MAGIC_SIZE + detail::FILE_HEADER_SIZE << std::endl; + } + + void write_metadata() { + if (!m_metadata.empty()) { write_bytes(detail::MAGIC_SIZE + detail::FILE_HEADER_SIZE, m_metadata.size(), m_metadata.c_str()); + flush(); } } @@ -265,8 +302,10 @@ class XcpLogFileWriter { bool m_finalized{ false }; #ifdef __APPLE__ std::thread collector_thread{}; + std::thread flushing_thread{}; #else std::jthread collector_thread{}; + std::jthread flushing_thread{}; #endif std::mutex mtx; TsQueue my_queue; diff --git a/setup.py b/setup.py index ec075dc..5e929db 100644 --- a/setup.py +++ b/setup.py @@ -48,7 +48,7 @@ Pybind11Extension( EXT_NAMES[0], include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/recorder", "pyxcp/cpp_ext"], - sources=["pyxcp/recorder/lz4.c", "pyxcp/recorder/wrap.cpp"], + sources=["pyxcp/recorder/lz4.c", "pyxcp/recorder/lz4hc.c", "pyxcp/recorder/wrap.cpp"], define_macros=[("EXTENSION_NAME", EXT_NAMES[0]), ("NDEBUG", 1)], optional=False, cxx_std=20, From 86d109873c51a37d862fee76526ad830ec6346f4 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 3 Jun 2024 18:48:02 +0300 Subject: [PATCH 36/99] 64bit patch --- pyxcp/daq_stim/__init__.py | 4 -- pyxcp/examples/run_daq.py | 4 +- pyxcp/recorder/reader.hpp | 12 ++-- pyxcp/recorder/rekorder.hpp | 28 ++++----- pyxcp/recorder/unfolder.hpp | 118 ++++++++++++++++++------------------ pyxcp/recorder/wrap.cpp | 2 +- pyxcp/recorder/writer.hpp | 59 ++++-------------- 7 files changed, 94 insertions(+), 133 deletions(-) diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 0e469bf..e701550 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -165,16 +165,12 @@ def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 200 self.prealloc = prealloc self.chunk_size = chunk_size - def __del__(self): - print("DaqRecorder::__del__()") - def initialize(self): metadata = self.measurement_params.dumps() _DaqRecorderPolicy.create_writer(self, self.file_name, self.prealloc, self.chunk_size, metadata) _DaqRecorderPolicy.initialize(self) def finalize(self): - print("DaqRecorder::finalize()") _DaqRecorderPolicy.finalize(self) def start(self): diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 506555b..abc730d 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -136,8 +136,8 @@ daq_parser.setup() # Run internal and XCP setup procedures. print("start DAQ lists.") daq_parser.start() # Start DAQ lists. - # time.sleep(12 * 5.0) # Arbitrary termination condition.ls *. - time.sleep(6 * 60.0 * 60.0) # Arbitrary termination condition.ls *. + time.sleep(1 * 60.0 * 60.0) # Arbitrary termination condition.ls *. + # time.sleep(6 * 60.0 * 60.0) # Arbitrary termination condition.ls *. daq_parser.stop() # Stop DAQ lists. print("finalize DAQ lists.\n") x.disconnect() diff --git a/pyxcp/recorder/reader.hpp b/pyxcp/recorder/reader.hpp index d6ce3ec..9a6b939 100644 --- a/pyxcp/recorder/reader.hpp +++ b/pyxcp/recorder/reader.hpp @@ -77,7 +77,7 @@ class XcpLogFileReader { std::optional next_block() { auto container = ContainerHeaderType{}; auto frame = frame_header_t{}; - std::uint32_t boffs = 0; + std::uint64_t boffs = 0; auto result = FrameVector{}; payload_t payload; @@ -96,7 +96,7 @@ class XcpLogFileReader { throw std::runtime_error("LZ4 decompression failed."); } boffs = 0; - for (std::uint32_t idx = 0; idx < container.record_count; ++idx) { + for (std::uint64_t idx = 0; idx < container.record_count; ++idx) { _fcopy(reinterpret_cast(&frame), reinterpret_cast(&(buffer[boffs])), sizeof(frame_header_t)); boffs += sizeof(frame_header_t); result.emplace_back( @@ -117,11 +117,11 @@ class XcpLogFileReader { protected: - [[nodiscard]] blob_t const *ptr(std::uint32_t pos = 0) const { + [[nodiscard]] blob_t const *ptr(std::uint64_t pos = 0) const { return reinterpret_cast(m_mmap->data() + pos); } - void read_bytes(std::uint32_t pos, std::uint32_t count, blob_t *buf) const { + void read_bytes(std::uint64_t pos, std::uint64_t count, blob_t *buf) const { auto addr = reinterpret_cast(ptr(pos)); _fcopy(reinterpret_cast(buf), addr, count); } @@ -129,8 +129,8 @@ class XcpLogFileReader { private: std::string m_file_name; - std::uint32_t m_offset{ 0 }; - std::uint32_t m_current_container{ 0 }; + std::uint64_t m_offset{ 0 }; + std::uint64_t m_current_container{ 0 }; mio::mmap_source *m_mmap{ nullptr }; FileHeaderType m_header; std::string m_metadata; diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 051cc01..4b1e28e 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -80,15 +80,15 @@ struct FileHeaderType { std::uint16_t hdr_size; std::uint16_t version; std::uint16_t options; - std::uint32_t num_containers; - std::uint32_t record_count; - std::uint32_t size_compressed; - std::uint32_t size_uncompressed; + std::uint64_t num_containers; + std::uint64_t record_count; + std::uint64_t size_compressed; + std::uint64_t size_uncompressed; }; -using HeaderTuple = std::tuple; +using HeaderTuple = std::tuple; -static_assert(sizeof(FileHeaderType) == 22); +static_assert(sizeof(FileHeaderType) == 38); struct ContainerHeaderType { std::uint32_t record_count; @@ -138,22 +138,22 @@ constexpr auto FILE_HEADER_SIZE = sizeof(FileHeaderType); constexpr auto CONTAINER_SIZE = sizeof(ContainerHeaderType); } // namespace detail -constexpr auto file_header_size() -> std::uint32_t { +constexpr auto file_header_size() -> std::uint64_t { return (detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE); } -using rounding_func_t = std::function; +using rounding_func_t = std::function; -inline rounding_func_t create_rounding_func(std::uint32_t multiple) { - return [multiple](std::uint32_t value) { +inline rounding_func_t create_rounding_func(std::uint64_t multiple) { + return [multiple](std::uint64_t value) { return (value + (multiple - 1)) & ~(multiple - 1); }; } const auto round_to_alignment = create_rounding_func(__ALIGNMENT_REQUIREMENT); -inline void _fcopy(char* dest, char const * src, std::uint32_t n) noexcept { - for (std::uint32_t i = 0; i < n; ++i) { +inline void _fcopy(char* dest, char const * src, std::uint64_t n) noexcept { + for (std::uint64_t i = 0; i < n; ++i) { dest[i] = src[i]; } } @@ -163,13 +163,13 @@ inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { return payload.get(); } -inline payload_t create_payload(std::uint32_t size, blob_t const * data) noexcept { +inline payload_t create_payload(std::uint64_t size, blob_t const * data) noexcept { auto pl = std::make_shared(size); _fcopy(reinterpret_cast(pl.get()), reinterpret_cast(data), size); return pl; } #else -inline payload_t create_payload(std::uint32_t size, blob_t const * data) { +inline payload_t create_payload(std::uint64_t size, blob_t const * data) { return py::array_t(size, data); } diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index ca3275d..a45da26 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -20,18 +20,18 @@ using measurement_tuple_t = std::tuple)>; template -auto get_value(blob_t const * buf, std::uint32_t offset) -> Ty { +auto get_value(blob_t const * buf, std::uint64_t offset) -> Ty { return *reinterpret_cast(&buf[offset]); } template -auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> Ty { +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> Ty { return _bswap(get_value(buf, offset)); } #if HAS_FLOAT16==1 template<> -auto get_value(blob_t const * buf, std::uint32_t offset) -> std::float16_t { +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::float16_t { auto tmp = get_value(buf, offset); return *(reinterpret_cast(&tmp)); @@ -40,7 +40,7 @@ auto get_value(blob_t const * buf, std::uint32_t offset) -> std: #if HAS_BFLOAT16==1 template<> -auto get_value(blob_t const * buf, std::uint32_t offset) -> std::bfloat16_t { +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { auto tmp = get_value(buf, offset); return *(reinterpret_cast(&tmp)); @@ -49,14 +49,14 @@ auto get_value(blob_t const * buf, std::uint32_t offset) -> std template<> -auto get_value(blob_t const * buf, std::uint32_t offset) -> float { +auto get_value(blob_t const * buf, std::uint64_t offset) -> float { auto tmp = get_value(buf, offset); return *(reinterpret_cast(&tmp)); } template<> -auto get_value(blob_t const * buf, std::uint32_t offset) -> double { +auto get_value(blob_t const * buf, std::uint64_t offset) -> double { auto tmp = get_value(buf, offset); return *(reinterpret_cast(&tmp)); @@ -64,7 +64,7 @@ auto get_value(blob_t const * buf, std::uint32_t offset) -> double { #if HAS_FLOAT16==1 template<> -auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::float16_t { +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::float16_t { auto tmp = get_value_swapped(buf, offset); return *(reinterpret_cast(&tmp)); @@ -73,7 +73,7 @@ auto get_value_swapped(blob_t const * buf, std::uint32_t offset) #if HAS_BFLOAT16==1 template<> -auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::bfloat16_t { +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { auto tmp = get_value_swapped(buf, offset); return *(reinterpret_cast(&tmp)); @@ -82,46 +82,46 @@ auto get_value_swapped(blob_t const * buf, std::uint32_t offset template<> -auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> float { +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> float { auto tmp = get_value_swapped(buf, offset); return *(reinterpret_cast(&tmp)); } template<> -auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> double { +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> double { auto tmp = get_value_swapped(buf, offset); return *(reinterpret_cast(&tmp)); } template<> -auto get_value(blob_t const * buf, std::uint32_t offset) -> std::int16_t { +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::int16_t { return static_cast(get_value(buf, offset)); } template<> -auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::int16_t { +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::int16_t { return static_cast(get_value_swapped(buf, offset)); } template<> -auto get_value(blob_t const * buf, std::uint32_t offset) -> std::int32_t { +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::int32_t { return static_cast(get_value(buf, offset)); } template<> -auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::int32_t { +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::int32_t { return static_cast(get_value_swapped(buf, offset)); } template<> -auto get_value(blob_t const * buf, std::uint32_t offset) -> std::int64_t { +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::int64_t { return static_cast(get_value(buf, offset)); } template<> -auto get_value_swapped(blob_t const * buf, std::uint32_t offset) -> std::int64_t { +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::int64_t { return static_cast(get_value_swapped(buf, offset)); } @@ -129,96 +129,96 @@ auto get_value_swapped(blob_t const * buf, std::uint32_t offset) - ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// template -void set_value(blob_t * buf, std::uint32_t offset, Ty value) { +void set_value(blob_t * buf, std::uint64_t offset, Ty value) { ::memcpy(&buf[offset], &value, sizeof(Ty)); } template -void set_value_swapped(blob_t * buf, std::uint32_t offset, Ty value) { +void set_value_swapped(blob_t * buf, std::uint64_t offset, Ty value) { set_value(buf, offset, _bswap(value)); } template<> -void set_value(blob_t * buf, std::uint32_t offset, std::int8_t value) { +void set_value(blob_t * buf, std::uint64_t offset, std::int8_t value) { buf[offset] = static_cast(value); } template<> -void set_value(blob_t * buf, std::uint32_t offset, std::uint8_t value) { +void set_value(blob_t * buf, std::uint64_t offset, std::uint8_t value) { buf[offset] = static_cast(value); } template<> -void set_value(blob_t * buf, std::uint32_t offset, std::int16_t value) { +void set_value(blob_t * buf, std::uint64_t offset, std::int16_t value) { set_value(buf, offset, static_cast(value)); } template<> -void set_value_swapped(blob_t * buf, std::uint32_t offset, std::int16_t value) { +void set_value_swapped(blob_t * buf, std::uint64_t offset, std::int16_t value) { set_value_swapped(buf, offset, static_cast(value)); } template<> -void set_value(blob_t * buf, std::uint32_t offset, std::int32_t value) { +void set_value(blob_t * buf, std::uint64_t offset, std::int32_t value) { set_value(buf, offset, static_cast(value)); } template<> -void set_value_swapped(blob_t * buf, std::uint32_t offset, std::int32_t value) { +void set_value_swapped(blob_t * buf, std::uint64_t offset, std::int32_t value) { set_value_swapped(buf, offset, static_cast(value)); } template<> -void set_value(blob_t * buf, std::uint32_t offset, std::int64_t value) { +void set_value(blob_t * buf, std::uint64_t offset, std::int64_t value) { set_value(buf, offset, static_cast(value)); } template<> -void set_value_swapped(blob_t * buf, std::uint32_t offset, std::int64_t value) { +void set_value_swapped(blob_t * buf, std::uint64_t offset, std::int64_t value) { set_value_swapped(buf, offset, static_cast(value)); } #if HAS_FLOAT16==1 template<> -void set_value(blob_t * buf, std::uint32_t offset, std::float16_t value) { +void set_value(blob_t * buf, std::uint64_t offset, std::float16_t value) { set_value(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value_swapped(blob_t * buf, std::uint32_t offset, std::float16_t value) { +void set_value_swapped(blob_t * buf, std::uint64_t offset, std::float16_t value) { set_value_swapped(buf, offset, *reinterpret_cast(&value)); } #endif #if HAS_BFLOAT16==1 template<> -void set_value(blob_t * buf, std::uint32_t offset, std::bfloat16_t value) { +void set_value(blob_t * buf, std::uint64_t offset, std::bfloat16_t value) { set_value(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value_swapped(blob_t * buf, std::uint32_t offset, std::bfloat16_t value) { +void set_value_swapped(blob_t * buf, std::uint64_t offset, std::bfloat16_t value) { set_value_swapped(buf, offset, *reinterpret_cast(&value)); } #endif template<> -void set_value(blob_t * buf, std::uint32_t offset, float value) { +void set_value(blob_t * buf, std::uint64_t offset, float value) { set_value(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value_swapped(blob_t * buf, std::uint32_t offset, float value) { +void set_value_swapped(blob_t * buf, std::uint64_t offset, float value) { set_value_swapped(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value(blob_t * buf, std::uint32_t offset, double value) { +void set_value(blob_t * buf, std::uint64_t offset, double value) { set_value(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value_swapped(blob_t * buf, std::uint32_t offset, double value) { +void set_value_swapped(blob_t * buf, std::uint64_t offset, double value) { set_value_swapped(buf, offset, *reinterpret_cast(&value)); } @@ -353,21 +353,21 @@ struct Getter { std::uint8_t m_id_size; std::uint8_t m_ts_size; - std::function int8; - std::function uint8; - std::function int16; - std::function int32; - std::function int64; - std::function uint16; - std::function uint32; - std::function uint64; - std::function float_; - std::function double_; + std::function int8; + std::function uint8; + std::function int16; + std::function int32; + std::function int64; + std::function uint16; + std::function uint32; + std::function uint64; + std::function float_; + std::function double_; #if HAS_FLOAT16==1 - std::function float16; + std::function float16; #endif #if HAS_BFLOAT16==1 - std::function bfloat16; + std::function bfloat16; #endif std::vector m_first_pids; std::map> m_odt_to_daq_map; @@ -500,21 +500,21 @@ struct Setter { #endif std::uint8_t m_id_size; std::uint8_t m_ts_size; - std::function int8; - std::function uint8; - std::function int16; - std::function int32; - std::function int64; - std::function uint16; - std::function uint32; - std::function uint64; - std::function float_; - std::function double_; + std::function int8; + std::function uint8; + std::function int16; + std::function int32; + std::function int64; + std::function uint16; + std::function uint32; + std::function uint64; + std::function float_; + std::function double_; #if HAS_FLOAT16==1 - std::function float16; + std::function float16; #endif #if HAS_BFLOAT16==1 - std::function bfloat16; + std::function bfloat16; #endif std::map> m_odt_to_daq_map; }; @@ -1051,7 +1051,7 @@ class DaqRecorderPolicy : public DAQPolicyBase { m_writer->add_frame(frame_cat, counter, timestamp, static_cast(payload.size()), payload.c_str()); } - void create_writer(const std::string& file_name, std::uint32_t prealloc, std::uint32_t chunk_size, std::string_view metadata) { + void create_writer(const std::string& file_name, std::uint64_t prealloc, std::uint64_t chunk_size, std::string_view metadata) { m_writer = std::make_unique(file_name, prealloc, chunk_size, metadata); } diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index e20d830..24b1742 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -106,7 +106,7 @@ PYBIND11_MODULE(rekorder, m) { ; py::class_(m, "_PyXcpLogFileWriter") - .def(py::init(), + .def(py::init(), py::arg("filename"), py::arg("prealloc"), py::arg("chunk_size"), py::arg("metadata")="") .def("finalize", &XcpLogFileWriter::finalize) .def("add_frame", &XcpLogFileWriter::add_frame) diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 37c6817..a952a5d 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -29,7 +29,6 @@ class XcpLogFileWriter { m_metadata = metadata; if (!metadata.empty()) { - // std::cout << "XMRAW_HAS_METADATA: " << std::size(metadata) << std::endl; m_offset += std::size(metadata); write_metadata(); } @@ -47,9 +46,7 @@ class XcpLogFileWriter { void finalize() { std::error_code ec; - std::cout << "finalize?\n"; if (!m_finalized) { - std::cout << "\tYES!!!\n"; m_finalized = true; stop_thread(); if (m_container_record_count) { @@ -125,33 +122,6 @@ class XcpLogFileWriter { } } - void flush() { - std::chrono::time_point start_time = std::chrono::system_clock::now(); - - - #ifdef __APPLE__ - flushing_thread = std::thread([this]() { -#else - flushing_thread = std::jthread([this]() { -#endif - #if defined(_WIN32) - if (!FlushFileBuffers(m_fd)) { - std::cout << error_string("FlushFileBuffers", get_last_error()); - } - #else - if (fflush(m_fd); == -1) { - std::cout << error_string("fflush", get_last_error()); - } - #endif - }); - - std::chrono::time_point stop_time = std::chrono::system_clock::now(); - auto eta = std::chrono::duration_cast(stop_time - start_time); - - std::cout <<"[DEBUG] " << "Flushing took " << static_cast(eta.count()) / 1000000.0 << " seconds." << std::endl; - - } - blob_t *ptr(std::uint32_t pos = 0) const { return (blob_t *)(m_mmap->data() + pos); } @@ -190,7 +160,6 @@ class XcpLogFileWriter { detail::VERSION, m_metadata.empty() ? 0 : XMRAW_HAS_METADATA, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed ); - flush(); } container.record_count = m_container_record_count; container.size_compressed = cp_size; @@ -209,7 +178,7 @@ class XcpLogFileWriter { m_num_containers += 1; } - void write_bytes(std::uint32_t pos, std::uint32_t count, char const *buf) const { + void write_bytes(std::uint64_t pos, std::uint64_t count, char const *buf) const { auto addr = reinterpret_cast(ptr(pos)); _fcopy(addr, buf, count); @@ -234,7 +203,6 @@ class XcpLogFileWriter { void write_metadata() { if (!m_metadata.empty()) { write_bytes(detail::MAGIC_SIZE + detail::FILE_HEADER_SIZE, m_metadata.size(), m_metadata.c_str()); - flush(); } } @@ -265,7 +233,6 @@ class XcpLogFileWriter { compress_frames(); } } - std::cout << "Exiting collector_thread()\n"; }); return true; @@ -284,28 +251,26 @@ class XcpLogFileWriter { private: std::string m_file_name; - std::uint32_t m_offset{ 0 }; - std::uint32_t m_chunk_size{ 0 }; + std::uint64_t m_offset{ 0 }; + std::uint64_t m_chunk_size{ 0 }; std::string m_metadata; - std::uint32_t m_num_containers{ 0 }; - std::uint32_t m_record_count{ 0UL }; - std::uint32_t m_container_record_count{ 0UL }; - std::uint32_t m_total_size_uncompressed{ 0UL }; - std::uint32_t m_total_size_compressed{ 0UL }; - std::uint32_t m_container_size_uncompressed{ 0UL }; - std::uint32_t m_container_size_compressed{ 0UL }; + std::uint64_t m_num_containers{ 0 }; + std::uint64_t m_record_count{ 0UL }; + std::uint64_t m_container_record_count{ 0UL }; + std::uint64_t m_total_size_uncompressed{ 0UL }; + std::uint64_t m_total_size_compressed{ 0UL }; + std::uint64_t m_container_size_uncompressed{ 0UL }; + std::uint64_t m_container_size_compressed{ 0UL }; __ALIGN blob_t *m_intermediate_storage{ nullptr }; - std::uint32_t m_intermediate_storage_offset{ 0 }; - std::uint32_t m_hard_limit{0}; + std::uint64_t m_intermediate_storage_offset{ 0 }; + std::uint64_t m_hard_limit{0}; mio::file_handle_type m_fd{ INVALID_HANDLE_VALUE }; mio::mmap_sink *m_mmap{ nullptr }; bool m_finalized{ false }; #ifdef __APPLE__ std::thread collector_thread{}; - std::thread flushing_thread{}; #else std::jthread collector_thread{}; - std::jthread flushing_thread{}; #endif std::mutex mtx; TsQueue my_queue; From 7e68416e8c9fc9259d912b3ae6f05f1a7e8ea480 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 5 Jun 2024 18:38:18 +0300 Subject: [PATCH 37/99] today() --- pyxcp/recorder/writer.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index a952a5d..f76a183 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -152,7 +152,6 @@ class XcpLogFileWriter { // printf("comp: %d %d [%f]\n", m_intermediate_storage_offset, cp_size, double(m_intermediate_storage_offset) / // double(cp_size)); if (m_offset > (m_hard_limit >> 1)) { - //std::cout << "Offset " << m_offset << " execceds the hard limit of " << (m_hard_limit >> 1) << std::endl; std::cout <<"[INFO] " << std::chrono::system_clock::now() << ": Doubling measurement file size." << std::endl; m_hard_limit <<= 1; truncate(m_hard_limit, true); From 48a631205d1194f0b9ae149f5f14117834cc7035 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 5 Jun 2024 18:52:16 +0300 Subject: [PATCH 38/99] resolved --- docs/recorder.rst | 27 +++++++++++++++++++++++++++ pyxcp/examples/conf_can.toml | 4 +++- pyxcp/examples/conf_eth.toml | 9 ++++----- pyxcp/examples/xcp_read_benchmark.py | 4 +++- pyxcp/examples/xcphello.py | 6 ++++++ pyxcp/examples/xcphello_recorder.py | 2 +- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/docs/recorder.rst b/docs/recorder.rst index e69de29..704144a 100644 --- a/docs/recorder.rst +++ b/docs/recorder.rst @@ -0,0 +1,27 @@ + +Recorder +======== + + +:py:class:`pyxcp.recorder.XcpLogFileWriter` + +:py:meth:`pyxcp.recorder.XcpLogFileWritermymethod` + +Creating recipes +---------------- + +To retrieve a list of random ingredients, +you can use the ``lumache.get_random_ingredients()`` function: + +.. py:function:: lumache.get_random_ingredients(kind=None) + + Return a list of random ingredients as strings. + + :param kind: Optional "kind" of ingredients. + :type kind: list[str] or None + :return: The ingredients list. + :rtype: list[str] + +The ``kind`` parameter should be either ``"meat"``, ``"fish"``, +or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients` +will raise an exception. diff --git a/pyxcp/examples/conf_can.toml b/pyxcp/examples/conf_can.toml index 7578276..c8640c8 100644 --- a/pyxcp/examples/conf_can.toml +++ b/pyxcp/examples/conf_can.toml @@ -7,7 +7,9 @@ CAN_ID_MASTER = 257 CAN_ID_SLAVE = 258 CAN_ID_BROADCAST = 256 MAX_DLC_REQUIRED = false -BITRATE = 250000 +BITRATE = 50000 +DATA_BITRATE = 50000 +FD=false BTL_CYCLES = 16 SAMPLE_RATE = 1 SAMPLE_POINT = 87.5 diff --git a/pyxcp/examples/conf_eth.toml b/pyxcp/examples/conf_eth.toml index 65ff82e..bfd9caf 100644 --- a/pyxcp/examples/conf_eth.toml +++ b/pyxcp/examples/conf_eth.toml @@ -1,10 +1,9 @@ TRANSPORT = "ETH" -# TRANSPORT = "CAN" -CAN_DRIVER = "kvaser" -CHANNEL=0 -HOST = "localhost" +#HOST = "192.168.197.40" +#HOST = "192.168.198.175" +HOST="localhost" PORT = 5555 PROTOCOL = "UDP" IPV6 = false CREATE_DAQ_TIMESTAMPS = true -SEED_N_KEY_DLL = "SeedNKeyXcp.dll" +SEED_N_KEY_DLL="SeedNKeyXcp.dll" diff --git a/pyxcp/examples/xcp_read_benchmark.py b/pyxcp/examples/xcp_read_benchmark.py index fc08ce5..0eb7150 100644 --- a/pyxcp/examples/xcp_read_benchmark.py +++ b/pyxcp/examples/xcp_read_benchmark.py @@ -7,6 +7,7 @@ import seaborn as sns from pyxcp.cmdline import ArgumentParser +from pyxcp.transport import FrameRecorderPolicy sns.set() @@ -16,7 +17,8 @@ LENGTH = 0x1000 ITERATIONS = 100 -ap = ArgumentParser(description="pyXCP hello world.") +recorder_policy = FrameRecorderPolicy() # Create frame recorder. +ap = ArgumentParser(description="pyXCP hello world.", policy=recorder_policy) with ap.run() as x: xs = [] ys = [] diff --git a/pyxcp/examples/xcphello.py b/pyxcp/examples/xcphello.py index 9c02ae2..67b3c88 100644 --- a/pyxcp/examples/xcphello.py +++ b/pyxcp/examples/xcphello.py @@ -1,6 +1,7 @@ #!/usr/bin/env python """Very basic hello-world example. """ +import json from pprint import pprint from pyxcp.cmdline import ArgumentParser @@ -35,11 +36,16 @@ def callout(master, args): print("=================") print(f"ID: {identifier!r}") pprint(x.slaveProperties) + x.cond_unlock() cps = x.getCurrentProtectionStatus() print("\nProtection Status") print("=================") for k, v in cps.items(): print(f" {k:6s}: {v}") + daq = x.getDaqInfo() + pprint(daq) + with open("daq.json", "wt") as of: + json.dump(daq, of) if daq_info: dqp = x.getDaqProcessorInfo() print("\nDAQ Processor Info:") diff --git a/pyxcp/examples/xcphello_recorder.py b/pyxcp/examples/xcphello_recorder.py index f7be7db..9449536 100644 --- a/pyxcp/examples/xcphello_recorder.py +++ b/pyxcp/examples/xcphello_recorder.py @@ -80,7 +80,7 @@ def callout(master, args): print("=================") print(f"{x.getDaqListInfo(idx)}") x.disconnect() - +print("After recording...") ## ## Now read and dump recorded frames. ## From c32fbf58ff80f83149bf9ac7f7d5a3f746128440 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 5 Jun 2024 19:13:37 +0300 Subject: [PATCH 39/99] add xcp_recording.py --- pyxcp/examples/xcp_recording.py | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 pyxcp/examples/xcp_recording.py diff --git a/pyxcp/examples/xcp_recording.py b/pyxcp/examples/xcp_recording.py new file mode 100644 index 0000000..076c9e3 --- /dev/null +++ b/pyxcp/examples/xcp_recording.py @@ -0,0 +1,49 @@ +import argparse +import sys +from pprint import pprint + +from pyxcp.recorder import XcpLogFileReader, XcpLogFileUnfolder +from pyxcp.utils import hexDump + + +parser = argparse.ArgumentParser(description="Dump .xmraw files.") +parser.add_argument("xmraw_file", help=".xmraw file") +# parser.add_argument("-l", help = "loglevel [warn | info | error | debug]", dest = "loglevel", type = str, default = "warn") +args = parser.parse_args() + +print(args.xmraw_file) + + +class Unfolder(XcpLogFileUnfolder): + + def on_daq_list(self, daq_list_num, timestamp0, timestamp1, measurement): + print(daq_list_num, timestamp0, timestamp1, measurement) + + +lfr = Unfolder(args.xmraw_file) +print("-" * 80) +print(lfr.get_header()) +print("-" * 80) +lfr.run() +# print(lfr.parameters) +print("=" * 80) +print("=" * 80) +print("=" * 80) +print(lfr.daq_lists) +sys.exit() + +reader = XcpLogFileReader(args.xmraw_file) +hdr = reader.get_header() # Get file information. +print("\nRecording file header") +print("=====================\n") +pprint(hdr) + +print("\nRecorded frames") +print("===============\n") +print("CAT CTR TS PAYLOAD") +print("-" * 80) +for category, counter, timestamp, payload in reader: + print(f"{category.name:8} {counter:6} {timestamp:7.7f} {hexDump(payload)}") + # pass +print("-" * 80) +reader.reset_iter() # reader acts as an Python iterator -- can be reseted with this non-standard method. From 63c39b9c420038c15b92970d5f3a8748d4bdc7d9 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 2 Jun 2024 21:31:44 +0300 Subject: [PATCH 40/99] today --- pyxcp/transport/sxi.py | 87 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index fd27e8a..44dde2f 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- + import struct -from time import time +import threading +from array import array +from collections import deque +from time import sleep, time import serial @@ -9,6 +13,9 @@ from pyxcp.transport.base import BaseTransport +RECV_SIZE = 16384 + + class SxI(BaseTransport): """""" @@ -30,24 +37,30 @@ def __init__(self, config=None, policy=None): self.framing = self.config.framing self.esc_sync = self.config.esc_sync self.esc_esc = self.config.esc_esc - - def __del__(self): - self.closeConnection() - - def connect(self): self.logger.debug(f"Trying to open serial comm_port {self.port_name}.") try: - self.comm_port = serial.Serial() - self.comm_port.port = self.port_name - self.comm_port.baudrate = self.baudrate - self.comm_port.bytesize = self.bytesize - self.comm_port.parity = self.parity - self.comm_port.stopbits = self.stopbits - self.comm_port.timeout = self.timeout - self.comm_port.open() + self.comm_port = serial.Serial( + port=self.port_name, + baudrate=self.baudrate, + bytesize=self.bytesize, + parity=self.parity, + stopbits=self.stopbits, + timeout=self.timeout, + ) except serial.SerialException as e: self.logger.critical(f"{e}") raise + self._packet_listener = threading.Thread( + target=self._packet_listen, + args=(), + kwargs={}, + ) + self._packets = deque() + + def __del__(self): + self.closeConnection() + + def connect(self): self.logger.info(f"Serial comm_port openend as {self.comm_port.portstr}@{self.baudrate} Bits/Sec.") self.startListener() @@ -62,6 +75,50 @@ def output(self, enable): def flush(self): self.comm_port.flush() + def _packet_listen(self): + print("PACKET-LISTEN") + close_event_set = self.closeEvent.is_set + + _packets = self._packets + read = self.comm_port.read + + buffer = array("B", bytes(RECV_SIZE)) + buffer_view = memoryview(buffer) + + while True: + try: + print("Trying...") + if close_event_set(): + print("close_event_set()") + return + print("Enter reader...") + recv_timestamp = time() + sleep(0.1) + ra = self.comm_port.read_all() + print("*** READ-ALL", ra) + read_count = read(buffer, 10) # 100ms timeout + print("RC", read_count) + if read_count != RECV_SIZE: + _packets.append((buffer_view[:read_count].tobytes(), recv_timestamp)) + else: + _packets.append((buffer.tobytes(), recv_timestamp)) + # except (USBError, USBTimeoutError): + # print(format_exc()) + # sleep(SHORT_SLEEP) + continue + except BaseException: # noqa: B036 + # Note: catch-all only permitted if the intention is re-raising. + self.status = 0 # disconnected + break + + def startListener(self): + super().startListener() + print("*** START LISTENER ***") + if self._packet_listener.is_alive(): + self._packet_listener.join() + self._packet_listener = threading.Thread(target=self._packet_listen) + self._packet_listener.start() + def listen(self): while True: if self.closeEvent.is_set(): @@ -77,7 +134,7 @@ def listen(self): if len(response) != length: raise types.FrameSizeError("Size mismatch.") - + print("\t***RESP", response) self.processResponse(response, length, counter, recv_timestamp) def send(self, frame): From 79637fe05532db300c1cb09e00dd6124279037bb Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 5 Jun 2024 18:26:11 +0300 Subject: [PATCH 41/99] today() --- pyxcp/config/__init__.py | 21 ++++++--- pyxcp/examples/run_daq.py | 19 ++++++++ pyxcp/transport/can.py | 4 +- pyxcp/transport/eth.py | 18 ++++++++ pyxcp/transport/sxi.py | 97 ++++++++++++++++----------------------- 5 files changed, 94 insertions(+), 65 deletions(-) diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 54edc9f..b3fba83 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -27,9 +27,6 @@ from pyxcp.config import legacy -# warnings.simplefilter("always") - - class CanBase: has_fd = False has_bitrate = True @@ -708,8 +705,19 @@ class SxI(SingletonConfigurable): "HEADER_LEN_CTR_WORD", "HEADER_LEN_FILL_WORD", ], - default_value="HEADER_LEN_BYTE", - help="XCPonSxI header format.", + default_value="HEADER_LEN_CTR_WORD", + help="""XCPonSxI header format. +Number of bytes: + + LEN CTR FILL +______________________________________________________________ +HEADER_LEN_BYTE | 1 X X +HEADER_LEN_CTR_BYTE | 1 1 X +HEADER_LEN_FILL_BYTE | 1 X 1 +HEADER_LEN_WORD | 2 X X +HEADER_LEN_CTR_WORD | 2 2 X +HEADER_LEN_FILL_WORD | 2 X 2 +""", ).tag(config=True) tail_format = Enum( ["NO_CHECKSUM", "CHECKSUM_BYTE", "CHECKSUM_WORD"], default_value="NO_CHECKSUM", help="XCPonSxI tail format." @@ -808,7 +816,7 @@ def __init__(self, **kws): class General(SingletonConfigurable): """ """ - loglevel = Unicode("WARN", help="Set the log level by value or name.").tag(config=True) + loglevel = Unicode("INFO", help="Set the log level by value or name.").tag(config=True) disable_error_handling = Bool(False, help="Disable XCP error-handler for performance reasons.").tag(config=True) disconnect_response_optional = Bool(False, help="Ignore missing response on DISCONNECT request.").tag(config=True) seed_n_key_dll = Unicode("", allow_none=False, help="Dynamic library used for slave resource unlocking.").tag(config=True) @@ -868,6 +876,7 @@ def start(self): else: pyxcp.generate_config_file(sys.stdout) + class ProfileApp(Application): subcommands = Dict( dict( diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index abc730d..56fd255 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -121,6 +121,25 @@ ), ] +DAQ_LISTS = [ + DaqList( + "mkrzero_floats", + 0, + False, + True, + [ + ("floatCounter1", 0x6A77, 0, "F32"), + ("floatCounter2", 0x6A7B, 0, "F32"), + ("floatCounter3", 0x6A7F, 0, "F32"), + ("floatCounter4", 0x6A83, 0, "F32"), + ("floatCounter5", 0x6A87, 0, "F32"), + ("floatCounter6", 0x6A8B, 0, "F32"), + ("floatCounter7", 0x6A8F, 0, "F32"), + ("floatCounter8", 0x6A93, 0, "F32"), + ], + ), +] + # daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. daq_parser = DaqRecorder(DAQ_LISTS, "run_daq", 2) diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index 9b2b80d..dc4719a 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -321,8 +321,8 @@ def __init__(self, config, policy=None): self.interface_name = self.config.interface self.interface_configuration = detect_available_configs(interfaces=[self.interface_name]) parameters = self.get_interface_parameters() - self.logger.debug(f"Opening {self.interface_name!r} CAN-interface {list(parameters.items())}") - self.logger.debug( + self.logger.info(f"Opening {self.interface_name!r} CAN-interface {list(parameters.items())}") + self.logger.info( f"Master-ID (Tx): 0x{self.can_id_master.id:08X}{self.can_id_master.type_str} -- " f"Slave-ID (Rx): 0x{self.can_id_slave.id:08X}{self.can_id_slave.type_str}" ) diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 8f9c227..766e0a8 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -14,6 +14,23 @@ RECV_SIZE = 8196 +def socket_to_str(sock: socket.socket) -> str: + peer = sock.getpeername() + local = sock.getsockname() + AF = { + socket.AF_INET: "AF_INET", + socket.AF_INET6: "AF_INET6", + } + TYPE = { + socket.SOCK_DGRAM: "SOCK_DGRAM", + socket.SOCK_STREAM: "SOCK_STREAM", + } + family = AF.get(sock.family, "OTHER") + typ = TYPE.get(sock.type, "UNKNOWN") + res = f"Connected to: {peer[0]}:{peer[1]} local address: {local[0]}:{local[1]} [{family}][{typ}]" + return res + + class Eth(BaseTransport): """""" @@ -83,6 +100,7 @@ def __init__(self, config=None, policy=None): def connect(self): if self.status == 0: self.sock.connect(self.sockaddr) + self.logger.info(socket_to_str(self.sock)) self.startListener() self.status = 1 # connected diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index 44dde2f..473fb82 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -1,11 +1,9 @@ # -*- coding: utf-8 -*- - import struct -import threading -from array import array from collections import deque -from time import sleep, time +from dataclasses import dataclass +from time import time import serial @@ -13,16 +11,19 @@ from pyxcp.transport.base import BaseTransport +@dataclass +class HeaderValues: + length: int = 0 + counter: int = 0 + filler: int = 0 + + RECV_SIZE = 16384 class SxI(BaseTransport): """""" - MAX_DATAGRAM_SIZE = 512 - HEADER = struct.Struct(" Date: Thu, 6 Jun 2024 08:34:33 +0300 Subject: [PATCH 42/99] MacOS fixes --- pyxcp/recorder/rekorder.hpp | 2 +- pyxcp/recorder/unfolder.hpp | 27 ++++++++++++++------------- pyxcp/recorder/writer.hpp | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 4b1e28e..51aaa78 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -204,7 +204,7 @@ std::error_code get_last_error() { } #else -inline error_string(std::string_view func, std::error_code error_code) { +inline std::string error_string(std::string_view func, std::error_code error_code) { std::ostringstream ss; auto message = strerror(static_cast(error_code.value())); diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index a45da26..1c3f465 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -700,12 +700,12 @@ class Deserializer { stim = from_binary(); enable_timestamps = from_binary(); - odt_count = from_binary(); - total_entries = from_binary(); - total_length = from_binary(); + odt_count = from_binary(); // not used + total_entries = from_binary(); // not used + total_length = from_binary(); // not used std::size_t meas_size = from_binary(); - for (auto i = 0; i < meas_size; ++i) { + for (std::size_t i = 0; i < meas_size; ++i) { // name, address, ext, dt_name auto meas = create_mc_object(); measurements.push_back(meas); @@ -713,12 +713,12 @@ class Deserializer { } std::size_t meas_opt_size = from_binary(); - for (auto i = 0; i < meas_opt_size; ++i) { + for (std::size_t i = 0; i < meas_opt_size; ++i) { measurements_opt.emplace_back(create_bin()); } std::size_t hname_size = from_binary(); - for (auto i = 0; i < hname_size; ++i) { + for (std::size_t i = 0; i < hname_size; ++i) { auto header = from_binary(); header_names.push_back(header); } @@ -740,10 +740,10 @@ class Deserializer { flatten_odts_t odts; std::size_t odt_count = from_binary(); - for (auto i = 0; i < odt_count; ++i) { + for (std::size_t i = 0; i < odt_count; ++i) { std::vector> flatten_odt{}; std::size_t odt_entry_count = from_binary(); - for (auto j = 0; j < odt_entry_count; ++j) { + for (std::size_t j = 0; j < odt_entry_count; ++j) { name = from_binary(); address = from_binary(); ext = from_binary(); @@ -771,7 +771,7 @@ class Deserializer { ext = from_binary(); length = from_binary(); data_type = from_binary(); - type_index = from_binary(); + type_index = from_binary(); // not used std::size_t comp_size = from_binary(); for (auto i=0U; i < comp_size; i++) { components.push_back(create_mc_object()); @@ -1043,7 +1043,7 @@ class DaqRecorderPolicy : public DAQPolicyBase { DAQPolicyBase::set_parameters(params); } - void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) { + void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) override { if (frame_cat != static_cast(FrameCategory::DAQ)) { // Only record DAQ frames for now. return; @@ -1055,11 +1055,11 @@ class DaqRecorderPolicy : public DAQPolicyBase { m_writer = std::make_unique(file_name, prealloc, chunk_size, metadata); } - void initialize() { + void initialize() override { // TODO: Save meta-data. } - void finalize() { + void finalize() override { m_writer->finalize(); } @@ -1128,6 +1128,7 @@ class XcpLogFileUnfolder { } XcpLogFileUnfolder() = delete; + virtual ~XcpLogFileUnfolder() = default; void run() { @@ -1136,7 +1137,7 @@ class XcpLogFileUnfolder { std::string result; result.resize(length); - for (auto idx=0; idx < length; ++idx) { + for (std::size_t idx=0; idx < length; ++idx) { result[idx] = static_cast(in_str[idx]); } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index f76a183..f912789 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -152,7 +152,7 @@ class XcpLogFileWriter { // printf("comp: %d %d [%f]\n", m_intermediate_storage_offset, cp_size, double(m_intermediate_storage_offset) / // double(cp_size)); if (m_offset > (m_hard_limit >> 1)) { - std::cout <<"[INFO] " << std::chrono::system_clock::now() << ": Doubling measurement file size." << std::endl; + // std::cout <<"[INFO] " << std::chrono::system_clock::now() << ": Doubling measurement file size." << std::endl; m_hard_limit <<= 1; truncate(m_hard_limit, true); write_header( From 82c17e714f4d65888174533049c0d8daff9681d0 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 6 Jun 2024 08:48:28 +0300 Subject: [PATCH 43/99] today() --- pyxcp/cpp_ext/bin.hpp | 2 +- pyxcp/cpp_ext/blockmem.hpp | 1 - pyxcp/cpp_ext/daqlist.hpp | 57 ++- pyxcp/cpp_ext/extension_wrapper.cpp | 4 +- pyxcp/cpp_ext/helper.hpp | 75 ++-- pyxcp/cpp_ext/mcobject.hpp | 41 +- pyxcp/daq_stim/linreg.hpp | 160 ++++---- pyxcp/daq_stim/scheduler.cpp | 2 +- pyxcp/daq_stim/scheduler.hpp | 5 +- pyxcp/daq_stim/stim.cpp | 4 +- pyxcp/daq_stim/stim.hpp | 59 +-- pyxcp/daq_stim/stim_wrapper.cpp | 2 +- pyxcp/recorder/mio.hpp | 4 +- pyxcp/recorder/reader.hpp | 6 +- pyxcp/recorder/rekorder.hpp | 28 +- pyxcp/recorder/unfolder.hpp | 561 ++++++++++++++-------------- pyxcp/recorder/wrap.cpp | 154 ++++---- pyxcp/recorder/writer.hpp | 31 +- reformat.cmd | 1 + 19 files changed, 563 insertions(+), 634 deletions(-) create mode 100644 reformat.cmd diff --git a/pyxcp/cpp_ext/bin.hpp b/pyxcp/cpp_ext/bin.hpp index e94c864..b6745d4 100644 --- a/pyxcp/cpp_ext/bin.hpp +++ b/pyxcp/cpp_ext/bin.hpp @@ -26,7 +26,7 @@ class Bin { } void set_entries(std::vector&& entries) { - m_entries = std::move(entries); + m_entries = std::move(entries); } std::uint16_t get_size() const { diff --git a/pyxcp/cpp_ext/blockmem.hpp b/pyxcp/cpp_ext/blockmem.hpp index 2200b39..ac65671 100644 --- a/pyxcp/cpp_ext/blockmem.hpp +++ b/pyxcp/cpp_ext/blockmem.hpp @@ -55,5 +55,4 @@ class BlockMemory { std::mutex m_mtx; }; - #endif // __BLOCKMEM_HPP diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index 248f991..9832e8e 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -18,7 +18,7 @@ class DaqList { const std::vector& measurements ) : m_name(meas_name), m_event_num(event_num), m_stim(stim), m_enable_timestamps(enable_timestamps) { - //std::cout << "DAQ-List: " << meas_name << " " << event_num << " " << stim << " " << enable_timestamps << std::endl; + // std::cout << "DAQ-List: " << meas_name << " " << event_num << " " << stim << " " << enable_timestamps << std::endl; for (const auto& measurement : measurements) { auto const& [name, address, ext, dt_name] = measurement; m_measurements.emplace_back(McObject(name, address, static_cast(ext), 0, dt_name)); @@ -140,42 +140,39 @@ class DaqList { return ss.str(); } - - std::string to_string() const { + std::string to_string() const { std::stringstream ss; - ss << "DaqList("; - ss << "name=\"" << m_name << "\", "; - ss << "event_num=" << static_cast(m_event_num) << ", "; - ss << "stim=" << bool_to_string(m_stim) << ", "; - ss << "enable_timestamps" << bool_to_string(m_enable_timestamps) << ", "; - ss << "measurements=[\n"; - for (const auto& meas: m_measurements) { - ss << ::to_string(meas) << ",\n"; - } - ss << "],\n"; - ss << "measurements_opt=[\n"; - for (const auto& meas: m_measurements_opt) { - ss << ::to_string(meas) << ",\n"; - } - ss << "],\n"; - ss << "header_names=[\n"; - for (const auto& header: m_header_names) { - ss << "\"" << header << "\","; - } - ss << "\n]"; - - // using flatten_odts_t = std::vector>>; - ss << ")"; - return ss.str(); - } + ss << "DaqList("; + ss << "name=\"" << m_name << "\", "; + ss << "event_num=" << static_cast(m_event_num) << ", "; + ss << "stim=" << bool_to_string(m_stim) << ", "; + ss << "enable_timestamps" << bool_to_string(m_enable_timestamps) << ", "; + ss << "measurements=[\n"; + for (const auto& meas : m_measurements) { + ss << ::to_string(meas) << ",\n"; + } + ss << "],\n"; + ss << "measurements_opt=[\n"; + for (const auto& meas : m_measurements_opt) { + ss << ::to_string(meas) << ",\n"; + } + ss << "],\n"; + ss << "header_names=[\n"; + for (const auto& header : m_header_names) { + ss << "\"" << header << "\","; + } + ss << "\n]"; + // using flatten_odts_t = std::vector>>; + ss << ")"; + return ss.str(); + } static void loads(std::string_view buffer) { - } - private: std::string m_name; diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index 16c1f5c..c906169 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -50,9 +50,7 @@ PYBIND11_MODULE(cpp_ext, m) { py::init&>(), "name"_a, "event_num"_a, "stim"_a, "enable_timestamps"_a, "measurements"_a ) - .def("__repr__", [](const DaqList& self) { - return self.to_string(); - }) + .def("__repr__", [](const DaqList& self) { return self.to_string(); }) .def_property("name", &DaqList::get_name, nullptr) .def_property("event_num", &DaqList::get_event_num, nullptr) .def_property("stim", &DaqList::get_stim, nullptr) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 5f638b2..6c7d373 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -1,37 +1,34 @@ #if !defined(__HELPER_HPP) -#define __HELPER_HPP + #define __HELPER_HPP -#include -#include + #include + #include -#if __cplusplus >= 202302L - #include + #if __cplusplus >= 202302L + #include - #if defined(__STDCPP_BFLOAT16_T__) - #define HAS_BFLOAT16 (1) - #else - #define HAS_BFLOAT16 (0) - #endif + #if defined(__STDCPP_BFLOAT16_T__) + #define HAS_BFLOAT16 (1) + #else + #define HAS_BFLOAT16 (0) + #endif - #if defined(__STDCPP_FLOAT16_T__) - #define HAS_FLOAT16 (1) + #if defined(__STDCPP_FLOAT16_T__) + #define HAS_FLOAT16 (1) + #else + #define HAS_FLOAT16 (0) + #endif #else - #define HAS_FLOAT16 (0) + #define HAS_FLOAT16 (0) + #define HAS_BFLOAT16 (0) #endif -#else - #define HAS_FLOAT16 (0) - #define HAS_BFLOAT16 (0) -#endif - -template -constexpr void DBG_PRINTN(Args&&... args) noexcept -{ +template +constexpr void DBG_PRINTN(Args &&...args) noexcept { ((std::cout << std::forward(args) << " "), ...); } - // NOTE: C++23 has std::byteswap() constexpr auto _bswap(std::uint64_t v) noexcept { return ((v & UINT64_C(0x0000'0000'0000'00FF)) << 56) | ((v & UINT64_C(0x0000'0000'0000'FF00)) << 40) | @@ -49,13 +46,12 @@ constexpr auto _bswap(std::uint16_t v) noexcept { return ((v & UINT16_C(0x00FF)) << 8) | ((v & UINT16_C(0xFF00)) >> 8); } - template -inline std::string to_binary(const T& value) { +inline std::string to_binary(const T &value) { std::string result; auto ptr = reinterpret_cast(&value); - for (std::size_t idx=0; idx -inline std::string to_binary(const std::string& value) { +inline std::string to_binary(const std::string &value) { std::string result; - auto ptr = reinterpret_cast(value.c_str()); + auto ptr = reinterpret_cast(value.c_str()); std::size_t length = std::size(value); // We are using Pascal strings as serialization format. auto len_bin = to_binary(length); std::copy(len_bin.begin(), len_bin.end(), std::back_inserter(result)); - for (std::size_t idx=0; idx"; - } - return ""; + switch (value) { + case 0: + return "INTEL"; + case 1: + return "MOTOROLA"; + default: + return ""; + } + return ""; } -#endif // __HELPER_HPP +#endif // __HELPER_HPP diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index d8f94b2..5866f3e 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -6,29 +6,28 @@ #include #include #include - #include + #include #include #include "helper.hpp" const std::map> TYPE_MAP = { - { "U8", { 0, 1 }}, - { "I8", { 1, 1 }}, - { "U16", { 2, 2 }}, - { "I16", { 3, 2 }}, - { "U32", { 4, 4 }}, - { "I32", { 5, 4 }}, - { "U64", { 6, 8 }}, - { "I64", { 7, 8 }}, - { "F32", { 8, 4 }}, - { "F64", { 9, 8 }}, -#if HAS_FLOAT16 - { "F16", { 10, 2 }}, -#endif -#if HAS_BFLOAT16 - { "BF16", { 11, 2 }}, -#endif - + { "U8", { 0, 1 } }, + { "I8", { 1, 1 } }, + { "U16", { 2, 2 } }, + { "I16", { 3, 2 } }, + { "U32", { 4, 4 } }, + { "I32", { 5, 4 } }, + { "U64", { 6, 8 } }, + { "I64", { 7, 8 } }, + { "F32", { 8, 4 } }, + { "F64", { 9, 8 } }, + #if HAS_FLOAT16 + { "F16", { 10, 2 } }, + #endif + #if HAS_BFLOAT16 + { "BF16", { 11, 2 } }, + #endif }; class McObject { @@ -46,11 +45,13 @@ class McObject { m_type_index(-1), m_components(components) { if (data_type != "") { - std::string dt_toupper; + std::string dt_toupper; dt_toupper.resize(data_type.size()); - std::transform(data_type.begin(), data_type.end(), dt_toupper.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c); }); + std::transform(data_type.begin(), data_type.end(), dt_toupper.begin(), [](unsigned char c) -> unsigned char { + return std::toupper(c); + }); if (!TYPE_MAP.contains(dt_toupper)) { throw std::runtime_error("Invalid data type: " + data_type); diff --git a/pyxcp/daq_stim/linreg.hpp b/pyxcp/daq_stim/linreg.hpp index 583b6d4..a2f5005 100644 --- a/pyxcp/daq_stim/linreg.hpp +++ b/pyxcp/daq_stim/linreg.hpp @@ -5,13 +5,12 @@ #ifndef PYXCP_LINREG_HPP #define PYXCP_LINREG_HPP +#include +#include #include #include #include -#include -#include - #if 0 #include "linreg.h" #include "print.h" @@ -33,19 +32,16 @@ #define ERR_EQUALS 1.05 struct servo { - double max_frequency; - double step_threshold; - double first_step_threshold; - int first_update; + double max_frequency; + double step_threshold; + double first_step_threshold; + int first_update; int64_t offset_threshold; - int num_offset_values; - int curr_offset_values; + int num_offset_values; + int curr_offset_values; void (*destroy)(struct servo *servo); -f - double (*sample)(struct servo *servo, - int64_t offset, uint64_t local_ts, double weight, - enum servo_state *state); + f double (*sample)(struct servo *servo, int64_t offset, uint64_t local_ts, double weight, enum servo_state *state); void (*sync_interval)(struct servo *servo, double interval); @@ -56,12 +52,11 @@ f void (*leap)(struct servo *servo, int leap); }; - /* Uncorrected local time vs remote time */ struct point { uint64_t x; uint64_t y; - double w; + double w; }; struct result { @@ -104,23 +99,17 @@ struct linreg_servo { class LinearRegression { public: - - private: - }; - -static void linreg_destroy(struct servo *servo) -{ +static void linreg_destroy(struct servo *servo) { struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); free(s); } -static void move_reference(struct linreg_servo *s, int64_t x, int64_t y) -{ +static void move_reference(struct linreg_servo *s, int64_t x, int64_t y) { struct result *res; - unsigned int i; + unsigned int i; s->reference.x += x; s->reference.y += y; @@ -132,9 +121,8 @@ static void move_reference(struct linreg_servo *s, int64_t x, int64_t y) } } -static void update_reference(struct linreg_servo *s, uint64_t local_ts) -{ - double x_interval; +static void update_reference(struct linreg_servo *s, uint64_t local_ts) { + double x_interval; int64_t y_interval; if (s->last_update) { @@ -151,8 +139,7 @@ static void update_reference(struct linreg_servo *s, uint64_t local_ts) s->last_update = local_ts; } -static void add_sample(struct linreg_servo *s, int64_t offset, double weight) -{ +static void add_sample(struct linreg_servo *s, int64_t offset, double weight) { s->last_point = (s->last_point + 1) % MAX_POINTS; s->points[s->last_point].x = s->reference.x; @@ -163,14 +150,14 @@ static void add_sample(struct linreg_servo *s, int64_t offset, double weight) s->num_points++; } -static void regress(struct linreg_servo *s) -{ - double x, y, y0, e, x_sum, y_sum, xy_sum, x2_sum, w, w_sum; - unsigned int i, l, n, size; +static void regress(struct linreg_servo *s) { + double x, y, y0, e, x_sum, y_sum, xy_sum, x2_sum, w, w_sum; + unsigned int i, l, n, size; struct result *res; - x_sum = 0.0, y_sum = 0.0, xy_sum = 0.0, x2_sum = 0.0; w_sum = 0.0; - i = 0; + x_sum = 0.0, y_sum = 0.0, xy_sum = 0.0, x2_sum = 0.0; + w_sum = 0.0; + i = 0; y0 = (int64_t)(s->points[s->last_point].y - s->reference.y); @@ -211,52 +198,43 @@ static void regress(struct linreg_servo *s) } /* Get new intercept and slope */ - res->slope = (xy_sum - x_sum * y_sum / w_sum) / - (x2_sum - x_sum * x_sum / w_sum); + res->slope = (xy_sum - x_sum * y_sum / w_sum) / (x2_sum - x_sum * x_sum / w_sum); res->intercept = (y_sum - res->slope * x_sum) / w_sum; } } -static void update_size(struct linreg_servo *s) -{ +static void update_size(struct linreg_servo *s) { struct result *res; - double best_err; - int size, best_size; + double best_err; + int size, best_size; /* Find largest size with smallest prediction error */ best_size = 0; - best_err = 0.0; + best_err = 0.0; for (size = MIN_SIZE; size <= MAX_SIZE; size++) { res = &s->results[size - MIN_SIZE]; - if ((!best_size && res->slope) || - (best_err * ERR_EQUALS > res->err && - res->err_updates >= ERR_INITIAL_UPDATES)) { + if ((!best_size && res->slope) || (best_err * ERR_EQUALS > res->err && res->err_updates >= ERR_INITIAL_UPDATES)) { best_size = size; - best_err = res->err; + best_err = res->err; } } s->size = best_size; } -static double linreg_sample(struct servo *servo, - int64_t offset, - uint64_t local_ts, - double weight, - enum servo_state *state) -{ +static double linreg_sample(struct servo *servo, int64_t offset, uint64_t local_ts, double weight, enum servo_state *state) { struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); - struct result *res; - int corr_interval; + struct result *res; + int corr_interval; /* - * The current time and the time when will be the frequency of the - * clock actually updated is assumed here to be equal to local_ts - * (which is the time stamp of the received sync message). As long as - * the differences are smaller than the update interval, the loop - * should be robust enough to handle this simplification. + * The current time and the time when will be the frequency of the + * clock actually updated is assumed here to be equal to local_ts + * (which is the time stamp of the received sync message). As long as + * the differences are smaller than the update interval, the loop + * should be robust enough to handle this simplification. */ update_reference(s, local_ts); @@ -273,14 +251,10 @@ static double linreg_sample(struct servo *servo, res = &s->results[s->size - MIN_SIZE]; - pr_debug("linreg: points %d slope %.9f intercept %.0f err %.0f", - 1 << s->size, res->slope, res->intercept, res->err); + pr_debug("linreg: points %d slope %.9f intercept %.0f err %.0f", 1 << s->size, res->slope, res->intercept, res->err); - if ((servo->first_update && - servo->first_step_threshold && - servo->first_step_threshold < fabs(res->intercept)) || - (servo->step_threshold && - servo->step_threshold < fabs(res->intercept))) { + if ((servo->first_update && servo->first_step_threshold && servo->first_step_threshold < fabs(res->intercept)) || + (servo->step_threshold && servo->step_threshold < fabs(res->intercept))) { /* The clock will be stepped by offset */ move_reference(s, 0, -offset); s->last_update -= offset; @@ -293,12 +267,12 @@ static double linreg_sample(struct servo *servo, s->clock_freq = 1e9 * (res->slope - 1.0); /* - * Adjust the frequency to correct the time offset. Use longer - * correction interval with larger sizes to reduce the frequency error. - * The update interval is assumed to be not affected by the frequency - * adjustment. If it is (e.g. phc2sys controlling the system clock), a - * correction slowing down the clock will result in an overshoot. With - * the system clock's maximum adjustment of 10% that's acceptable. + * Adjust the frequency to correct the time offset. Use longer + * correction interval with larger sizes to reduce the frequency error. + * The update interval is assumed to be not affected by the frequency + * adjustment. If it is (e.g. phc2sys controlling the system clock), a + * correction slowing down the clock will result in an overshoot. With + * the system clock's maximum adjustment of 10% that's acceptable. */ corr_interval = s->size <= 4 ? 1 : s->size / 2; s->clock_freq += res->intercept / s->update_interval / corr_interval; @@ -314,43 +288,39 @@ static double linreg_sample(struct servo *servo, return -s->clock_freq; } -static void linreg_sync_interval(struct servo *servo, double interval) -{ +static void linreg_sync_interval(struct servo *servo, double interval) { struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); s->update_interval = interval; } -static void linreg_reset(struct servo *servo) -{ +static void linreg_reset(struct servo *servo) { struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); - unsigned int i; + unsigned int i; - s->num_points = 0; - s->last_update = 0; - s->size = 0; + s->num_points = 0; + s->last_update = 0; + s->size = 0; s->frequency_ratio = 1.0; for (i = MIN_SIZE; i <= MAX_SIZE; i++) { - s->results[i - MIN_SIZE].slope = 0.0; + s->results[i - MIN_SIZE].slope = 0.0; s->results[i - MIN_SIZE].err_updates = 0; } } -static double linreg_rate_ratio(struct servo *servo) -{ +static double linreg_rate_ratio(struct servo *servo) { struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); return s->frequency_ratio; } -static void linreg_leap(struct servo *servo, int leap) -{ +static void linreg_leap(struct servo *servo, int leap) { struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); /* - * Move reference when leap second is applied to the reference - * time as if the clock was stepped in the opposite direction + * Move reference when leap second is applied to the reference + * time as if the clock was stepped in the opposite direction */ if (s->leap && !leap) move_reference(s, 0, s->leap * 1000000000); @@ -358,26 +328,24 @@ static void linreg_leap(struct servo *servo, int leap) s->leap = leap; } -struct servo *linreg_servo_create(double fadj) -{ +struct servo *linreg_servo_create(double fadj) { struct linreg_servo *s; s = calloc(1, sizeof(*s)); if (!s) return NULL; - s->servo.destroy = linreg_destroy; - s->servo.sample = linreg_sample; + s->servo.destroy = linreg_destroy; + s->servo.sample = linreg_sample; s->servo.sync_interval = linreg_sync_interval; - s->servo.reset = linreg_reset; - s->servo.rate_ratio = linreg_rate_ratio; - s->servo.leap = linreg_leap; + s->servo.reset = linreg_reset; + s->servo.rate_ratio = linreg_rate_ratio; + s->servo.leap = linreg_leap; - s->clock_freq = -fadj; + s->clock_freq = -fadj; s->frequency_ratio = 1.0; return &s->servo; } - #endif // PYXCP_LINREG_HPP diff --git a/pyxcp/daq_stim/scheduler.cpp b/pyxcp/daq_stim/scheduler.cpp index 439d701..befe218 100644 --- a/pyxcp/daq_stim/scheduler.cpp +++ b/pyxcp/daq_stim/scheduler.cpp @@ -18,7 +18,7 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) { } } -#include + #include void mul4_vectorized(float* ptr) { __m128 f = _mm_loadu_ps(ptr); diff --git a/pyxcp/daq_stim/scheduler.hpp b/pyxcp/daq_stim/scheduler.hpp index 662c798..5a4fc00 100644 --- a/pyxcp/daq_stim/scheduler.hpp +++ b/pyxcp/daq_stim/scheduler.hpp @@ -10,9 +10,9 @@ #include #if defined(_WIN32) -#include + #include -#include + #include VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired); @@ -68,7 +68,6 @@ struct Scheduler { struct Scheduler { Scheduler() = default; ~Scheduler() = default; - }; #endif diff --git a/pyxcp/daq_stim/stim.cpp b/pyxcp/daq_stim/stim.cpp index 1eeec71..ca6e862 100644 --- a/pyxcp/daq_stim/stim.cpp +++ b/pyxcp/daq_stim/stim.cpp @@ -1,7 +1,7 @@ #if defined(_MSC_VER) -#pragma comment(lib, "Winmm.lib") -#pragma comment(lib, "Avrt.lib") + #pragma comment(lib, "Winmm.lib") + #pragma comment(lib, "Avrt.lib") #endif #include "stim.hpp" diff --git a/pyxcp/daq_stim/stim.hpp b/pyxcp/daq_stim/stim.hpp index d6675e6..1bfe015 100644 --- a/pyxcp/daq_stim/stim.hpp +++ b/pyxcp/daq_stim/stim.hpp @@ -2,6 +2,8 @@ #if !defined(__STIM_HPP) #define __STIM_HPP + #include + #include #include #include @@ -16,15 +18,13 @@ #include #include - #include - - #include "scheduler.hpp" #include "helper.hpp" + #include "scheduler.hpp" -#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64) ) - #include - #include -#endif + #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64)) + #include + #include + #endif namespace py = pybind11; @@ -173,7 +173,6 @@ class FakeEnum { return py::bytes(ss.str()); } - private: std::uint8_t m_value; @@ -211,6 +210,7 @@ struct DaqEventInfo { } public: + std::string m_name; std::int8_t m_unit_exp; std::size_t m_cycle; @@ -275,7 +275,7 @@ class Stim { using send_function_t = std::function)>; explicit Stim(bool activate = true) : m_activate(activate) { -#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64) ) + #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64)) if (timeBeginPeriod(100) == TIMERR_NOERROR) { // std::cout << "timeBeginPeriod() OK!!!" << std::endl; } else { @@ -287,9 +287,8 @@ class Stim { auto xxx = AvSetMmThreadCharacteristics("Pro Audio", &task_index); auto start = timeGetTime(); -#endif + #endif // m_scheduler.start_thread(); - } void setParameters(const StimParameters& params) { @@ -322,7 +321,7 @@ class Stim { return; } m_daq_ptr = { daqListNumber, odtNumber, odtEntryNumber }; - //DBG_PRINTN("SET_DAQ_PTR -- daq:", daqListNumber, " odt:", odtNumber, " entry:", odtEntryNumber, std::endl); + // DBG_PRINTN("SET_DAQ_PTR -- daq:", daqListNumber, " odt:", odtNumber, " entry:", odtEntryNumber, std::endl); } void clearDaqList(std::uint16_t daqListNumber) { @@ -334,7 +333,7 @@ class Stim { return; } auto entry = m_daq_lists[daqListNumber]; - //DBG_PRINTN("CLEAR_DAQ_LIST -- daq:", daqListNumber, std::endl); + // DBG_PRINTN("CLEAR_DAQ_LIST -- daq:", daqListNumber, std::endl); entry.clear(); } @@ -346,18 +345,19 @@ class Stim { auto [d, o, e] = m_daq_ptr; auto& entry = m_daq_lists[d].m_odts[o].m_entries[e]; - //DBG_PRINTN("WRITE_DAQ -- bit-offset:", bitOffset, " size:", entrySize, " addr-ext:", addressExt," addr:", address, std::endl); + // DBG_PRINTN("WRITE_DAQ -- bit-offset:", bitOffset, " size:", entrySize, " addr-ext:", addressExt," addr:", address, + // std::endl); entry.bitOffset = bitOffset; entry.address = address; entry.address_extension = addressExt; entry.entry_size = entrySize; -#if 0 + #if 0 std::cout << "\tBO: " << entry.bitOffset << std::endl; std::cout << "\tES: " << entry.entry_size << std::endl; std::cout << "\tAD: " << entry.address << std::endl; std::cout << "\tAE: " << entry.address_extension << std::endl; -#endif + #endif } void setDaqListMode( @@ -370,10 +370,11 @@ class Stim { if (!validateEntryNumber(daqListNumber)) { throw std::runtime_error("Invalid DAQ list number: " + std::to_string(eventChannelNumber) + "\n"); } - //std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << ":" - // << priority << std::endl; + // std::cout << "SET_DAQ_LIST_MODE: " << mode << ":" << daqListNumber << ":" << eventChannelNumber << ":" << prescaler << + // ":" + // << priority << std::endl; - auto& entry = m_daq_lists.at(daqListNumber); + auto& entry = m_daq_lists.at(daqListNumber); entry.mode = mode; entry.prescaler = prescaler; @@ -390,11 +391,12 @@ class Stim { event.m_daq_lists.emplace(daqListNumber); if ((mode & DIRECTION_STIM) == DIRECTION_STIM) { - //std::cout << "\tSTIM-MODE!!!\n"; + // std::cout << "\tSTIM-MODE!!!\n"; m_stim_lists.emplace(daqListNumber); - //std::cout << "\t\tEvent: " << event.m_name << " ==> " << event.m_cycle_time << " - " << event.m_periodic << std::endl; + // std::cout << "\t\tEvent: " << event.m_name << " ==> " << event.m_cycle_time << " - " << event.m_periodic << + // std::endl; calculate_scheduler_period(event.m_cycle_time); } } @@ -420,23 +422,24 @@ class Stim { entry.mode |= (SELECTED); } - //std::cout << "START_STOP_DAQ_LIST " << mode << ":" << daqListNumber << std::endl; + // std::cout << "START_STOP_DAQ_LIST " << mode << ":" << daqListNumber << std::endl; } void startStopSynch(std::uint16_t mode) { if (!m_activate) { return; } - //std::cout << "START_STOP_SYNCH " << mode << ":" << std::endl; + // std::cout << "START_STOP_SYNCH " << mode << ":" << std::endl; -// send(0x00, { 0x04, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }); + // send(0x00, { 0x04, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }); for (auto dl : m_stim_lists) { - //std::cout << "\tRunning list: " << dl << std::endl; + // std::cout << "\tRunning list: " << dl << std::endl; } auto idx = 0; for (auto dl : m_daq_lists) { - //std::cout << "DAQ-L: " << idx << " " << dl.mode << " - " << dl.event_channel_number << ":" << dl.prescaler << std::endl; + // std::cout << "DAQ-L: " << idx << " " << dl.mode << " - " << dl.event_channel_number << ":" << dl.prescaler << + // std::endl; idx++; } } @@ -549,7 +552,8 @@ class Stim { m_scheduler_max_value = value; } - // std::cout << "\tSCHED_Value: " << value << " - BEFORE: " << *m_scheduler_period << ":" << *m_scheduler_max_value << std::endl; + // std::cout << "\tSCHED_Value: " << value << " - BEFORE: " << *m_scheduler_period << ":" << *m_scheduler_max_value << + // std::endl; m_scheduler_period = std::gcd(*m_scheduler_period, value); m_scheduler_max_value = std::lcm(*m_scheduler_max_value, value); // std::cout << "\tSCHED_Period: " << *m_scheduler_period << " max: " << *m_scheduler_max_value << std::endl; @@ -581,6 +585,7 @@ class Stim { } private: + bool m_activate; StimParameters m_params{}; std::vector m_daq_lists{}; diff --git a/pyxcp/daq_stim/stim_wrapper.cpp b/pyxcp/daq_stim/stim_wrapper.cpp index 046b805..6938ab9 100644 --- a/pyxcp/daq_stim/stim_wrapper.cpp +++ b/pyxcp/daq_stim/stim_wrapper.cpp @@ -8,7 +8,7 @@ namespace py = pybind11; using namespace py::literals; #if defined(_MSC_VER) -#pragma warning(disable: 4251 4273) + #pragma warning(disable: 4251 4273) #endif #include "stim.hpp" diff --git a/pyxcp/recorder/mio.hpp b/pyxcp/recorder/mio.hpp index 500ed67..31fa49f 100644 --- a/pyxcp/recorder/mio.hpp +++ b/pyxcp/recorder/mio.hpp @@ -816,7 +816,7 @@ namespace detail { ); } - } // namespace win + } // namespace win #endif // _WIN32 /** @@ -843,7 +843,7 @@ namespace detail { #ifdef _WIN32 const auto handle = win::open_file_helper(path, mode); #else // POSIX - const auto handle = ::open(c_str(path), mode == access_mode::read ? O_RDONLY : O_RDWR); + const auto handle = ::open(c_str(path), mode == access_mode::read ? O_RDONLY : O_RDWR); #endif if (handle == invalid_handle) { error = detail::last_error(); diff --git a/pyxcp/recorder/reader.hpp b/pyxcp/recorder/reader.hpp index 9a6b939..63b527e 100644 --- a/pyxcp/recorder/reader.hpp +++ b/pyxcp/recorder/reader.hpp @@ -42,7 +42,7 @@ class XcpLogFileReader { if ((m_header.options & XMRAW_HAS_METADATA) == XMRAW_HAS_METADATA) { std::size_t metadata_length = 0; - std::size_t data_start = m_offset + sizeof(std::size_t); + std::size_t data_start = m_offset + sizeof(std::size_t); read_bytes(m_offset, sizeof(std::size_t), reinterpret_cast(&metadata_length)); @@ -59,8 +59,8 @@ class XcpLogFileReader { [[nodiscard]] auto get_header_as_tuple() const noexcept -> HeaderTuple { auto hdr = get_header(); - return std::make_tuple(hdr.version, hdr.options, - hdr.num_containers, hdr.record_count, hdr.size_uncompressed, hdr.size_compressed, + return std::make_tuple( + hdr.version, hdr.options, hdr.num_containers, hdr.record_count, hdr.size_uncompressed, hdr.size_compressed, (double)((std::uint64_t)(((double)hdr.size_uncompressed / (double)hdr.size_compressed * 100.0) + 0.5)) / 100.0 ); } diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 51aaa78..8fe9f6c 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -20,9 +20,9 @@ #include #include #include + #include #include #include - #include #include #include #include @@ -57,7 +57,6 @@ using namespace pybind11::literals; #define __ALIGNMENT_REQUIREMENT __BIGGEST_ALIGNMENT__ #define __ALIGN alignas(__ALIGNMENT_REQUIREMENT) - constexpr auto kilobytes(std::uint32_t value) -> std::uint32_t { return value * 1024; } @@ -108,7 +107,7 @@ using payload_t = py::array_t; struct frame_header_t { std::uint8_t category{ 0 }; std::uint16_t counter{ 0 }; - double timestamp{ 0.0 }; + double timestamp{ 0.0 }; std::uint16_t length{ 0 }; }; @@ -180,15 +179,16 @@ inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { } #endif /* STANDALONE_REKORDER */ -//////////////////////////////////// -#ifdef _WIN32 -inline std::string error_string(std::string_view func, std::error_code error_code) -{ - LPSTR messageBuffer = nullptr; + //////////////////////////////////// + #ifdef _WIN32 +inline std::string error_string(std::string_view func, std::error_code error_code) { + LPSTR messageBuffer = nullptr; std::ostringstream ss; - size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, static_cast(error_code.value()), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + size_t size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + static_cast(error_code.value()), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL + ); std::string message(messageBuffer, size); LocalFree(messageBuffer); @@ -203,7 +203,7 @@ std::error_code get_last_error() { return std::error_code(GetLastError(), std::system_category()); } -#else + #else inline std::string error_string(std::string_view func, std::error_code error_code) { std::ostringstream ss; @@ -213,18 +213,15 @@ inline std::string error_string(std::string_view func, std::error_code error_cod ss << func << ": "; ss << message; return ss.str(); - } std::error_code get_last_error() { return std::error_code(errno, std::system_category()); } - -#endif // _WIN32 + #endif // _WIN32 //////////////////////////////////// - inline void hexdump(blob_t const * buf, std::uint16_t sz) { for (std::uint16_t idx = 0; idx < sz; ++idx) { printf("%02X ", buf[idx]); @@ -232,7 +229,6 @@ inline void hexdump(blob_t const * buf, std::uint16_t sz) { printf("\n\r"); } - #include "reader.hpp" #include "unfolder.hpp" #include "writer.hpp" diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 1c3f465..83e2117 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -4,19 +4,18 @@ #include #include +#include #include #include #include -#include - #include "daqlist.hpp" #include "helper.hpp" #include "mcobject.hpp" #include "writer.hpp" -using measurement_value_t = std::variant; -using measurement_tuple_t = std::tuple>; +using measurement_value_t = std::variant; +using measurement_tuple_t = std::tuple>; using measurement_callback_t = std::function)>; template @@ -29,7 +28,7 @@ auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> Ty { return _bswap(get_value(buf, offset)); } -#if HAS_FLOAT16==1 +#if HAS_FLOAT16 == 1 template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> std::float16_t { auto tmp = get_value(buf, offset); @@ -38,7 +37,7 @@ auto get_value(blob_t const * buf, std::uint64_t offset) -> std: } #endif -#if HAS_BFLOAT16==1 +#if HAS_BFLOAT16 == 1 template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { auto tmp = get_value(buf, offset); @@ -47,7 +46,6 @@ auto get_value(blob_t const * buf, std::uint64_t offset) -> std } #endif - template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> float { auto tmp = get_value(buf, offset); @@ -62,7 +60,7 @@ auto get_value(blob_t const * buf, std::uint64_t offset) -> double { return *(reinterpret_cast(&tmp)); } -#if HAS_FLOAT16==1 +#if HAS_FLOAT16 == 1 template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::float16_t { auto tmp = get_value_swapped(buf, offset); @@ -71,7 +69,7 @@ auto get_value_swapped(blob_t const * buf, std::uint64_t offset) } #endif -#if HAS_BFLOAT16==1 +#if HAS_BFLOAT16 == 1 template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { auto tmp = get_value_swapped(buf, offset); @@ -80,7 +78,6 @@ auto get_value_swapped(blob_t const * buf, std::uint64_t offset } #endif - template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> float { auto tmp = get_value_swapped(buf, offset); @@ -125,104 +122,102 @@ auto get_value_swapped(blob_t const * buf, std::uint64_t offset) - return static_cast(get_value_swapped(buf, offset)); } - ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// template -void set_value(blob_t * buf, std::uint64_t offset, Ty value) { +void set_value(blob_t* buf, std::uint64_t offset, Ty value) { ::memcpy(&buf[offset], &value, sizeof(Ty)); } template -void set_value_swapped(blob_t * buf, std::uint64_t offset, Ty value) { +void set_value_swapped(blob_t* buf, std::uint64_t offset, Ty value) { set_value(buf, offset, _bswap(value)); } template<> -void set_value(blob_t * buf, std::uint64_t offset, std::int8_t value) { +void set_value(blob_t* buf, std::uint64_t offset, std::int8_t value) { buf[offset] = static_cast(value); } template<> -void set_value(blob_t * buf, std::uint64_t offset, std::uint8_t value) { +void set_value(blob_t* buf, std::uint64_t offset, std::uint8_t value) { buf[offset] = static_cast(value); } template<> -void set_value(blob_t * buf, std::uint64_t offset, std::int16_t value) { +void set_value(blob_t* buf, std::uint64_t offset, std::int16_t value) { set_value(buf, offset, static_cast(value)); } template<> -void set_value_swapped(blob_t * buf, std::uint64_t offset, std::int16_t value) { +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int16_t value) { set_value_swapped(buf, offset, static_cast(value)); } template<> -void set_value(blob_t * buf, std::uint64_t offset, std::int32_t value) { +void set_value(blob_t* buf, std::uint64_t offset, std::int32_t value) { set_value(buf, offset, static_cast(value)); } template<> -void set_value_swapped(blob_t * buf, std::uint64_t offset, std::int32_t value) { +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int32_t value) { set_value_swapped(buf, offset, static_cast(value)); } template<> -void set_value(blob_t * buf, std::uint64_t offset, std::int64_t value) { +void set_value(blob_t* buf, std::uint64_t offset, std::int64_t value) { set_value(buf, offset, static_cast(value)); } template<> -void set_value_swapped(blob_t * buf, std::uint64_t offset, std::int64_t value) { +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int64_t value) { set_value_swapped(buf, offset, static_cast(value)); } -#if HAS_FLOAT16==1 +#if HAS_FLOAT16 == 1 template<> -void set_value(blob_t * buf, std::uint64_t offset, std::float16_t value) { +void set_value(blob_t* buf, std::uint64_t offset, std::float16_t value) { set_value(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value_swapped(blob_t * buf, std::uint64_t offset, std::float16_t value) { +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::float16_t value) { set_value_swapped(buf, offset, *reinterpret_cast(&value)); } #endif -#if HAS_BFLOAT16==1 +#if HAS_BFLOAT16 == 1 template<> -void set_value(blob_t * buf, std::uint64_t offset, std::bfloat16_t value) { +void set_value(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { set_value(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value_swapped(blob_t * buf, std::uint64_t offset, std::bfloat16_t value) { +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { set_value_swapped(buf, offset, *reinterpret_cast(&value)); } #endif template<> -void set_value(blob_t * buf, std::uint64_t offset, float value) { +void set_value(blob_t* buf, std::uint64_t offset, float value) { set_value(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value_swapped(blob_t * buf, std::uint64_t offset, float value) { +void set_value_swapped(blob_t* buf, std::uint64_t offset, float value) { set_value_swapped(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value(blob_t * buf, std::uint64_t offset, double value) { +void set_value(blob_t* buf, std::uint64_t offset, double value) { set_value(buf, offset, *reinterpret_cast(&value)); } template<> -void set_value_swapped(blob_t * buf, std::uint64_t offset, double value) { +void set_value_swapped(blob_t* buf, std::uint64_t offset, double value) { set_value_swapped(buf, offset, *reinterpret_cast(&value)); } - /* ** Get primitive datatypes, consider byte-order. */ @@ -242,11 +237,11 @@ struct Getter { uint64 = get_value_swapped; float_ = get_value_swapped; double_ = get_value_swapped; -#if HAS_FLOAT16==1 - float16 = get_value_swapped; +#if HAS_FLOAT16 == 1 + float16 = get_value_swapped; #endif -#if HAS_BFLOAT16==1 - bfloat16 = get_value_swapped; +#if HAS_BFLOAT16 == 1 + bfloat16 = get_value_swapped; #endif } else { int16 = get_value; @@ -257,11 +252,11 @@ struct Getter { uint64 = get_value; float_ = get_value; double_ = get_value; -#if HAS_FLOAT16==1 - float16 = get_value; +#if HAS_FLOAT16 == 1 + float16 = get_value; #endif -#if HAS_BFLOAT16==1 - bfloat16 = get_value; +#if HAS_BFLOAT16 == 1 + bfloat16 = get_value; #endif } } @@ -303,11 +298,11 @@ struct Getter { return float_(buf, offset); case 9: return double_(buf, offset); -#if HAS_FLOAT16==1 +#if HAS_FLOAT16 == 1 case 10: return float16(buf, offset); #endif -#if HAS_BFLOAT16==1 +#if HAS_BFLOAT16 == 1 case 11: return bfloat16(buf, offset); #endif @@ -363,14 +358,14 @@ struct Getter { std::function uint64; std::function float_; std::function double_; -#if HAS_FLOAT16==1 - std::function float16; +#if HAS_FLOAT16 == 1 + std::function float16; #endif -#if HAS_BFLOAT16==1 - std::function bfloat16; +#if HAS_BFLOAT16 == 1 + std::function bfloat16; #endif - std::vector m_first_pids; - std::map> m_odt_to_daq_map; + std::vector m_first_pids; + std::map> m_odt_to_daq_map; }; ////////////////////////////////////////////////////////////////////////////////////////////// @@ -390,11 +385,11 @@ struct Setter { uint64 = set_value_swapped; float_ = set_value_swapped; double_ = set_value_swapped; -#if HAS_FLOAT16==1 - float16 = set_value_swapped; +#if HAS_FLOAT16 == 1 + float16 = set_value_swapped; #endif -#if HAS_BFLOAT16==1 - bfloat16 = set_value_swapped; +#if HAS_BFLOAT16 == 1 + bfloat16 = set_value_swapped; #endif } else { int16 = set_value; @@ -405,16 +400,16 @@ struct Setter { uint64 = set_value; float_ = set_value; double_ = set_value; -#if HAS_FLOAT16==1 - float16 = set_value; +#if HAS_FLOAT16 == 1 + float16 = set_value; #endif -#if HAS_BFLOAT16==1 - bfloat16 = set_value; +#if HAS_BFLOAT16 == 1 + bfloat16 = set_value; #endif } } - void set_timestamp(blob_t * buf, std::uint32_t timestamp) { + void set_timestamp(blob_t* buf, std::uint32_t timestamp) { switch (m_ts_size) { case 0: break; @@ -432,7 +427,7 @@ struct Setter { } } - void writer(std::uint16_t tp, blob_t * buf, std::uint16_t offset, const measurement_value_t& value) { + void writer(std::uint16_t tp, blob_t* buf, std::uint16_t offset, const measurement_value_t& value) { switch (tp) { case 0: uint8(buf, offset, static_cast(std::get(value))); @@ -464,12 +459,12 @@ struct Setter { case 9: double_(buf, offset, static_cast(std::get(value))); break; -#if HAS_FLOAT16==1 +#if HAS_FLOAT16 == 1 case 10: float16(buf, offset, static_cast(std::get(value))); break; #endif -#if HAS_BFLOAT16==1 +#if HAS_BFLOAT16 == 1 case 11: bfloat16(buf, offset, static_cast(std::get(value))); break; @@ -498,35 +493,36 @@ struct Setter { } } #endif - std::uint8_t m_id_size; - std::uint8_t m_ts_size; - std::function int8; - std::function uint8; - std::function int16; - std::function int32; - std::function int64; - std::function uint16; - std::function uint32; - std::function uint64; - std::function float_; - std::function double_; -#if HAS_FLOAT16==1 - std::function float16; + std::uint8_t m_id_size; + std::uint8_t m_ts_size; + std::function int8; + std::function uint8; + std::function int16; + std::function int32; + std::function int64; + std::function uint16; + std::function uint32; + std::function uint64; + std::function float_; + std::function double_; +#if HAS_FLOAT16 == 1 + std::function float16; #endif -#if HAS_BFLOAT16==1 - std::function bfloat16; +#if HAS_BFLOAT16 == 1 + std::function bfloat16; #endif - std::map> m_odt_to_daq_map; + std::map> m_odt_to_daq_map; }; + ////////////////////////////////////////////////////////////////////////////////////////////// struct MeasurementParameters { MeasurementParameters() = default; - MeasurementParameters(MeasurementParameters&& ) = default; - MeasurementParameters(const MeasurementParameters& ) = default; - MeasurementParameters& operator=(MeasurementParameters&& ) = default; - MeasurementParameters& operator=(const MeasurementParameters& ) = default; + MeasurementParameters(MeasurementParameters&&) = default; + MeasurementParameters(const MeasurementParameters&) = default; + MeasurementParameters& operator=(MeasurementParameters&&) = default; + MeasurementParameters& operator=(const MeasurementParameters&) = default; explicit MeasurementParameters( std::uint8_t byte_order, std::uint8_t id_field_size, bool timestamps_supported, bool ts_fixed, bool prescaler_supported, @@ -543,7 +539,7 @@ struct MeasurementParameters { m_ts_size(ts_size), m_min_daq(min_daq), m_daq_lists(daq_lists), - m_first_pids(first_pids) { + m_first_pids(first_pids) { } std::string dumps() const { @@ -567,116 +563,118 @@ struct MeasurementParameters { std::size_t fp_count = m_first_pids.size(); ss << to_binary(fp_count); - for (const auto& fp : m_first_pids) { - ss << to_binary(fp); - } + for (const auto& fp : m_first_pids) { + ss << to_binary(fp); + } - return to_binary(std::size(ss.str())) + ss.str(); + return to_binary(std::size(ss.str())) + ss.str(); } - auto get_byte_order() const noexcept { - return m_byte_order; - } - - auto get_id_field_size() const noexcept { - return m_id_field_size; - } - - auto get_timestamps_supported() const noexcept { - return m_timestamps_supported; - } - - auto get_ts_fixed() const noexcept { - return m_ts_fixed; - } - - auto get_prescaler_supported() const noexcept { - return m_prescaler_supported; - } - - auto get_selectable_timestamps() const noexcept { - return m_selectable_timestamps; - } - - auto get_ts_scale_factor() const noexcept { - return m_ts_scale_factor; - } - - auto get_ts_size() const noexcept { - return m_ts_size; - } - - auto get_min_daq() const noexcept { - return m_min_daq; - } - - auto get_daq_lists() const noexcept { - return m_daq_lists; - } - - auto get_first_pids() const noexcept { - return m_first_pids; - } - - std::uint8_t m_byte_order; - std::uint8_t m_id_field_size; - bool m_timestamps_supported; - bool m_ts_fixed; - bool m_prescaler_supported; - bool m_selectable_timestamps; - double m_ts_scale_factor; - std::uint8_t m_ts_size; - std::uint16_t m_min_daq; - std::vector m_daq_lists; - std::vector m_first_pids; -}; + auto get_byte_order() const noexcept { + return m_byte_order; + } + auto get_id_field_size() const noexcept { + return m_id_field_size; + } + + auto get_timestamps_supported() const noexcept { + return m_timestamps_supported; + } + + auto get_ts_fixed() const noexcept { + return m_ts_fixed; + } + + auto get_prescaler_supported() const noexcept { + return m_prescaler_supported; + } + + auto get_selectable_timestamps() const noexcept { + return m_selectable_timestamps; + } + + auto get_ts_scale_factor() const noexcept { + return m_ts_scale_factor; + } + + auto get_ts_size() const noexcept { + return m_ts_size; + } + + auto get_min_daq() const noexcept { + return m_min_daq; + } + + auto get_daq_lists() const noexcept { + return m_daq_lists; + } + + auto get_first_pids() const noexcept { + return m_first_pids; + } + + std::uint8_t m_byte_order; + std::uint8_t m_id_field_size; + bool m_timestamps_supported; + bool m_ts_fixed; + bool m_prescaler_supported; + bool m_selectable_timestamps; + double m_ts_scale_factor; + std::uint8_t m_ts_size; + std::uint16_t m_min_daq; + std::vector m_daq_lists; + std::vector m_first_pids; +}; class Deserializer { -public: + public: + explicit Deserializer(const std::string& buf) : m_buf(buf) { } MeasurementParameters run() { - std::uint8_t byte_order; - std::uint8_t id_field_size; - bool timestamps_supported; - bool ts_fixed; - bool prescaler_supported; - bool selectable_timestamps; - double ts_scale_factor; - std::uint8_t ts_size; - std::uint16_t min_daq; - std::size_t dl_count; - std::vector daq_lists; - std::size_t fp_count; - std::vector first_pids; - - byte_order = from_binary(); - id_field_size = from_binary(); - timestamps_supported = from_binary(); - ts_fixed = from_binary(); - prescaler_supported = from_binary(); + std::uint8_t byte_order; + std::uint8_t id_field_size; + bool timestamps_supported; + bool ts_fixed; + bool prescaler_supported; + bool selectable_timestamps; + double ts_scale_factor; + std::uint8_t ts_size; + std::uint16_t min_daq; + std::size_t dl_count; + std::vector daq_lists; + std::size_t fp_count; + std::vector first_pids; + + byte_order = from_binary(); + id_field_size = from_binary(); + timestamps_supported = from_binary(); + ts_fixed = from_binary(); + prescaler_supported = from_binary(); selectable_timestamps = from_binary(); - ts_scale_factor = from_binary(); - ts_size = from_binary(); - min_daq = from_binary(); - dl_count = from_binary(); + ts_scale_factor = from_binary(); + ts_size = from_binary(); + min_daq = from_binary(); + dl_count = from_binary(); for (std::size_t i = 0; i < dl_count; i++) { daq_lists.push_back(create_daq_list()); } - fp_count = from_binary(); - for (std::size_t i = 0; i < fp_count; i++) { - first_pids.push_back(from_binary()); - } + fp_count = from_binary(); + for (std::size_t i = 0; i < fp_count; i++) { + first_pids.push_back(from_binary()); + } - return MeasurementParameters(byte_order, id_field_size, timestamps_supported, ts_fixed, prescaler_supported, selectable_timestamps, - ts_scale_factor, ts_size, min_daq, daq_lists, first_pids); + return MeasurementParameters( + byte_order, id_field_size, timestamps_supported, ts_fixed, prescaler_supported, selectable_timestamps, ts_scale_factor, + ts_size, min_daq, daq_lists, first_pids + ); } -protected: + protected: DaqList create_daq_list() { std::string name; @@ -687,29 +685,29 @@ class Deserializer { std::vector measurements_opt; std::vector header_names; - std::uint16_t odt_count; - std::uint16_t total_entries; - std::uint16_t total_length; + std::uint16_t odt_count; + std::uint16_t total_entries; + std::uint16_t total_length; - flatten_odts_t flatten_odts; + flatten_odts_t flatten_odts; std::vector initializer_list{}; - name = from_binary(); - event_num = from_binary(); - stim = from_binary(); + name = from_binary(); + event_num = from_binary(); + stim = from_binary(); enable_timestamps = from_binary(); - odt_count = from_binary(); // not used - total_entries = from_binary(); // not used - total_length = from_binary(); // not used + odt_count = from_binary(); // not used + total_entries = from_binary(); // not used + total_length = from_binary(); // not used std::size_t meas_size = from_binary(); for (std::size_t i = 0; i < meas_size; ++i) { // name, address, ext, dt_name auto meas = create_mc_object(); measurements.push_back(meas); - initializer_list.push_back({meas.get_name(), meas.get_address(), meas.get_ext(), meas.get_data_type()}); + initializer_list.push_back({ meas.get_name(), meas.get_address(), meas.get_ext(), meas.get_data_type() }); } std::size_t meas_opt_size = from_binary(); @@ -731,11 +729,11 @@ class Deserializer { } flatten_odts_t create_flatten_odts() { - std::string name; + std::string name; std::uint32_t address; - std::uint8_t ext; + std::uint8_t ext; std::uint16_t size; - std::int16_t type_index; + std::int16_t type_index; flatten_odts_t odts; @@ -744,10 +742,10 @@ class Deserializer { std::vector> flatten_odt{}; std::size_t odt_entry_count = from_binary(); for (std::size_t j = 0; j < odt_entry_count; ++j) { - name = from_binary(); - address = from_binary(); - ext = from_binary(); - size = from_binary(); + name = from_binary(); + address = from_binary(); + ext = from_binary(); + size = from_binary(); type_index = from_binary(); flatten_odt.push_back(std::make_tuple(name, address, ext, size, type_index)); } @@ -766,14 +764,14 @@ class Deserializer { std::int16_t type_index; std::vector components{}; - name = from_binary(); - address = from_binary(); - ext = from_binary(); - length = from_binary(); - data_type = from_binary(); - type_index = from_binary(); // not used + name = from_binary(); + address = from_binary(); + ext = from_binary(); + length = from_binary(); + data_type = from_binary(); + type_index = from_binary(); // not used std::size_t comp_size = from_binary(); - for (auto i=0U; i < comp_size; i++) { + for (auto i = 0U; i < comp_size; i++) { components.push_back(create_mc_object()); } @@ -785,10 +783,10 @@ class Deserializer { std::uint16_t residual_capacity; std::vector entries{}; - size = from_binary(); - residual_capacity = from_binary(); + size = from_binary(); + residual_capacity = from_binary(); std::size_t entry_size = from_binary(); - for (auto i=0U; i < entry_size; i++) { + for (auto i = 0U; i < entry_size; i++) { entries.push_back(create_mc_object()); } @@ -804,20 +802,20 @@ class Deserializer { template<> inline std::string from_binary() { - auto length = from_binary(); + auto length = from_binary(); std::string result; - auto start = m_buf.cbegin() + m_offset; + auto start = m_buf.cbegin() + m_offset; std::copy(start, start + length, std::back_inserter(result)); m_offset += length; return result; } -private: - std::string m_buf; - std::size_t m_offset = 0; -}; + private: + std::string m_buf; + std::size_t m_offset = 0; +}; class DaqListState { public: @@ -832,8 +830,7 @@ class DaqListState { DaqListState( std::uint16_t daq_list_num, std::uint16_t num_odts, std::uint16_t total_entries, bool enable_timestamps, - std::uint16_t initial_offset, const flatten_odts_t& flatten_odts, const Getter& getter, - MeasurementParameters params + std::uint16_t initial_offset, const flatten_odts_t& flatten_odts, const Getter& getter, MeasurementParameters params ) : m_daq_list_num(daq_list_num), m_num_odts(num_odts), @@ -848,9 +845,7 @@ class DaqListState { m_buffer{}, m_flatten_odts(flatten_odts), m_getter(getter), - m_params(params) - { - + m_params(params) { m_buffer.resize(m_total_entries); } @@ -953,7 +948,7 @@ class DaqListState { double m_timestamp1 = 0.0; state_t m_state = state_t::IDLE; std::vector m_buffer; - flatten_odts_t m_flatten_odts; + flatten_odts_t m_flatten_odts; Getter m_getter; MeasurementParameters m_params; }; @@ -964,7 +959,6 @@ auto requires_swap(std::uint8_t byte_order) -> bool { return (target_byte_order != std::endian::native) ? true : false; } - class DAQProcessor { public: @@ -975,7 +969,6 @@ class DAQProcessor { DAQProcessor() = delete; virtual ~DAQProcessor() = default; - std::optional feed(double timestamp, const std::string& payload) noexcept { const auto data = reinterpret_cast(payload.data()); auto [daq_num, odt_num] = m_getter.get_id(data); @@ -984,7 +977,7 @@ class DAQProcessor { // DAQ list completed. measurement_tuple_t result; - m_state[daq_num].add_result(result); // get_result()??? + m_state[daq_num].add_result(result); // get_result()??? return result; } return std::nullopt; @@ -1001,7 +994,7 @@ class DAQProcessor { m_getter, params )); } - m_getter.set_first_pids(m_params.m_daq_lists, m_params.m_first_pids); + m_getter.set_first_pids(m_params.m_daq_lists, m_params.m_first_pids); } MeasurementParameters m_params; @@ -1010,11 +1003,11 @@ class DAQProcessor { std::vector m_state; }; - class DAQPolicyBase { public: - virtual ~DAQPolicyBase() {} + virtual ~DAQPolicyBase() { + } virtual void set_parameters(const MeasurementParameters& params) noexcept { initialize(); @@ -1025,12 +1018,10 @@ class DAQPolicyBase { virtual void initialize() = 0; virtual void finalize() = 0; - }; - class DaqRecorderPolicy : public DAQPolicyBase { -public: + public: ~DaqRecorderPolicy() { finalize(); @@ -1063,20 +1054,19 @@ class DaqRecorderPolicy : public DAQPolicyBase { m_writer->finalize(); } -private: + private: std::unique_ptr m_writer; - MeasurementParameters m_params; + MeasurementParameters m_params; }; - -class DaqOnlinePolicy : public DAQPolicyBase { +class DaqOnlinePolicy : public DAQPolicyBase { public: ~DaqOnlinePolicy() { } - DaqOnlinePolicy() = default; + DaqOnlinePolicy() = default; void set_parameters(const MeasurementParameters& params) noexcept { m_unfolder = std::make_unique(params); @@ -1093,8 +1083,8 @@ class DaqOnlinePolicy : public DAQPolicyBase { } auto result = m_unfolder->feed(timestamp, payload); if (result) { - const auto& [daq_list, ts0, ts1, meas] = *result; - on_daq_list(daq_list, ts0, ts1, meas); + const auto& [daq_list, ts0, ts1, meas] = *result; + on_daq_list(daq_list, ts0, ts1, meas); } } @@ -1109,84 +1099,76 @@ class DaqOnlinePolicy : public DAQPolicyBase { std::unique_ptr m_unfolder; }; - class XcpLogFileUnfolder { public: - explicit XcpLogFileUnfolder(const std::string& file_name) : - m_reader(file_name) { - - auto metadata = m_reader.get_metadata(); - if (metadata != "") { - auto des = Deserializer(metadata); - m_params = des.run(); - m_unfolder = std::make_unique(m_params); - } else { - // cannot proceed!!! - } - + explicit XcpLogFileUnfolder(const std::string& file_name) : m_reader(file_name) { + auto metadata = m_reader.get_metadata(); + if (metadata != "") { + auto des = Deserializer(metadata); + m_params = des.run(); + m_unfolder = std::make_unique(m_params); + } else { + // cannot proceed!!! + } } - XcpLogFileUnfolder() = delete; + XcpLogFileUnfolder() = delete; virtual ~XcpLogFileUnfolder() = default; + void run() { + const auto converter = [](const blob_t* in_str, std::size_t length) -> std::string { + std::string result; + result.resize(length); - void run() { - - const auto converter = [](const blob_t * in_str, std::size_t length) -> std::string { - std::string result; - result.resize(length); - - for (std::size_t idx=0; idx < length; ++idx) { - result[idx] = static_cast(in_str[idx]); - } - - return result; - }; - - while (true) { - const auto& block = m_reader.next_block(); - if (!block) { - return; - } - - for (const auto& [frame_cat, counter, timestamp, length, payload] : block.value()) { - auto str_data = converter(payload.data(), std::size(payload)); - if (frame_cat != static_cast(FrameCategory::DAQ)) { - continue; - } - auto result = m_unfolder->feed(timestamp, str_data); - if (result) { - const auto& [daq_list, ts0, ts1, meas] = *result; - on_daq_list(daq_list, ts0, ts1, meas); - } - } - } - } + for (std::size_t idx = 0; idx < length; ++idx) { + result[idx] = static_cast(in_str[idx]); + } + return result; + }; + + while (true) { + const auto& block = m_reader.next_block(); + if (!block) { + return; + } + + for (const auto& [frame_cat, counter, timestamp, length, payload] : block.value()) { + auto str_data = converter(payload.data(), std::size(payload)); + if (frame_cat != static_cast(FrameCategory::DAQ)) { + continue; + } + auto result = m_unfolder->feed(timestamp, str_data); + if (result) { + const auto& [daq_list, ts0, ts1, meas] = *result; + on_daq_list(daq_list, ts0, ts1, meas); + } + } + } + } - virtual void on_daq_list( + virtual void on_daq_list( std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement ) = 0; + MeasurementParameters get_parameters() const { + return m_params; + } - MeasurementParameters get_parameters() const { - return m_params; - } - - auto get_daq_lists() const { - return m_params.m_daq_lists; - } + auto get_daq_lists() const { + return m_params.m_daq_lists; + } - auto get_header() const { - return m_reader.get_header(); - } + auto get_header() const { + return m_reader.get_header(); + } private: - XcpLogFileReader m_reader; - std::unique_ptr m_unfolder; - MeasurementParameters m_params; + XcpLogFileReader m_reader; + std::unique_ptr m_unfolder; + MeasurementParameters m_params; }; #if 0 @@ -1197,5 +1179,4 @@ class XcpLogFileUnfolder { " (?on_daq_list@XcpLogFileUnfolder@@UEAAXGNNAEBV?$vector@V?$variant@_J_KOV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@std@@V?$allocator@V?$variant@_J_KOV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@std@@@2@@std@@@Z)". #endif - #endif // RECORDER_UNFOLDER_HPP diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 24b1742..5ad9863 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -1,8 +1,8 @@ +#include #include #include #include -#include #include @@ -11,14 +11,13 @@ namespace py = pybind11; using namespace pybind11::literals; - class PyDaqOnlinePolicy : public DaqOnlinePolicy { public: using DaqOnlinePolicy::DaqOnlinePolicy; void on_daq_list( - std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector &measurement + std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement ) override { PYBIND11_OVERRIDE_PURE(void, DaqOnlinePolicy, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); } @@ -46,36 +45,34 @@ class PyDaqRecorderPolicy : public DaqRecorderPolicy { } }; - class PyXcpLogFileUnfolder : public XcpLogFileUnfolder { public: using XcpLogFileUnfolder::XcpLogFileUnfolder; - void on_daq_list(std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement) override { + void on_daq_list( + std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + ) override { PYBIND11_OVERRIDE_PURE(void, XcpLogFileUnfolder, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); } - }; - PYBIND11_MODULE(rekorder, m) { - m.doc() = "XCP raw frame recorder." - ; + m.doc() = "XCP raw frame recorder."; - #if 0 +#if 0 version; options; num_containers; record_count; size_compressed; size_uncompressed; - #endif +#endif py::class_(m, "FileHeaderType") .def(py::init()) - .def("__repr__",[](const FileHeaderType& self) { - std::stringstream ss; + .def("__repr__", [](const FileHeaderType& self) { + std::stringstream ss; ss << "FileHeaderType(" << std::endl; ss << " hdr_size=" << self.hdr_size << "," << std::endl; ss << " version=" << self.version << "," << std::endl; @@ -83,78 +80,74 @@ PYBIND11_MODULE(rekorder, m) { ss << " num_containers=" << self.num_containers << "," << std::endl; ss << " record_count=" << self.record_count << "," << std::endl; ss << " size_compressed=" << self.size_compressed << "," << std::endl; - ss << " size_uncompressed=" << self.size_uncompressed << "," << std::endl; - ss << " compression_ratio=" << (double)((std::uint64_t)(((double)self.size_uncompressed / (double)self.size_compressed * 100.0) + 0.5)) / 100.0 << std::endl; + ss << " size_uncompressed=" << self.size_uncompressed << "," << std::endl; + ss << " compression_ratio=" + << (double)((std::uint64_t)(((double)self.size_uncompressed / (double)self.size_compressed * 100.0) + 0.5)) / 100.0 + << std::endl; ss << ")" << std::endl; return ss.str(); - }) - ; + }); - py::class_(m, "Deserializer") - .def(py::init()) - .def("run", &Deserializer::run) - ; + py::class_(m, "Deserializer").def(py::init()).def("run", &Deserializer::run); py::class_(m, "_PyXcpLogFileReader") - .def(py::init()) + .def(py::init()) .def("next_block", &XcpLogFileReader::next_block) .def("reset", &XcpLogFileReader::reset) .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple) - .def("get_metadata",[](const XcpLogFileReader& self) { - return py::bytes(self.get_metadata()); - }) - ; + .def("get_metadata", [](const XcpLogFileReader& self) { return py::bytes(self.get_metadata()); }); py::class_(m, "_PyXcpLogFileWriter") - .def(py::init(), - py::arg("filename"), py::arg("prealloc"), py::arg("chunk_size"), py::arg("metadata")="") + .def( + py::init(), py::arg("filename"), + py::arg("prealloc"), py::arg("chunk_size"), py::arg("metadata") = "" + ) .def("finalize", &XcpLogFileWriter::finalize) - .def("add_frame", &XcpLogFileWriter::add_frame) - ; + .def("add_frame", &XcpLogFileWriter::add_frame); py::class_(m, "MeasurementParameters") .def(py::init< - std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector&, const std::vector&>( - )) - .def("dumps", [](const MeasurementParameters& self) { - return py::bytes(self.dumps()); - }) - .def("__repr__",[](const MeasurementParameters& self) { - std::stringstream ss; - ss << "MeasurementParameters("; - ss << "byte_order=\"" << byte_order_to_string(self.m_byte_order) << "\", "; - ss << "id_field_size=" << static_cast(self.m_id_field_size) << ", "; - ss << "timestamps_supported=" << bool_to_string(self.m_timestamps_supported) << ", "; - ss << "ts_fixed=" << bool_to_string(self.m_ts_fixed) << ", "; - ss << "prescaler_supported=" << bool_to_string(self.m_prescaler_supported) << ", "; - ss << "selectable_timestamps=" << bool_to_string(self.m_selectable_timestamps) << ", "; - ss << "ts_scale_factor=" << self.m_ts_scale_factor << ", "; - ss << "ts_size=" << static_cast(self.m_ts_size) << ", "; - ss << "min_daq=" << static_cast(self.m_min_daq) << ", "; - ss << "daq_lists=[\n"; - for (const auto& dl: self.m_daq_lists) { - ss << dl.to_string() << ",\n"; - } - ss << "],\n"; - ss << "first_pids=["; - for (auto fp: self.m_first_pids) { - ss << fp << ", "; - } - ss << "]"; - return ss.str(); - }) - .def_property_readonly("byte_order", &MeasurementParameters::get_byte_order) - .def_property_readonly("id_field_size", &MeasurementParameters::get_id_field_size) - .def_property_readonly("timestamps_supported", &MeasurementParameters::get_timestamps_supported) - .def_property_readonly("ts_fixed", &MeasurementParameters::get_ts_fixed) - .def_property_readonly("prescaler_supported", &MeasurementParameters::get_prescaler_supported) - .def_property_readonly("selectable_timestamps", &MeasurementParameters::get_selectable_timestamps) - .def_property_readonly("ts_scale_factor", &MeasurementParameters::get_ts_scale_factor) - .def_property_readonly("ts_size", &MeasurementParameters::get_ts_size) - .def_property_readonly("min_daq", &MeasurementParameters::get_min_daq) - .def_property_readonly("daq_lists", &MeasurementParameters::get_daq_lists) - .def_property_readonly("first_pids", &MeasurementParameters::get_first_pids) - ; + std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector&, + const std::vector&>()) + .def("dumps", [](const MeasurementParameters& self) { return py::bytes(self.dumps()); }) + .def( + "__repr__", + [](const MeasurementParameters& self) { + std::stringstream ss; + ss << "MeasurementParameters("; + ss << "byte_order=\"" << byte_order_to_string(self.m_byte_order) << "\", "; + ss << "id_field_size=" << static_cast(self.m_id_field_size) << ", "; + ss << "timestamps_supported=" << bool_to_string(self.m_timestamps_supported) << ", "; + ss << "ts_fixed=" << bool_to_string(self.m_ts_fixed) << ", "; + ss << "prescaler_supported=" << bool_to_string(self.m_prescaler_supported) << ", "; + ss << "selectable_timestamps=" << bool_to_string(self.m_selectable_timestamps) << ", "; + ss << "ts_scale_factor=" << self.m_ts_scale_factor << ", "; + ss << "ts_size=" << static_cast(self.m_ts_size) << ", "; + ss << "min_daq=" << static_cast(self.m_min_daq) << ", "; + ss << "daq_lists=[\n"; + for (const auto& dl : self.m_daq_lists) { + ss << dl.to_string() << ",\n"; + } + ss << "],\n"; + ss << "first_pids=["; + for (auto fp : self.m_first_pids) { + ss << fp << ", "; + } + ss << "]"; + return ss.str(); + } + ) + .def_property_readonly("byte_order", &MeasurementParameters::get_byte_order) + .def_property_readonly("id_field_size", &MeasurementParameters::get_id_field_size) + .def_property_readonly("timestamps_supported", &MeasurementParameters::get_timestamps_supported) + .def_property_readonly("ts_fixed", &MeasurementParameters::get_ts_fixed) + .def_property_readonly("prescaler_supported", &MeasurementParameters::get_prescaler_supported) + .def_property_readonly("selectable_timestamps", &MeasurementParameters::get_selectable_timestamps) + .def_property_readonly("ts_scale_factor", &MeasurementParameters::get_ts_scale_factor) + .def_property_readonly("ts_size", &MeasurementParameters::get_ts_size) + .def_property_readonly("min_daq", &MeasurementParameters::get_min_daq) + .def_property_readonly("daq_lists", &MeasurementParameters::get_daq_lists) + .def_property_readonly("first_pids", &MeasurementParameters::get_first_pids); py::class_(m, "DaqRecorderPolicy", py::dynamic_attr()) .def(py::init<>()) @@ -162,8 +155,7 @@ PYBIND11_MODULE(rekorder, m) { .def("feed", &DaqRecorderPolicy::feed) .def("set_parameters", &DaqRecorderPolicy::set_parameters) .def("initialize", &DaqRecorderPolicy::initialize) - .def("finalize", &DaqRecorderPolicy::finalize) - ; + .def("finalize", &DaqRecorderPolicy::finalize); py::class_(m, "DaqOnlinePolicy", py::dynamic_attr()) .def(py::init<>()) @@ -171,15 +163,13 @@ PYBIND11_MODULE(rekorder, m) { .def("feed", &DaqOnlinePolicy::feed) .def("finalize", &DaqOnlinePolicy::finalize) .def("set_parameters", &DaqOnlinePolicy::set_parameters) - .def("initialize", &DaqOnlinePolicy::initialize) - ; + .def("initialize", &DaqOnlinePolicy::initialize); py::class_(m, "XcpLogFileUnfolder", py::dynamic_attr()) - .def(py::init()) - .def("run", &XcpLogFileUnfolder::run) - .def("on_daq_list", &XcpLogFileUnfolder::on_daq_list) - .def_property_readonly("parameters", &XcpLogFileUnfolder::get_parameters) - .def_property_readonly("daq_lists", &XcpLogFileUnfolder::get_daq_lists) - .def("get_header", &XcpLogFileUnfolder::get_header) - ; + .def(py::init()) + .def("run", &XcpLogFileUnfolder::run) + .def("on_daq_list", &XcpLogFileUnfolder::on_daq_list) + .def_property_readonly("parameters", &XcpLogFileUnfolder::get_parameters) + .def_property_readonly("daq_lists", &XcpLogFileUnfolder::get_daq_lists) + .def("get_header", &XcpLogFileUnfolder::get_header); } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index f912789..fcbc58e 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -2,10 +2,14 @@ #ifndef RECORDER_WRITER_HPP #define RECORDER_WRITER_HPP +#include + class XcpLogFileWriter { public: - explicit XcpLogFileWriter(const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1, std::string_view metadata="") { + explicit XcpLogFileWriter( + const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1, std::string_view metadata = "" + ) { if (!file_name.ends_with(detail::FILE_EXTENSION)) { m_file_name = file_name + detail::FILE_EXTENSION; } else { @@ -21,9 +25,9 @@ class XcpLogFileWriter { m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666); #endif m_hard_limit = megabytes(prealloc); - truncate(m_hard_limit); + resize(m_hard_limit); m_mmap = new mio::mmap_sink(m_fd); - m_chunk_size = 512 * 1024; //megabytes(chunk_size); + m_chunk_size = 512 * 1024; // megabytes(chunk_size); m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)]; m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; m_metadata = metadata; @@ -64,7 +68,7 @@ class XcpLogFileWriter { std::cout << error_string("mio::unmap", ec); } - truncate(m_offset); + resize(m_offset); #if defined(_WIN32) if (!CloseHandle(m_fd)) { std::cout << error_string("CloseHandle", get_last_error()); @@ -79,9 +83,8 @@ class XcpLogFileWriter { } } - void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const *data) { + void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const *data) { auto payload = new char[length]; - // auto payload = mem.acquire(); _fcopy(payload, data, length); my_queue.put(std::make_tuple(category, counter, timestamp, length, payload)); @@ -89,7 +92,7 @@ class XcpLogFileWriter { protected: - void truncate(off_t size, bool remap = false) { + void resize(off_t size, bool remap = false) { std::error_code ec; if (remap) { @@ -102,11 +105,9 @@ class XcpLogFileWriter { #if defined(_WIN32) if (SetFilePointer(m_fd, size, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { - // TODO: Errorhandling. std::cout << error_string("SetFilePointer", get_last_error()); } if (SetEndOfFile(m_fd) == 0) { - // TODO: Errorhandling. std::cout << error_string("SetEndOfFile", get_last_error()); } #else @@ -144,17 +145,15 @@ class XcpLogFileWriter { LZ4_COMPRESSBOUND(m_intermediate_storage_offset), LZ4HC_CLEVEL_MAX ); - - if (cp_size < 0) { throw std::runtime_error("LZ4 compression failed."); } - // printf("comp: %d %d [%f]\n", m_intermediate_storage_offset, cp_size, double(m_intermediate_storage_offset) / - // double(cp_size)); + std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + if (m_offset > (m_hard_limit >> 1)) { - // std::cout <<"[INFO] " << std::chrono::system_clock::now() << ": Doubling measurement file size." << std::endl; + std::cout << "[INFO] " << std::ctime(&now) << ": Doubling measurement file size." << std::endl; m_hard_limit <<= 1; - truncate(m_hard_limit, true); + resize(m_hard_limit, true); write_header( detail::VERSION, m_metadata.empty() ? 0 : XMRAW_HAS_METADATA, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed @@ -262,7 +261,7 @@ class XcpLogFileWriter { std::uint64_t m_container_size_compressed{ 0UL }; __ALIGN blob_t *m_intermediate_storage{ nullptr }; std::uint64_t m_intermediate_storage_offset{ 0 }; - std::uint64_t m_hard_limit{0}; + std::uint64_t m_hard_limit{ 0 }; mio::file_handle_type m_fd{ INVALID_HANDLE_VALUE }; mio::mmap_sink *m_mmap{ nullptr }; bool m_finalized{ false }; diff --git a/reformat.cmd b/reformat.cmd new file mode 100644 index 0000000..bd1a0be --- /dev/null +++ b/reformat.cmd @@ -0,0 +1 @@ +clang-format -i .\pyxcp\cpp_ext\*.cpp .\pyxcp\recorder\*.cpp .\pyxcp\cpp_ext\*.hpp .\pyxcp\recorder\*.hpp .\pyxcp\daq_stim\*.hpp .\pyxcp\daq_stim\*.cpp From c92fc5dd500b2c3c83b7187b697d03b640cd140a Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 6 Jun 2024 18:15:13 +0300 Subject: [PATCH 44/99] today() --- docs/recorder.rst | 32 ++++++++++++++++++++++++++++++++ pyxcp/cpp_ext/mcobject.hpp | 2 +- pyxcp/recorder/rekorder.hpp | 31 +++++++++++++++++++++++++++---- pyxcp/recorder/writer.hpp | 24 +++++++++++++++--------- 4 files changed, 75 insertions(+), 14 deletions(-) diff --git a/docs/recorder.rst b/docs/recorder.rst index 704144a..1e40516 100644 --- a/docs/recorder.rst +++ b/docs/recorder.rst @@ -1,3 +1,4 @@ +<<<<<<< Updated upstream Recorder ======== @@ -25,3 +26,34 @@ you can use the ``lumache.get_random_ingredients()`` function: The ``kind`` parameter should be either ``"meat"``, ``"fish"``, or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients` will raise an exception. +||||||| Stash base +======= + +Recorder +======== + + +:py:class:`pyxcp.recorder.XcpLogFileWriter` + +:py:meth:`pyxcp.recorder.XcpLogFileWritermymethod` + +Creating recipes +---------------- + +To retrieve a list of random ingredients, +you can use the ``lumache.get_random_ingredients()`` function: + +.. py:function:: lumache.get_random_ingredients(kind=None) + + Return a list of random ingredients as strings. + + :param kind: Optional "kind" of ingredients. + :type kind: list[str] or None + :return: The ingredients list. + :rtype: list[str] + +The ``kind`` parameter should be either ``"meat"``, ``"fish"``, +or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients` +will raise an exception. + +>>>>>>> Stashed changes diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index 5866f3e..6bdc67b 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -5,8 +5,8 @@ #include #include #include - #include #include + #include #include #include "helper.hpp" diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 8fe9f6c..a0dbfb6 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -10,6 +10,7 @@ #include #include #include + #include #include #include #include @@ -179,7 +180,6 @@ inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { } #endif /* STANDALONE_REKORDER */ - //////////////////////////////////// #ifdef _WIN32 inline std::string error_string(std::string_view func, std::error_code error_code) { LPSTR messageBuffer = nullptr; @@ -199,7 +199,7 @@ inline std::string error_string(std::string_view func, std::error_code error_cod return ss.str(); } -std::error_code get_last_error() { +inline std::error_code get_last_error() { return std::error_code(GetLastError(), std::system_category()); } @@ -215,12 +215,35 @@ inline std::string error_string(std::string_view func, std::error_code error_cod return ss.str(); } -std::error_code get_last_error() { +inline std::error_code get_last_error() { return std::error_code(errno, std::system_category()); } #endif // _WIN32 -//////////////////////////////////// + +inline std::string& ltrim(std::string& s) { + auto it = std::find_if(s.begin(), s.end(), [](char c) { return !std::isspace(c, std::locale::classic()); }); + s.erase(s.begin(), it); + return s; +} + +inline std::string& rtrim(std::string& s) { + auto it = std::find_if(s.rbegin(), s.rend(), [](char c) { return !std::isspace(c, std::locale::classic()); }); + s.erase(it.base(), s.end()); + return s; +} + +inline std::string& trim(std::string& s) { + return ltrim(rtrim(s)); +} + +inline std::string current_timestamp() { + std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + std::string result{ std::ctime(&now) }; + + // result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end()); + return trim(result); +} inline void hexdump(blob_t const * buf, std::uint16_t sz) { for (std::uint16_t idx = 0; idx < sz; ++idx) { diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index fcbc58e..733ce31 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -2,7 +2,7 @@ #ifndef RECORDER_WRITER_HPP #define RECORDER_WRITER_HPP -#include +constexpr std::uint64_t MASK32 = (1ULL << 32) - 1; class XcpLogFileWriter { public: @@ -92,7 +92,7 @@ class XcpLogFileWriter { protected: - void resize(off_t size, bool remap = false) { + void resize(std::uint64_t size, bool remap = false) { std::error_code ec; if (remap) { @@ -104,8 +104,15 @@ class XcpLogFileWriter { } #if defined(_WIN32) - if (SetFilePointer(m_fd, size, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { - std::cout << error_string("SetFilePointer", get_last_error()); + LONG low_part = (MASK32 & size); + LONG high_part = size >> 32; + + if (SetFilePointer(m_fd, low_part, &high_part, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + auto err = get_last_error(); + + if (err.value() != NO_ERROR) { + std::cout << error_string("SetFilePointer", err); + } } if (SetEndOfFile(m_fd) == 0) { std::cout << error_string("SetEndOfFile", get_last_error()); @@ -148,10 +155,9 @@ class XcpLogFileWriter { if (cp_size < 0) { throw std::runtime_error("LZ4 compression failed."); } - std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); if (m_offset > (m_hard_limit >> 1)) { - std::cout << "[INFO] " << std::ctime(&now) << ": Doubling measurement file size." << std::endl; + std::cout << "[INFO] " << current_timestamp() << ": Doubling measurement file size." << std::endl; m_hard_limit <<= 1; resize(m_hard_limit, true); write_header( @@ -183,8 +189,8 @@ class XcpLogFileWriter { } void write_header( - uint16_t version, uint16_t options, uint32_t num_containers, uint32_t record_count, uint32_t size_compressed, - uint32_t size_uncompressed + std::uint16_t version, std::uint16_t options, std::uint64_t num_containers, std::uint64_t record_count, + std::uint64_t size_compressed, std::uint64_t size_uncompressed ) { auto header = FileHeaderType{}; write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); @@ -260,7 +266,7 @@ class XcpLogFileWriter { std::uint64_t m_container_size_uncompressed{ 0UL }; std::uint64_t m_container_size_compressed{ 0UL }; __ALIGN blob_t *m_intermediate_storage{ nullptr }; - std::uint64_t m_intermediate_storage_offset{ 0 }; + std::uint32_t m_intermediate_storage_offset{ 0 }; std::uint64_t m_hard_limit{ 0 }; mio::file_handle_type m_fd{ INVALID_HANDLE_VALUE }; mio::mmap_sink *m_mmap{ nullptr }; From f6c9c5ab87d1614610f49d140e9e07fd2551a7c3 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 6 Jun 2024 18:28:59 +0300 Subject: [PATCH 45/99] today() --- docs/recorder.rst | 32 ++++++++++++++++++++++++++++++++ pyxcp/recorder/unfolder.hpp | 8 -------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/docs/recorder.rst b/docs/recorder.rst index 1e40516..ac2c159 100644 --- a/docs/recorder.rst +++ b/docs/recorder.rst @@ -1,4 +1,34 @@ <<<<<<< Updated upstream +<<<<<<< Updated upstream + +Recorder +======== + + +:py:class:`pyxcp.recorder.XcpLogFileWriter` + +:py:meth:`pyxcp.recorder.XcpLogFileWritermymethod` + +Creating recipes +---------------- + +To retrieve a list of random ingredients, +you can use the ``lumache.get_random_ingredients()`` function: + +.. py:function:: lumache.get_random_ingredients(kind=None) + + Return a list of random ingredients as strings. + + :param kind: Optional "kind" of ingredients. + :type kind: list[str] or None + :return: The ingredients list. + :rtype: list[str] + +The ``kind`` parameter should be either ``"meat"``, ``"fish"``, +or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients` +will raise an exception. +||||||| Stash base +======= Recorder ======== @@ -26,6 +56,8 @@ you can use the ``lumache.get_random_ingredients()`` function: The ``kind`` parameter should be either ``"meat"``, ``"fish"``, or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients` will raise an exception. + +>>>>>>> Stashed changes ||||||| Stash base ======= diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 83e2117..022cdcf 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -1171,12 +1171,4 @@ class XcpLogFileUnfolder { MeasurementParameters m_params; }; -#if 0 - wrap.obj : error LNK2001: Nicht aufgel”stes externes Symbol ""public: virtual void __cdecl XcpLogFileUnfolder::on_daq_list( - unsigned short,double,double, - class std::vector,class std::allocator > >, - class std::allocator,class std::allocator > > > > const &) - " (?on_daq_list@XcpLogFileUnfolder@@UEAAXGNNAEBV?$vector@V?$variant@_J_KOV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@std@@V?$allocator@V?$variant@_J_KOV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@std@@@2@@std@@@Z)". -#endif - #endif // RECORDER_UNFOLDER_HPP From d8f841e264d762dfd62c4e885ccd27e2ae641e2e Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Fri, 7 Jun 2024 09:06:53 +0300 Subject: [PATCH 46/99] partially fix reinterpret_cast --- pyxcp/recorder/unfolder.hpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 022cdcf..ee3be29 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -33,7 +33,8 @@ template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> std::float16_t { auto tmp = get_value(buf, offset); - return *(reinterpret_cast(&tmp)); + //return *(reinterpret_cast(&tmp)); + return std::bit_cast(tmp); } #endif @@ -42,7 +43,8 @@ template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { auto tmp = get_value(buf, offset); - return *(reinterpret_cast(&tmp)); + //return *(reinterpret_cast(&tmp)); + return std::bit_cast(tmp); } #endif @@ -50,14 +52,16 @@ template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> float { auto tmp = get_value(buf, offset); - return *(reinterpret_cast(&tmp)); + //return *(reinterpret_cast(&tmp)); + return std::bit_cast(tmp); } template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> double { auto tmp = get_value(buf, offset); - return *(reinterpret_cast(&tmp)); + //return *(reinterpret_cast(&tmp)); + return std::bit_cast(tmp); } #if HAS_FLOAT16 == 1 @@ -65,7 +69,8 @@ template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::float16_t { auto tmp = get_value_swapped(buf, offset); - return *(reinterpret_cast(&tmp)); + //return *(reinterpret_cast(&tmp)); + return std::bit_cast(tmp); } #endif @@ -74,7 +79,8 @@ template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { auto tmp = get_value_swapped(buf, offset); - return *(reinterpret_cast(&tmp)); + //return *(reinterpret_cast(&tmp)); + return std::bit_cast(tmp); } #endif @@ -82,14 +88,16 @@ template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> float { auto tmp = get_value_swapped(buf, offset); - return *(reinterpret_cast(&tmp)); + //return *(reinterpret_cast(&tmp)); + return std::bit_cast(tmp); } template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> double { auto tmp = get_value_swapped(buf, offset); - return *(reinterpret_cast(&tmp)); + //return *(reinterpret_cast(&tmp)); + return std::bit_cast(tmp); } template<> From 939d583c95c1a5dd597eb9a7abfbad434cf0cc17 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Fri, 7 Jun 2024 10:56:05 +0300 Subject: [PATCH 47/99] today() --- pyxcp/recorder/unfolder.hpp | 32 ++++++++++++++++---------------- pyxcp/recorder/writer.hpp | 18 +++++++++++------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index ee3be29..d143eaf 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -33,7 +33,6 @@ template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> std::float16_t { auto tmp = get_value(buf, offset); - //return *(reinterpret_cast(&tmp)); return std::bit_cast(tmp); } #endif @@ -43,7 +42,6 @@ template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { auto tmp = get_value(buf, offset); - //return *(reinterpret_cast(&tmp)); return std::bit_cast(tmp); } #endif @@ -52,7 +50,6 @@ template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> float { auto tmp = get_value(buf, offset); - //return *(reinterpret_cast(&tmp)); return std::bit_cast(tmp); } @@ -60,7 +57,6 @@ template<> auto get_value(blob_t const * buf, std::uint64_t offset) -> double { auto tmp = get_value(buf, offset); - //return *(reinterpret_cast(&tmp)); return std::bit_cast(tmp); } @@ -69,7 +65,6 @@ template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::float16_t { auto tmp = get_value_swapped(buf, offset); - //return *(reinterpret_cast(&tmp)); return std::bit_cast(tmp); } #endif @@ -79,7 +74,6 @@ template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { auto tmp = get_value_swapped(buf, offset); - //return *(reinterpret_cast(&tmp)); return std::bit_cast(tmp); } #endif @@ -88,7 +82,6 @@ template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> float { auto tmp = get_value_swapped(buf, offset); - //return *(reinterpret_cast(&tmp)); return std::bit_cast(tmp); } @@ -96,7 +89,6 @@ template<> auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> double { auto tmp = get_value_swapped(buf, offset); - //return *(reinterpret_cast(&tmp)); return std::bit_cast(tmp); } @@ -185,45 +177,53 @@ void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int #if HAS_FLOAT16 == 1 template<> void set_value(blob_t* buf, std::uint64_t offset, std::float16_t value) { - set_value(buf, offset, *reinterpret_cast(&value)); + //set_value(buf, offset, *reinterpret_cast(&value)); + set_value(buf, offset, std::bit_cast(value)); } template<> void set_value_swapped(blob_t* buf, std::uint64_t offset, std::float16_t value) { - set_value_swapped(buf, offset, *reinterpret_cast(&value)); + //set_value_swapped(buf, offset, *reinterpret_cast(&value)); + set_value_swapped(buf, offset, std::bit_cast(value)); } #endif #if HAS_BFLOAT16 == 1 template<> void set_value(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { - set_value(buf, offset, *reinterpret_cast(&value)); + //set_value(buf, offset, *reinterpret_cast(&value)); + set_value(buf, offset, std::bit_cast(value)); } template<> void set_value_swapped(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { - set_value_swapped(buf, offset, *reinterpret_cast(&value)); + //set_value_swapped(buf, offset, *reinterpret_cast(&value)); + set_value_swapped(buf, offset, std::bit_cast(value)); } #endif template<> void set_value(blob_t* buf, std::uint64_t offset, float value) { - set_value(buf, offset, *reinterpret_cast(&value)); + //set_value(buf, offset, *reinterpret_cast(&value)); + set_value(buf, offset, std::bit_cast(value)); } template<> void set_value_swapped(blob_t* buf, std::uint64_t offset, float value) { - set_value_swapped(buf, offset, *reinterpret_cast(&value)); + //set_value_swapped(buf, offset, *reinterpret_cast(&value)); + set_value_swapped(buf, offset, std::bit_cast(value)); } template<> void set_value(blob_t* buf, std::uint64_t offset, double value) { - set_value(buf, offset, *reinterpret_cast(&value)); + //set_value(buf, offset, *reinterpret_cast(&value)); + set_value(buf, offset, std::bit_cast(value)); } template<> void set_value_swapped(blob_t* buf, std::uint64_t offset, double value) { - set_value_swapped(buf, offset, *reinterpret_cast(&value)); + //set_value_swapped(buf, offset, *reinterpret_cast(&value)); + set_value_swapped(buf, offset, std::bit_cast(value)); } /* diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 733ce31..a6eb47f 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -130,7 +130,7 @@ class XcpLogFileWriter { } } - blob_t *ptr(std::uint32_t pos = 0) const { + blob_t *ptr(std::uint64_t pos = 0) const { return (blob_t *)(m_mmap->data() + pos); } @@ -148,7 +148,8 @@ class XcpLogFileWriter { // printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); const int cp_size = ::LZ4_compress_HC( reinterpret_cast(m_intermediate_storage), - reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), m_intermediate_storage_offset, + reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), + m_intermediate_storage_offset, LZ4_COMPRESSBOUND(m_intermediate_storage_offset), LZ4HC_CLEVEL_MAX ); @@ -169,7 +170,10 @@ class XcpLogFileWriter { container.size_compressed = cp_size; container.size_uncompressed = m_container_size_uncompressed; - _fcopy(reinterpret_cast(ptr(m_offset)), reinterpret_cast(&container), detail::CONTAINER_SIZE); + _fcopy(reinterpret_cast(ptr(m_offset)), + reinterpret_cast(&container), + detail::CONTAINER_SIZE + ); m_offset += (detail::CONTAINER_SIZE + cp_size); m_total_size_uncompressed += m_container_size_uncompressed; @@ -256,15 +260,15 @@ class XcpLogFileWriter { std::string m_file_name; std::uint64_t m_offset{ 0 }; - std::uint64_t m_chunk_size{ 0 }; + std::uint32_t m_chunk_size{ 0 }; std::string m_metadata; std::uint64_t m_num_containers{ 0 }; std::uint64_t m_record_count{ 0UL }; - std::uint64_t m_container_record_count{ 0UL }; + std::uint32_t m_container_record_count{ 0UL }; std::uint64_t m_total_size_uncompressed{ 0UL }; std::uint64_t m_total_size_compressed{ 0UL }; - std::uint64_t m_container_size_uncompressed{ 0UL }; - std::uint64_t m_container_size_compressed{ 0UL }; + std::uint32_t m_container_size_uncompressed{ 0UL }; + std::uint32_t m_container_size_compressed{ 0UL }; __ALIGN blob_t *m_intermediate_storage{ nullptr }; std::uint32_t m_intermediate_storage_offset{ 0 }; std::uint64_t m_hard_limit{ 0 }; From 361d29fb293cce43d1b8121c26c4be427b9dfada Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Fri, 7 Jun 2024 12:00:35 +0300 Subject: [PATCH 48/99] fix type issues --- pyxcp/recorder/unfolder.hpp | 17 +++++++++-------- pyxcp/recorder/wrap.cpp | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index d143eaf..6697b0f 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -701,7 +701,7 @@ class Deserializer { std::vector initializer_list{}; - name = from_binary(); + name = from_binary_str(); event_num = from_binary(); stim = from_binary(); enable_timestamps = from_binary(); @@ -725,7 +725,7 @@ class Deserializer { std::size_t hname_size = from_binary(); for (std::size_t i = 0; i < hname_size; ++i) { - auto header = from_binary(); + auto header = from_binary_str(); header_names.push_back(header); } @@ -750,7 +750,7 @@ class Deserializer { std::vector> flatten_odt{}; std::size_t odt_entry_count = from_binary(); for (std::size_t j = 0; j < odt_entry_count; ++j) { - name = from_binary(); + name = from_binary_str(); address = from_binary(); ext = from_binary(); size = from_binary(); @@ -772,11 +772,11 @@ class Deserializer { std::int16_t type_index; std::vector components{}; - name = from_binary(); + name = from_binary_str(); address = from_binary(); ext = from_binary(); length = from_binary(); - data_type = from_binary(); + data_type = from_binary_str(); type_index = from_binary(); // not used std::size_t comp_size = from_binary(); for (auto i = 0U; i < comp_size; i++) { @@ -808,8 +808,9 @@ class Deserializer { return tmp; } - template<> - inline std::string from_binary() { + //template<> + //inline std::string from_binary_str() { + inline std::string from_binary_str() { auto length = from_binary(); std::string result; auto start = m_buf.cbegin() + m_offset; @@ -1050,7 +1051,7 @@ class DaqRecorderPolicy : public DAQPolicyBase { m_writer->add_frame(frame_cat, counter, timestamp, static_cast(payload.size()), payload.c_str()); } - void create_writer(const std::string& file_name, std::uint64_t prealloc, std::uint64_t chunk_size, std::string_view metadata) { + void create_writer(const std::string& file_name, std::uint32_t prealloc, std::uint32_t chunk_size, std::string_view metadata) { m_writer = std::make_unique(file_name, prealloc, chunk_size, metadata); } diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 5ad9863..71e095a 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -99,7 +99,7 @@ PYBIND11_MODULE(rekorder, m) { py::class_(m, "_PyXcpLogFileWriter") .def( - py::init(), py::arg("filename"), + py::init(), py::arg("filename"), py::arg("prealloc"), py::arg("chunk_size"), py::arg("metadata") = "" ) .def("finalize", &XcpLogFileWriter::finalize) From 06b481f0d3c604bd9dbceb4254d89bd73b0fce6f Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sat, 8 Jun 2024 12:48:07 +0300 Subject: [PATCH 49/99] Update poetry --- poetry.lock | 1524 +++++++++++++++++++++++------------------------- pyproject.toml | 4 +- 2 files changed, 726 insertions(+), 802 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0d28ca0..480f45c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "alabaster" @@ -12,16 +12,19 @@ files = [ ] [[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + [[package]] name = "attrs" version = "23.2.0" @@ -33,9 +36,6 @@ files = [ {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[tests]", "pre-commit"] @@ -46,27 +46,27 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "authlib" -version = "1.2.1" +version = "1.3.1" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "Authlib-1.2.1-py2.py3-none-any.whl", hash = "sha256:c88984ea00149a90e3537c964327da930779afa4564e354edfd98410bea01911"}, - {file = "Authlib-1.2.1.tar.gz", hash = "sha256:421f7c6b468d907ca2d9afede256f068f87e34d23dd221c07d13d4c234726afb"}, + {file = "Authlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:d35800b973099bbadc49b42b256ecb80041ad56b7fe1216a362c7943c088f377"}, + {file = "authlib-1.3.1.tar.gz", hash = "sha256:7ae843f03c06c5c0debd63c9db91f9fda64fa62a42a77419fa15fbb7e7a58917"}, ] [package.dependencies] -cryptography = ">=3.2" +cryptography = "*" [[package]] name = "babel" -version = "2.14.0" +version = "2.15.0" description = "Internationalization utilities" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, + {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, + {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] [package.dependencies] @@ -77,24 +77,25 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "bandit" -version = "1.7.5" +version = "1.7.8" description = "Security oriented static analyser for python code." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, - {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, + {file = "bandit-1.7.8-py3-none-any.whl", hash = "sha256:509f7af645bc0cd8fd4587abc1a038fc795636671ee8204d502b933aee44f381"}, + {file = "bandit-1.7.8.tar.gz", hash = "sha256:36de50f720856ab24a24dbaa5fee2c66050ed97c1477e0a1159deab1775eab6b"}, ] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=1.0.1" PyYAML = ">=5.3.1" rich = "*" stevedore = ">=1.20.0" [package.extras] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] +baseline = ["GitPython (>=3.1.30)"] +sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] toml = ["tomli (>=1.1.0)"] yaml = ["PyYAML"] @@ -121,36 +122,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "23.3.0" +version = "24.4.2" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.7" -files = [ - {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, - {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, - {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, - {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, - {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, - {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, - {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, - {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, - {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, - {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, - {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, - {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, - {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, - {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, +python-versions = ">=3.8" +files = [ + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, ] [package.dependencies] @@ -160,97 +158,84 @@ packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] name = "cffi" -version = "1.15.1" +version = "1.16.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = "*" -files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, ] [package.dependencies] @@ -258,13 +243,13 @@ pycparser = "*" [[package]] name = "cfgv" -version = "3.3.1" +version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.8" files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] [[package]] @@ -390,7 +375,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -419,71 +403,63 @@ extras = ["arrow", "cloudpickle", "cryptography", "lz4", "numpy", "ruamel.yaml"] [[package]] name = "coverage" -version = "7.2.7" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.7" -files = [ - {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, - {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, - {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, - {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, - {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, - {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, - {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, - {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, - {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, - {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, - {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, - {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, - {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, - {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, - {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, - {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, - {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, - {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.dependencies] @@ -494,43 +470,43 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "42.0.5" +version = "42.0.8" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, - {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, - {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, - {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, - {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, - {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, - {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, + {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, + {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, + {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, + {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, + {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, + {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, ] [package.dependencies] @@ -570,13 +546,13 @@ files = [ [[package]] name = "docutils" -version = "0.17.1" +version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" files = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] @@ -602,13 +578,13 @@ poetry = ["poetry"] [[package]] name = "exceptiongroup" -version = "1.2.0" +version = "1.2.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] @@ -616,18 +592,19 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.12.2" +version = "3.14.0" description = "A platform independent file lock." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, - {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, ] [package.extras] -docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" @@ -641,7 +618,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = ">=1.1.0,<4.3", markers = "python_version < \"3.8\""} mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.9.0,<2.10.0" pyflakes = ">=2.5.0,<2.6.0" @@ -700,62 +676,30 @@ develop = ["build", "twine"] [[package]] name = "furo" -version = "2022.9.29" +version = "2024.5.6" description = "A clean customisable Sphinx documentation theme." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "furo-2022.9.29-py3-none-any.whl", hash = "sha256:559ee17999c0f52728481dcf6b1b0cf8c9743e68c5e3a18cb45a7992747869a9"}, - {file = "furo-2022.9.29.tar.gz", hash = "sha256:d4238145629c623609c2deb5384f8d036e2a1ee2a101d64b67b4348112470dbd"}, + {file = "furo-2024.5.6-py3-none-any.whl", hash = "sha256:490a00d08c0a37ecc90de03ae9227e8eb5d6f7f750edf9807f398a2bdf2358de"}, + {file = "furo-2024.5.6.tar.gz", hash = "sha256:81f205a6605ebccbb883350432b4831c0196dd3d1bc92f61e1f459045b3d2b0b"}, ] [package.dependencies] beautifulsoup4 = "*" pygments = ">=2.7" -sphinx = ">=4.0,<6.0" -sphinx-basic-ng = "*" - -[[package]] -name = "gitdb" -version = "4.0.11" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.42" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -files = [ - {file = "GitPython-3.1.42-py3-none-any.whl", hash = "sha256:1bf9cd7c9e7255f77778ea54359e54ac22a72a5b51288c457c881057b7bb9ecd"}, - {file = "GitPython-3.1.42.tar.gz", hash = "sha256:2d99869e0fef71a73cbd242528105af1d6c1b108c60dfabd994bf292f76c3ceb"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} - -[package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar"] +sphinx = ">=6.0,<8.0" +sphinx-basic-ng = ">=1.0.0.beta2" [[package]] name = "identify" -version = "2.5.24" +version = "2.5.36" description = "File identification library for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, - {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, ] [package.extras] @@ -763,13 +707,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] @@ -785,22 +729,22 @@ files = [ [[package]] name = "importlib-metadata" -version = "4.2.0" +version = "7.1.0" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, - {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" @@ -815,30 +759,27 @@ files = [ [[package]] name = "isort" -version = "5.11.5" +version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8.0" files = [ - {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, - {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] [package.extras] -colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] +colors = ["colorama (>=0.4.6)"] [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -849,75 +790,75 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "line-profiler" -version = "4.1.2" +version = "4.1.3" description = "Line-by-line profiler" optional = false python-versions = ">=3.6" files = [ - {file = "line_profiler-4.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4344c1504ad1a57029a8ab30812d967a0917cad7b654077e8787e4a7d7ea3469"}, - {file = "line_profiler-4.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0720b356db3e9ca297c3260f280c5be3bb4b230eda61ce73b4df5e553418d37a"}, - {file = "line_profiler-4.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:09f742af37166768f92495bd3d3a71da1ba41d3004307a66c108f29ed947d6e1"}, - {file = "line_profiler-4.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:443a5df10eb7910df694340c8a81c1668a88bb59ca44149a3291f7b2ae960891"}, - {file = "line_profiler-4.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a906f9d1687eea7e5b22e3bd367d4b63706fcea1906baaad76b1cc4c1142553d"}, - {file = "line_profiler-4.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3b2c8cc34a776c5cfaa4a4a09a51541efcc9082dce15b19e494000e82576ced"}, - {file = "line_profiler-4.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:55ca0a78eb8d52515486c374ec53fa9e65e3c4128e8bbc909d8bfce267a91fdd"}, - {file = "line_profiler-4.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:f4a11389f06831d7984b63be0743fbbbae1ffb56fad04b4e538d3e6933b5c265"}, - {file = "line_profiler-4.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:32fa07f6fecfd209329559e4ae945dc7bdc0703355c8924bbf19101495b2373f"}, - {file = "line_profiler-4.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f8e9e8af6660629f214e424613c56a6622cf36d9c638c569c926b21374d7029"}, - {file = "line_profiler-4.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4753113c4e2c30a547937dbc456900d7f3a1b99bc8bc81a640a89306cd729c0f"}, - {file = "line_profiler-4.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f0989302404850a2a041ba60afe6c7240aea10fdd9432d5c1d464aca39a0369"}, - {file = "line_profiler-4.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f4b25ee412b0cd624614edd16c4c0af02dbeb73db2a08a49a14b120005a5630"}, - {file = "line_profiler-4.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93c6a49009ee75dcd8ff644c5fd39eeb8bb672d5a41bacdd239db14ae1ba3098"}, - {file = "line_profiler-4.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b96964cdb306741a01b95d210d634cc79ed70d2904336cbd8f69a9b5f284426d"}, - {file = "line_profiler-4.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:46a8cad2cb4b6a1229ddccf06694b1d01fd5acd1cf8c502caf937765a7c877de"}, - {file = "line_profiler-4.1.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a102fd8e13abd367379e39fd9426fd60e1e3a39fcd80fa25641618969464c022"}, - {file = "line_profiler-4.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44ee51bce974d6b2269492299d4abae6db1b06ae7617760c7436c597dbdbd032"}, - {file = "line_profiler-4.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e4cafd9a1effe1b9646f6a86716dbd291684fde1f8a297930d845d8a9340299"}, - {file = "line_profiler-4.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b433a2918e522d6dd0e6bdcf1216cede15c4f201f7eeb0d816114fbac5031cd7"}, - {file = "line_profiler-4.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad96accb1f5cdedfe2e6607f9be86d28196d3f743229e2b67bd28a40f76f133"}, - {file = "line_profiler-4.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4eb9df035861f7c2e9852773dff72a3324e2e5aebc0b8c7c2ba22437387ef5e7"}, - {file = "line_profiler-4.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e733c0e6626d0e9f1b434da40b93ed1c00ea503f3ced04f5a58c22d1163fe1c1"}, - {file = "line_profiler-4.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:8cc0c24384e29e99da5627669dbf312a23d11138de0169aa58d4ea5187522ba0"}, - {file = "line_profiler-4.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:900ad7be6d609fb1442200c7757de3534b381d6eeac22fa0135c5d0a900b5787"}, - {file = "line_profiler-4.1.2-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49c6c6e19c3c0d7cc8f1641ece9e52fec5e99c56472e26156c16473b7568d374"}, - {file = "line_profiler-4.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7ed1edd85f9a005a3e1316b3962a5fc42a159257cf2dfd13d10fcbefaece8ce"}, - {file = "line_profiler-4.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:2ed7027f7d1b3ae9a379a2f407f512b84ccf82d6a3a7b53a90bb17ada61928a9"}, - {file = "line_profiler-4.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e8537be16b46133ab86d6e805ca83b012b17ef36a7445dd5c89c45ba70b97aad"}, - {file = "line_profiler-4.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:934870b5e451c938f149c5475cc0286133d8718ba99ff4ec04fb1a87f7bfb985"}, - {file = "line_profiler-4.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbda8e0bb98b1790ba8819d0a72ee3e11e669c79fc703eaf0e5ed747cac2d441"}, - {file = "line_profiler-4.1.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cfd263c79927f74f174e32b83e4692e26ada2fefcdfef0c1dae5cfabb37a37"}, - {file = "line_profiler-4.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:390f5e5dc047a62ffb7dbd236b4d44c6175d4f66aabe654f4b35df9b9aa79d02"}, - {file = "line_profiler-4.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dce014572ee599b2d571cf45fbd0c7d5f1a1e822dabe82581e18dd0229b16799"}, - {file = "line_profiler-4.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4fe92a239d8097a3a0cacb280e0a2455be6633da3c844b784ba011043d090b36"}, - {file = "line_profiler-4.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:3df9b30cdd8b3652e658acb38a9533bac47f2b8f5c320c5a03dbdd378ac11b35"}, - {file = "line_profiler-4.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5643cb19c89f6749039452913803a8cfb554c07676f6c00bc96e0632a054abb6"}, - {file = "line_profiler-4.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:163d26586511b68551735052a1bcca173c9d8366573ab4a91c470c7f7bd89967"}, - {file = "line_profiler-4.1.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8fa3128e93e49ad8b5216e40dc9d2bc2e354e896c1512feead3d6db1668ce649"}, - {file = "line_profiler-4.1.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a1eb88cec273300377b364eee9ceffce2e639906bf210e7d7233c88dc87e62f"}, - {file = "line_profiler-4.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7f213eeb846c9bc950fd210dfcd0fa93b1d2991f218b8788c0759f06bd00557"}, - {file = "line_profiler-4.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ec6f137dbbdc0af6b88a1053c1430681c07a3b2d1719dc1f59be70d464851a23"}, - {file = "line_profiler-4.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3af457b2dfad6e2019f7e5bbe9eabac9b2c34824fb2ea574aee7b17998c48c98"}, - {file = "line_profiler-4.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:9dd72adc753019788ff0498dd686068c4d8e65d38c0eca1b4b58b5719c14fa7d"}, - {file = "line_profiler-4.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:62776d67dfc6c358de5c19d606eccbd95e6feb75928064850be0232e9276f751"}, - {file = "line_profiler-4.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:060d71ba11ff5476d7c10774a34955566bab545ab5ff39231306b4d84081725d"}, - {file = "line_profiler-4.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ad13e1d5a174336508bbf275202822c8898cd1f014881059103b748310d5bc84"}, - {file = "line_profiler-4.1.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77824dfc1f58dc7fe62fb053aa54586979ef60fea221dcdbba2022608c1314f"}, - {file = "line_profiler-4.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8ffc44a030789f7bc6594de581b39e8da0591fc6c598dd4243cf140b200528"}, - {file = "line_profiler-4.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4729820d8da3ed92f14e30dbd28a851eeefe2ba70b8b897f2d9c886ade8007c1"}, - {file = "line_profiler-4.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0bce5c04d0daf6dd19348540012b0a6d69206ae40db096de222e6d5f824922e8"}, - {file = "line_profiler-4.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:a65b70d6ecef4f2e61cf504a5c77085718f1dae9508b21c9058ad483ae7e16ee"}, - {file = "line_profiler-4.1.2.tar.gz", hash = "sha256:aa56578b0ff5a756fe180b3fda7bd67c27bbd478b3d0124612d8cf00e4a21df2"}, + {file = "line_profiler-4.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b26cccca30c0f859c585cd4a6c75ffde4dca80ba98a858d3d04b44a6b560c65"}, + {file = "line_profiler-4.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e8a1ed7bf88049cb8d069a2dac96c91b25b5a77cb712c207b7f484ab86f8b134"}, + {file = "line_profiler-4.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c320a8ccb2b9d0df85b8f19000242407d0cb1ea5804b4967fe6f755824c81a87"}, + {file = "line_profiler-4.1.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5751939d9dd95b1ec74e0aee428fe17d037fcb346fd23a7bf928b71c2dca2d19"}, + {file = "line_profiler-4.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b45f405d63730e5284403c1ff293f1e7f8ac7a39486db4c55a858712cec333d"}, + {file = "line_profiler-4.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9e24d61810ad153ab6a795d68f735812de4131f282128b799467f7fa56cac94f"}, + {file = "line_profiler-4.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f961465381e5bdc9fa7e5597af6714ada700d3e6ca61cca56763477f1047ff23"}, + {file = "line_profiler-4.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:6112436cb48ab635bc64e3dbfd80f67b56967e72aa7853e5084a64e11be5fe65"}, + {file = "line_profiler-4.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:16c8d2830e9daf0bcd49422e9367db5c825b02b88c383b9228c281ce14a5ad80"}, + {file = "line_profiler-4.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e3ed5dd55bda1b0f65893ff377b6aedae69490f7be4fd5d818dd5bcc75553bf"}, + {file = "line_profiler-4.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f0ad37589b270e59f65ec6704435f02ece6d4246af112c0413095a5d3b13285b"}, + {file = "line_profiler-4.1.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c29ef65e3e0085f20ffedcddfa8d02f6f6eaa0dacec29129cd74d206f9f6c"}, + {file = "line_profiler-4.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ef054e1b6fd2443341911a2ddad0f8b6ed24903fa6a7e5e8201cd4272132e3a"}, + {file = "line_profiler-4.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:02bc0650ef8f87a489d6fbafcc0040ca76144d2a4c40e4044babccfe769b5525"}, + {file = "line_profiler-4.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f032c0973f0c1150440dce5f9b91509fce474c11b10c2c93a2109e1e0dab8a45"}, + {file = "line_profiler-4.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:ec8a34285338aadc6a74e91b022b6d8ea19ac5deaaa0c9b880a1ab7b4ed45c43"}, + {file = "line_profiler-4.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8ae10578f1325772ccfa2833288d826e4bc781214d74b87331a6b7e5793252ca"}, + {file = "line_profiler-4.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b7c89c68379879d3a11c5e76499f0f7a08683436762af6bf51db126d3cb9cdd9"}, + {file = "line_profiler-4.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9f4abf9ecb8b508d96420dde44d54a8484e73468132229bbba2229283a7e9fb"}, + {file = "line_profiler-4.1.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d12bf40ed654ad1d5c132be172054b9ec5ae3ba138ca2099002075fb14396a64"}, + {file = "line_profiler-4.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56d17f3bf22b9c7d72b3cb2d283d71152f4cc98e8ba88e720c743b2e3d9be6ad"}, + {file = "line_profiler-4.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9d7c7593ae86215d99d1d32e4b92ed6ace2ac8388aab781b74bf97d44e72ff1f"}, + {file = "line_profiler-4.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:248f16ba356ac1e19be834b0bdaf29c95c1c9229beaa63e0e3aad9aa3edfc012"}, + {file = "line_profiler-4.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:b85468d30ed16e362e8a044df0f331796c6ec5a76a55e88aae57078a2eec6afa"}, + {file = "line_profiler-4.1.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:82d5333d1ffac08b34828213bd674165e50876610061faa97660928b346a620d"}, + {file = "line_profiler-4.1.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f56985a885e2936eab6303fc82f1a20e5e0bb6d4d8f44f8a3825179d261053e"}, + {file = "line_profiler-4.1.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:713d43be1382f47c2f04d5d25ba3c65978292249849f85746a8476d6a8863717"}, + {file = "line_profiler-4.1.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6a3dd7ba3a17da254338313ec1d4ce4bdd723812e5cb58f4d05b78c1c5dbe4"}, + {file = "line_profiler-4.1.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:481bbace88b2e15fb63a16e578a48faa28eba7399afe7da6ce1bde569780c346"}, + {file = "line_profiler-4.1.3-cp36-cp36m-win_amd64.whl", hash = "sha256:654b16f9e82b0ce7f7657ef859bf2324275e9cd70c8169414922c9cb37d5589f"}, + {file = "line_profiler-4.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:39332137af7a562c44524cef7c37de9860428ce2cde8b9c51047ccad9fd5eca4"}, + {file = "line_profiler-4.1.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dad96626acd5804c818c374d34ce1debea07b1e100b160499f4dfbcf5fc1cbe6"}, + {file = "line_profiler-4.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7125846d636959907e307c1f0bbf6f05fe5b7ca195b929f7b676fd20cf0763f2"}, + {file = "line_profiler-4.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a89de2a09363dd1a62a0a49e82a7157854b6e92b1893627b14e952412357db60"}, + {file = "line_profiler-4.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9e11f5831a251d3a3551372b523b3bc0da1e912ab2ade2c4d9d8e0b225eed6ab"}, + {file = "line_profiler-4.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:66d856975284dc62ac6f5a97757e160c1eb9898078014385cf74b829d8d806b7"}, + {file = "line_profiler-4.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3fb0f43900d36d7ccd8b30b8506498440d5ec610f2f1d40de3de11c3e304fb90"}, + {file = "line_profiler-4.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7394227bfb5bf15002d3695e674916fe82c38957cd2f56fccd43b71dc3447d1e"}, + {file = "line_profiler-4.1.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8e19a0ca3198b173a5b7caa304be3b39d122f89b0cfc2a134c5cbb4105ee2fd6"}, + {file = "line_profiler-4.1.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ad57e3c80fb0aee0c86a25d738e3556063eb3d57d0a43217de13f134417915d"}, + {file = "line_profiler-4.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cca919a8199236326f14f3719e992f30dd43a272b0e8fcb98e436a66e4a96fc"}, + {file = "line_profiler-4.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d6753834e1ea03ea19015d0553f0ce0d61bbf2269b85fc0f42833d616369488b"}, + {file = "line_profiler-4.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a32559afd550852f2054a441d33afe16e8b68b167ffb15373ec2b521c6fdc51f"}, + {file = "line_profiler-4.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:e526f9dfad5e8e21cd5345d5213757cfc26af33f072042f3ccff36b10c46a23c"}, + {file = "line_profiler-4.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5aec873bea3a1357c1a21f788b44d29e288df2a579b4433c8a85fc2b0a8c229d"}, + {file = "line_profiler-4.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6059a8960487fc1e7b333178d39c53d3de5fd3c7da04477019e70d13c4c8520c"}, + {file = "line_profiler-4.1.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2ac815ba3cdc8603de6b0ea57a725f4aea1e0a2b7d8c99fabb43f6f2b1670dc0"}, + {file = "line_profiler-4.1.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ebd58a953fa86384150b79638331133ef0c22d8d68f046e00fe97e62053edae"}, + {file = "line_profiler-4.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c91e4cb038496e771220daccb512dab5311619392fec59ea916e9316630e9825"}, + {file = "line_profiler-4.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b4e4a49a42d4d9e1dce122dd0a5a427f9a337c22cf8a82712f006cae038870bf"}, + {file = "line_profiler-4.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:209d41401238eb0da340f92dfaf60dd84500be475b2b6738cf0ef28579b4df9a"}, + {file = "line_profiler-4.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:68684974e81344344174caf723bb4ab6659bc186d05c8f7e2453002e6bf74cff"}, + {file = "line_profiler-4.1.3.tar.gz", hash = "sha256:e5f1123c3672c3218ba063c23bd64a51159e44649fed6780b993c781fb5ed318"}, ] [package.extras] -all = ["Cython (>=3.0.3)", "IPython (>=7.14.0)", "IPython (>=7.18.0)", "IPython (>=8.12.2)", "IPython (>=8.14.0)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.8.1)", "cmake (>=3.21.2)", "coverage[toml] (>=5.3)", "ninja (>=1.10.2)", "pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=4.6.0,<=4.6.11)", "pytest (>=4.6.0,<=4.6.11)", "pytest (>=4.6.0,<=6.1.2)", "pytest (>=6.2.5)", "pytest-cov (>=2.8.1)", "pytest-cov (>=2.8.1)", "pytest-cov (>=2.9.0)", "pytest-cov (>=3.0.0)", "rich (>=12.3.0)", "scikit-build (>=0.11.1)", "setuptools (>=41.0.1)", "setuptools (>=68.2.2)", "ubelt (>=1.3.4)", "xdoctest (>=1.1.2)"] -all-strict = ["Cython (==3.0.3)", "IPython (==7.14.0)", "IPython (==7.18.0)", "IPython (==8.12.2)", "IPython (==8.14.0)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.8.1)", "cmake (==3.21.2)", "coverage[toml] (==5.3)", "ninja (==1.10.2)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "rich (==12.3.0)", "scikit-build (==0.11.1)", "setuptools (==41.0.1)", "setuptools (==68.2.2)", "ubelt (==1.3.4)", "xdoctest (==1.1.2)"] +all = ["Cython (>=3.0.3)", "IPython (>=7.14.0)", "IPython (>=7.18.0)", "IPython (>=8.12.2)", "IPython (>=8.14.0)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.11.2)", "cibuildwheel (>=2.8.1)", "cmake (>=3.21.2)", "coverage[toml] (>=6.1.1)", "coverage[toml] (>=6.5.0)", "coverage[toml] (>=6.5.0)", "coverage[toml] (>=6.5.0)", "coverage[toml] (>=6.5.0)", "coverage[toml] (>=7.3.0)", "ninja (>=1.10.2)", "pytest (>=6.2.5)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest-cov (>=3.0.0)", "rich (>=12.3.0)", "scikit-build (>=0.11.1)", "setuptools (>=41.0.1)", "setuptools (>=68.2.2)", "ubelt (>=1.3.4)", "xdoctest (>=1.1.3)"] +all-strict = ["Cython (==3.0.3)", "IPython (==7.14.0)", "IPython (==7.18.0)", "IPython (==8.12.2)", "IPython (==8.14.0)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.11.2)", "cibuildwheel (==2.8.1)", "cmake (==3.21.2)", "coverage[toml] (==6.1.1)", "coverage[toml] (==6.5.0)", "coverage[toml] (==6.5.0)", "coverage[toml] (==6.5.0)", "coverage[toml] (==6.5.0)", "coverage[toml] (==7.3.0)", "ninja (==1.10.2)", "pytest (==6.2.5)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest-cov (==3.0.0)", "rich (==12.3.0)", "scikit-build (==0.11.1)", "setuptools (==41.0.1)", "setuptools (==68.2.2)", "ubelt (==1.3.4)", "xdoctest (==1.1.3)"] ipython = ["IPython (>=7.14.0)", "IPython (>=7.18.0)", "IPython (>=8.12.2)", "IPython (>=8.14.0)"] ipython-strict = ["IPython (==7.14.0)", "IPython (==7.18.0)", "IPython (==8.12.2)", "IPython (==8.14.0)"] optional = ["IPython (>=7.14.0)", "IPython (>=7.18.0)", "IPython (>=8.12.2)", "IPython (>=8.14.0)", "rich (>=12.3.0)"] optional-strict = ["IPython (==7.14.0)", "IPython (==7.18.0)", "IPython (==8.12.2)", "IPython (==8.14.0)", "rich (==12.3.0)"] -tests = ["coverage[toml] (>=5.3)", "pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=4.6.0,<=4.6.11)", "pytest (>=4.6.0,<=4.6.11)", "pytest (>=4.6.0,<=6.1.2)", "pytest (>=6.2.5)", "pytest-cov (>=2.8.1)", "pytest-cov (>=2.8.1)", "pytest-cov (>=2.9.0)", "pytest-cov (>=3.0.0)", "ubelt (>=1.3.4)", "xdoctest (>=1.1.2)"] -tests-strict = ["coverage[toml] (==5.3)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "ubelt (==1.3.4)", "xdoctest (==1.1.2)"] +tests = ["coverage[toml] (>=6.1.1)", "coverage[toml] (>=6.5.0)", "coverage[toml] (>=6.5.0)", "coverage[toml] (>=6.5.0)", "coverage[toml] (>=6.5.0)", "coverage[toml] (>=7.3.0)", "pytest (>=6.2.5)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest (>=7.4.4)", "pytest-cov (>=3.0.0)", "ubelt (>=1.3.4)", "xdoctest (>=1.1.3)"] +tests-strict = ["coverage[toml] (==6.1.1)", "coverage[toml] (==6.5.0)", "coverage[toml] (==6.5.0)", "coverage[toml] (==6.5.0)", "coverage[toml] (==6.5.0)", "coverage[toml] (==7.3.0)", "pytest (==6.2.5)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest (==7.4.4)", "pytest-cov (==3.0.0)", "ubelt (==1.3.4)", "xdoctest (==1.1.3)"] [[package]] name = "line-profiler-pycharm" @@ -950,17 +891,16 @@ tornado = {version = "*", markers = "python_version > \"2.7\""} [[package]] name = "mako" -version = "1.2.4" +version = "1.3.5" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, - {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, + {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, + {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, ] [package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} MarkupSafe = ">=0.9.2" [package.extras] @@ -970,18 +910,17 @@ testing = ["pytest"] [[package]] name = "markdown-it-py" -version = "2.2.0" +version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, - {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] [package.dependencies] mdurl = ">=0.1,<1.0" -typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] @@ -990,7 +929,7 @@ compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0 linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins"] profiling = ["gprof2dot"] -rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] @@ -1064,22 +1003,21 @@ files = [ [[package]] name = "marshmallow" -version = "3.19.0" +version = "3.21.3" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "marshmallow-3.19.0-py3-none-any.whl", hash = "sha256:93f0958568da045b0021ec6aeb7ac37c81bfcccbb9a0e7ed8559885070b3a19b"}, - {file = "marshmallow-3.19.0.tar.gz", hash = "sha256:90032c0fd650ce94b6ec6dc8dfeb0e3ff50c144586462c389b81a07205bedb78"}, + {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"}, + {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"}, ] [package.dependencies] packaging = ">=17.0" [package.extras] -dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.3.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)"] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -1095,21 +1033,21 @@ files = [ [[package]] name = "mdit-py-plugins" -version = "0.3.5" +version = "0.4.1" description = "Collection of plugins for markdown-it-py" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mdit-py-plugins-0.3.5.tar.gz", hash = "sha256:eee0adc7195e5827e17e02d2a258a2ba159944a0748f59c5099a4a27f78fcf6a"}, - {file = "mdit_py_plugins-0.3.5-py3-none-any.whl", hash = "sha256:ca9a0714ea59a24b2b044a1831f48d817dd0c817e84339f20e7889f392d77c4e"}, + {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, + {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, ] [package.dependencies] -markdown-it-py = ">=1.0.0,<3.0.0" +markdown-it-py = ">=1.0.0,<4.0.0" [package.extras] code-style = ["pre-commit"] -rtd = ["attrs", "myst-parser (>=0.16.1,<0.17.0)", "sphinx-book-theme (>=0.1.0,<0.2.0)"] +rtd = ["myst-parser", "sphinx-book-theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] @@ -1125,121 +1063,114 @@ files = [ [[package]] name = "msgpack" -version = "1.0.5" +version = "1.0.8" description = "MessagePack serializer" optional = false -python-versions = "*" -files = [ - {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:525228efd79bb831cf6830a732e2e80bc1b05436b086d4264814b4b2955b2fa9"}, - {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f8d8b3bf1ff2672567d6b5c725a1b347fe838b912772aa8ae2bf70338d5a198"}, - {file = "msgpack-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdc793c50be3f01106245a61b739328f7dccc2c648b501e237f0699fe1395b81"}, - {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb47c21a8a65b165ce29f2bec852790cbc04936f502966768e4aae9fa763cb7"}, - {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42b9594cc3bf4d838d67d6ed62b9e59e201862a25e9a157019e171fbe672dd3"}, - {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55b56a24893105dc52c1253649b60f475f36b3aa0fc66115bffafb624d7cb30b"}, - {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1967f6129fc50a43bfe0951c35acbb729be89a55d849fab7686004da85103f1c"}, - {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a97bf595a232c3ee6d57ddaadd5453d174a52594bf9c21d10407e2a2d9b3bd"}, - {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d25dd59bbbbb996eacf7be6b4ad082ed7eacc4e8f3d2df1ba43822da9bfa122a"}, - {file = "msgpack-1.0.5-cp310-cp310-win32.whl", hash = "sha256:382b2c77589331f2cb80b67cc058c00f225e19827dbc818d700f61513ab47bea"}, - {file = "msgpack-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:4867aa2df9e2a5fa5f76d7d5565d25ec76e84c106b55509e78c1ede0f152659a"}, - {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f5ae84c5c8a857ec44dc180a8b0cc08238e021f57abdf51a8182e915e6299f0"}, - {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e6ca5d5699bcd89ae605c150aee83b5321f2115695e741b99618f4856c50898"}, - {file = "msgpack-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5494ea30d517a3576749cad32fa27f7585c65f5f38309c88c6d137877fa28a5a"}, - {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab2f3331cb1b54165976a9d976cb251a83183631c88076613c6c780f0d6e45a"}, - {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28592e20bbb1620848256ebc105fc420436af59515793ed27d5c77a217477705"}, - {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe5c63197c55bce6385d9aee16c4d0641684628f63ace85f73571e65ad1c1e8d"}, - {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed40e926fa2f297e8a653c954b732f125ef97bdd4c889f243182299de27e2aa9"}, - {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b2de4c1c0538dcb7010902a2b97f4e00fc4ddf2c8cda9749af0e594d3b7fa3d7"}, - {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf22a83f973b50f9d38e55c6aade04c41ddda19b00c4ebc558930d78eecc64ed"}, - {file = "msgpack-1.0.5-cp311-cp311-win32.whl", hash = "sha256:c396e2cc213d12ce017b686e0f53497f94f8ba2b24799c25d913d46c08ec422c"}, - {file = "msgpack-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c4c68d87497f66f96d50142a2b73b97972130d93677ce930718f68828b382e2"}, - {file = "msgpack-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a2b031c2e9b9af485d5e3c4520f4220d74f4d222a5b8dc8c1a3ab9448ca79c57"}, - {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f837b93669ce4336e24d08286c38761132bc7ab29782727f8557e1eb21b2080"}, - {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1d46dfe3832660f53b13b925d4e0fa1432b00f5f7210eb3ad3bb9a13c6204a6"}, - {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:366c9a7b9057e1547f4ad51d8facad8b406bab69c7d72c0eb6f529cf76d4b85f"}, - {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4c075728a1095efd0634a7dccb06204919a2f67d1893b6aa8e00497258bf926c"}, - {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:f933bbda5a3ee63b8834179096923b094b76f0c7a73c1cfe8f07ad608c58844b"}, - {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:36961b0568c36027c76e2ae3ca1132e35123dcec0706c4b7992683cc26c1320c"}, - {file = "msgpack-1.0.5-cp36-cp36m-win32.whl", hash = "sha256:b5ef2f015b95f912c2fcab19c36814963b5463f1fb9049846994b007962743e9"}, - {file = "msgpack-1.0.5-cp36-cp36m-win_amd64.whl", hash = "sha256:288e32b47e67f7b171f86b030e527e302c91bd3f40fd9033483f2cacc37f327a"}, - {file = "msgpack-1.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:137850656634abddfb88236008339fdaba3178f4751b28f270d2ebe77a563b6c"}, - {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c05a4a96585525916b109bb85f8cb6511db1c6f5b9d9cbcbc940dc6b4be944b"}, - {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a62ec00b636583e5cb6ad313bbed36bb7ead5fa3a3e38938503142c72cba4f"}, - {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef8108f8dedf204bb7b42994abf93882da1159728a2d4c5e82012edd92c9da9f"}, - {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1835c84d65f46900920b3708f5ba829fb19b1096c1800ad60bae8418652a951d"}, - {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e57916ef1bd0fee4f21c4600e9d1da352d8816b52a599c46460e93a6e9f17086"}, - {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:17358523b85973e5f242ad74aa4712b7ee560715562554aa2134d96e7aa4cbbf"}, - {file = "msgpack-1.0.5-cp37-cp37m-win32.whl", hash = "sha256:cb5aaa8c17760909ec6cb15e744c3ebc2ca8918e727216e79607b7bbce9c8f77"}, - {file = "msgpack-1.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:ab31e908d8424d55601ad7075e471b7d0140d4d3dd3272daf39c5c19d936bd82"}, - {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b72d0698f86e8d9ddf9442bdedec15b71df3598199ba33322d9711a19f08145c"}, - {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:379026812e49258016dd84ad79ac8446922234d498058ae1d415f04b522d5b2d"}, - {file = "msgpack-1.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:332360ff25469c346a1c5e47cbe2a725517919892eda5cfaffe6046656f0b7bb"}, - {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:476a8fe8fae289fdf273d6d2a6cb6e35b5a58541693e8f9f019bfe990a51e4ba"}, - {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9985b214f33311df47e274eb788a5893a761d025e2b92c723ba4c63936b69b1"}, - {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48296af57cdb1d885843afd73c4656be5c76c0c6328db3440c9601a98f303d87"}, - {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:addab7e2e1fcc04bd08e4eb631c2a90960c340e40dfc4a5e24d2ff0d5a3b3edb"}, - {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:916723458c25dfb77ff07f4c66aed34e47503b2eb3188b3adbec8d8aa6e00f48"}, - {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:821c7e677cc6acf0fd3f7ac664c98803827ae6de594a9f99563e48c5a2f27eb0"}, - {file = "msgpack-1.0.5-cp38-cp38-win32.whl", hash = "sha256:1c0f7c47f0087ffda62961d425e4407961a7ffd2aa004c81b9c07d9269512f6e"}, - {file = "msgpack-1.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:bae7de2026cbfe3782c8b78b0db9cbfc5455e079f1937cb0ab8d133496ac55e1"}, - {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:20c784e66b613c7f16f632e7b5e8a1651aa5702463d61394671ba07b2fc9e025"}, - {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:266fa4202c0eb94d26822d9bfd7af25d1e2c088927fe8de9033d929dd5ba24c5"}, - {file = "msgpack-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18334484eafc2b1aa47a6d42427da7fa8f2ab3d60b674120bce7a895a0a85bdd"}, - {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57e1f3528bd95cc44684beda696f74d3aaa8a5e58c816214b9046512240ef437"}, - {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586d0d636f9a628ddc6a17bfd45aa5b5efaf1606d2b60fa5d87b8986326e933f"}, - {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a740fa0e4087a734455f0fc3abf5e746004c9da72fbd541e9b113013c8dc3282"}, - {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3055b0455e45810820db1f29d900bf39466df96ddca11dfa6d074fa47054376d"}, - {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a61215eac016f391129a013c9e46f3ab308db5f5ec9f25811e811f96962599a8"}, - {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:362d9655cd369b08fda06b6657a303eb7172d5279997abe094512e919cf74b11"}, - {file = "msgpack-1.0.5-cp39-cp39-win32.whl", hash = "sha256:ac9dd47af78cae935901a9a500104e2dea2e253207c924cc95de149606dc43cc"}, - {file = "msgpack-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:06f5174b5f8ed0ed919da0e62cbd4ffde676a374aba4020034da05fab67b9164"}, - {file = "msgpack-1.0.5.tar.gz", hash = "sha256:c075544284eadc5cddc70f4757331d99dcbc16b2bbd4849d15f8aae4cf36d31c"}, +python-versions = ">=3.8" +files = [ + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, + {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, + {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, + {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, + {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, + {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, + {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, + {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, + {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, + {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, + {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] [[package]] name = "mypy" -version = "1.4.1" +version = "1.10.0" description = "Optional static typing for Python" optional = false -python-versions = ">=3.7" -files = [ - {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, - {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, - {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, - {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, - {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, - {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, - {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, - {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, - {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, - {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, - {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, - {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, - {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, - {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, - {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, - {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, - {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, - {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, - {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, - {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, - {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, - {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, - {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, - {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, - {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, - {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, +python-versions = ">=3.8" +files = [ + {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, + {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, + {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, + {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, + {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, + {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, + {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, + {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, + {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, + {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, + {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, + {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, + {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, + {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, + {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, + {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, + {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, + {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] -python2 = ["typed-ast (>=1.4.0,<2)"] +mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] @@ -1255,64 +1186,61 @@ files = [ [[package]] name = "myst-parser" -version = "0.18.1" -description = "An extended commonmark compliant parser, with bridges to docutils & sphinx." +version = "3.0.1" +description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "myst-parser-0.18.1.tar.gz", hash = "sha256:79317f4bb2c13053dd6e64f9da1ba1da6cd9c40c8a430c447a7b146a594c246d"}, - {file = "myst_parser-0.18.1-py3-none-any.whl", hash = "sha256:61b275b85d9f58aa327f370913ae1bec26ebad372cc99f3ab85c8ec3ee8d9fb8"}, + {file = "myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1"}, + {file = "myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87"}, ] [package.dependencies] -docutils = ">=0.15,<0.20" +docutils = ">=0.18,<0.22" jinja2 = "*" -markdown-it-py = ">=1.0.0,<3.0.0" -mdit-py-plugins = ">=0.3.1,<0.4.0" +markdown-it-py = ">=3.0,<4.0" +mdit-py-plugins = ">=0.4,<1.0" pyyaml = "*" -sphinx = ">=4,<6" -typing-extensions = "*" +sphinx = ">=6,<8" [package.extras] -code-style = ["pre-commit (>=2.12,<3.0)"] -linkify = ["linkify-it-py (>=1.0,<2.0)"] -rtd = ["ipython", "sphinx-book-theme", "sphinx-design", "sphinxcontrib.mermaid (>=0.7.1,<0.8.0)", "sphinxext-opengraph (>=0.6.3,<0.7.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] -testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=6,<7)", "pytest-cov", "pytest-param-files (>=0.3.4,<0.4.0)", "pytest-regressions", "sphinx (<5.2)", "sphinx-pytest"] +code-style = ["pre-commit (>=3.0,<4.0)"] +linkify = ["linkify-it-py (>=2.0,<3.0)"] +rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] +testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"] +testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"] [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.1" description = "Node.js virtual environment builder" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] name = "pathspec" -version = "0.11.2" +version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] @@ -1328,13 +1256,13 @@ files = [ [[package]] name = "pep8-naming" -version = "0.13.3" +version = "0.14.1" description = "Check PEP-8 naming conventions, plugin for flake8" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, - {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, + {file = "pep8-naming-0.14.1.tar.gz", hash = "sha256:1ef228ae80875557eb6c1549deafed4dabbf3261cfcafa12f773fe0db9be8a36"}, + {file = "pep8_naming-0.14.1-py3-none-any.whl", hash = "sha256:63f514fc777d715f935faf185dedd679ab99526a7f2f503abb61587877f7b1c5"}, ] [package.dependencies] @@ -1342,69 +1270,62 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "4.0.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} - [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.2.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, - {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.20.0" +version = "3.5.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, - {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, + {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, + {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, ] [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} nodeenv = ">=0.11.1" pyyaml = ">=5.1" -toml = "*" -virtualenv = ">=20.0.8" +virtualenv = ">=20.10.0" [[package]] name = "pre-commit-hooks" -version = "4.4.0" +version = "4.6.0" description = "Some out-of-the-box hooks for pre-commit." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pre_commit_hooks-4.4.0-py2.py3-none-any.whl", hash = "sha256:fc8837335476221ccccda3d176ed6ae29fe58753ce7e8b7863f5d0f987328fc6"}, - {file = "pre_commit_hooks-4.4.0.tar.gz", hash = "sha256:7011eed8e1a25cde94693da009cba76392194cecc2f3f06c51a44ea6ad6c2af9"}, + {file = "pre_commit_hooks-4.6.0-py2.py3-none-any.whl", hash = "sha256:a69199e6a2d45ec59c1020a81ca1549abddc2afb798276d9a0d951752d6abbfe"}, + {file = "pre_commit_hooks-4.6.0.tar.gz", hash = "sha256:eb1f43ee67869cd41b4c59017fad4a0f9d4d61201d163f2135535aaf65035a2b"}, ] [package.dependencies] @@ -1424,66 +1345,124 @@ files = [ [[package]] name = "pycparser" -version = "2.21" +version = "2.22" description = "C parser in Python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] [[package]] name = "pydantic" -version = "1.10.14" -description = "Data validation and settings management using python type hints" +version = "2.7.3" +description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-1.10.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4fcec873f90537c382840f330b90f4715eebc2bc9925f04cb92de593eae054"}, - {file = "pydantic-1.10.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e3a76f571970fcd3c43ad982daf936ae39b3e90b8a2e96c04113a369869dc87"}, - {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d886bd3c3fbeaa963692ef6b643159ccb4b4cefaf7ff1617720cbead04fd1d"}, - {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:798a3d05ee3b71967844a1164fd5bdb8c22c6d674f26274e78b9f29d81770c4e"}, - {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:23d47a4b57a38e8652bcab15a658fdb13c785b9ce217cc3a729504ab4e1d6bc9"}, - {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9f674b5c3bebc2eba401de64f29948ae1e646ba2735f884d1594c5f675d6f2a"}, - {file = "pydantic-1.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:24a7679fab2e0eeedb5a8924fc4a694b3bcaac7d305aeeac72dd7d4e05ecbebf"}, - {file = "pydantic-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d578ac4bf7fdf10ce14caba6f734c178379bd35c486c6deb6f49006e1ba78a7"}, - {file = "pydantic-1.10.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa7790e94c60f809c95602a26d906eba01a0abee9cc24150e4ce2189352deb1b"}, - {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad4e10efa5474ed1a611b6d7f0d130f4aafadceb73c11d9e72823e8f508e663"}, - {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1245f4f61f467cb3dfeced2b119afef3db386aec3d24a22a1de08c65038b255f"}, - {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:21efacc678a11114c765eb52ec0db62edffa89e9a562a94cbf8fa10b5db5c046"}, - {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:412ab4a3f6dbd2bf18aefa9f79c7cca23744846b31f1d6555c2ee2b05a2e14ca"}, - {file = "pydantic-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:e897c9f35281f7889873a3e6d6b69aa1447ceb024e8495a5f0d02ecd17742a7f"}, - {file = "pydantic-1.10.14-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d604be0f0b44d473e54fdcb12302495fe0467c56509a2f80483476f3ba92b33c"}, - {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42c7d17706911199798d4c464b352e640cab4351efe69c2267823d619a937e5"}, - {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:596f12a1085e38dbda5cbb874d0973303e34227b400b6414782bf205cc14940c"}, - {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bfb113860e9288d0886e3b9e49d9cf4a9d48b441f52ded7d96db7819028514cc"}, - {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bc3ed06ab13660b565eed80887fcfbc0070f0aa0691fbb351657041d3e874efe"}, - {file = "pydantic-1.10.14-cp37-cp37m-win_amd64.whl", hash = "sha256:ad8c2bc677ae5f6dbd3cf92f2c7dc613507eafe8f71719727cbc0a7dec9a8c01"}, - {file = "pydantic-1.10.14-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c37c28449752bb1f47975d22ef2882d70513c546f8f37201e0fec3a97b816eee"}, - {file = "pydantic-1.10.14-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49a46a0994dd551ec051986806122767cf144b9702e31d47f6d493c336462597"}, - {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e3819bd20a42470d6dd0fe7fc1c121c92247bca104ce608e609b59bc7a77ee"}, - {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbb503bbbbab0c588ed3cd21975a1d0d4163b87e360fec17a792f7d8c4ff29f"}, - {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:336709883c15c050b9c55a63d6c7ff09be883dbc17805d2b063395dd9d9d0022"}, - {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ae57b4d8e3312d486e2498d42aed3ece7b51848336964e43abbf9671584e67f"}, - {file = "pydantic-1.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:dba49d52500c35cfec0b28aa8b3ea5c37c9df183ffc7210b10ff2a415c125c4a"}, - {file = "pydantic-1.10.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c66609e138c31cba607d8e2a7b6a5dc38979a06c900815495b2d90ce6ded35b4"}, - {file = "pydantic-1.10.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d986e115e0b39604b9eee3507987368ff8148222da213cd38c359f6f57b3b347"}, - {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:646b2b12df4295b4c3148850c85bff29ef6d0d9621a8d091e98094871a62e5c7"}, - {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282613a5969c47c83a8710cc8bfd1e70c9223feb76566f74683af889faadc0ea"}, - {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:466669501d08ad8eb3c4fecd991c5e793c4e0bbd62299d05111d4f827cded64f"}, - {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:13e86a19dca96373dcf3190fcb8797d40a6f12f154a244a8d1e8e03b8f280593"}, - {file = "pydantic-1.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:08b6ec0917c30861e3fe71a93be1648a2aa4f62f866142ba21670b24444d7fd8"}, - {file = "pydantic-1.10.14-py3-none-any.whl", hash = "sha256:8ee853cd12ac2ddbf0ecbac1c289f95882b2d4482258048079d13be700aa114c"}, - {file = "pydantic-1.10.14.tar.gz", hash = "sha256:46f17b832fe27de7850896f3afee50ea682220dd218f7e9c88d436788419dca6"}, + {file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"}, + {file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"}, ] [package.dependencies] -typing-extensions = ">=4.2.0" +annotated-types = ">=0.4.0" +pydantic-core = "2.18.4" +typing-extensions = ">=4.6.1" [package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, + {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, + {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, + {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, + {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, + {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, + {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, + {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, + {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, + {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, + {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, + {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, + {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, + {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydocstyle" @@ -1497,7 +1476,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = ">=2.0.0,<5.0.0", markers = "python_version < \"3.8\""} snowballstemmer = ">=2.2.0" [package.extras] @@ -1516,17 +1494,16 @@ files = [ [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] @@ -1545,43 +1522,41 @@ cp2110 = ["hidapi"] [[package]] name = "pytest" -version = "7.4.4" +version = "8.2.2" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "python-can" -version = "4.2.2" +version = "4.4.0" description = "Controller Area Network interface module for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "python-can-4.2.2.tar.gz", hash = "sha256:6ad50f4613289f3c4d276b6d2ac8901d776dcb929994cce93f55a69e858c595f"}, - {file = "python_can-4.2.2-py3-none-any.whl", hash = "sha256:7eea9b81b0ff908000a825db024313f622895bd578e8a17433e0474cd7d2da83"}, + {file = "python_can-4.4.0-py3-none-any.whl", hash = "sha256:ece0013091cafc52f5134c887c43cce215d3a3d6ac90c4674f68a79526f07e43"}, + {file = "python_can-4.4.0.tar.gz", hash = "sha256:69b5bc8364d8f19c9dfc35107610634fe9ff8dda84ee2e54b81458ac8ca11737"}, ] [package.dependencies] msgpack = {version = ">=1.0.0,<1.1.0", markers = "platform_system != \"Windows\""} -packaging = "*" +packaging = ">=23.1" pywin32 = {version = ">=305", markers = "platform_system == \"Windows\" and platform_python_implementation == \"CPython\""} -setuptools = "*" typing-extensions = ">=3.10.0.0" wrapt = ">=1.10,<2.0" @@ -1591,6 +1566,7 @@ canine = ["python-can-canine (>=0.2.2)"] cantact = ["cantact (>=0.0.7)"] cvector = ["python-can-cvector"] gs-usb = ["gs-usb (>=0.2.1)"] +lint = ["black (==23.12.*)", "mypy (==1.8.*)", "pylint (==3.0.*)", "ruff (==0.1.13)"] mf4 = ["asammdf (>=6.0.0)"] neovi = ["filelock", "python-ics (>=2.12)"] nixnet = ["nixnet (>=0.3.2)"] @@ -1614,13 +1590,13 @@ files = [ [[package]] name = "pyupgrade" -version = "3.3.2" +version = "3.8.0" description = "A tool to automatically upgrade syntax for newer versions." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pyupgrade-3.3.2-py2.py3-none-any.whl", hash = "sha256:c05b82c911934b3a638b29f48f48dc6e0ef6c57c55ec36f2b41ae9dbf6711b4b"}, - {file = "pyupgrade-3.3.2.tar.gz", hash = "sha256:bcfed63d38811809f179fd269dec246680b0aaa5bbe662b535826e5fa2275219"}, + {file = "pyupgrade-3.8.0-py2.py3-none-any.whl", hash = "sha256:08d0e6129f5e9da7e7a581bdbea689e0d49c3c93eeaf156a07ae2fd794f52660"}, + {file = "pyupgrade-3.8.0.tar.gz", hash = "sha256:1facb0b8407cca468dfcc1d13717e3a85aa37b9e6e7338664ad5bfe5ef50c867"}, ] [package.dependencies] @@ -1722,13 +1698,13 @@ files = [ [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -1878,13 +1854,13 @@ files = [ [[package]] name = "safety" -version = "3.0.1" +version = "3.2.2" description = "Checks installed dependencies for known vulnerabilities and licenses." optional = false python-versions = ">=3.7" files = [ - {file = "safety-3.0.1-py3-none-any.whl", hash = "sha256:1ed058bc4bef132b974e58d7fcad020fb897cd255328016f8a5a194b94ca91d2"}, - {file = "safety-3.0.1.tar.gz", hash = "sha256:1f2000f03652f3a0bfc67f8fd1e98bc5723ccb76e15cb1bdd68545c3d803df01"}, + {file = "safety-3.2.2-py3-none-any.whl", hash = "sha256:1fca683ba6c0b4acb9f74953910639472a384d7b300292c89998a0100f016081"}, + {file = "safety-3.2.2.tar.gz", hash = "sha256:2a5867004aa1e3cedc90000ab726fdad2ce0ae179863c1b1697d7e8ded1b8cdb"}, ] [package.dependencies] @@ -1894,11 +1870,11 @@ dparse = ">=0.6.4b0" jinja2 = ">=3.1.0" marshmallow = ">=3.15.0" packaging = ">=21.0" -pydantic = ">=1.10.12,<2.0" +pydantic = ">=1.10.12" requests = "*" rich = "*" "ruamel.yaml" = ">=0.17.21" -safety-schemas = ">=0.0.1" +safety-schemas = ">=0.0.2" setuptools = ">=65.5.1" typer = "*" typing-extensions = ">=4.7.1" @@ -1929,19 +1905,29 @@ typing-extensions = ">=4.7.1" [[package]] name = "setuptools" -version = "68.0.0" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, - {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] [[package]] name = "six" @@ -1954,17 +1940,6 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[[package]] -name = "smmap" -version = "5.0.1" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.7" -files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, -] - [[package]] name = "snowballstemmer" version = "2.2.0" @@ -1978,38 +1953,38 @@ files = [ [[package]] name = "soupsieve" -version = "2.4.1" +version = "2.5" description = "A modern CSS selector implementation for Beautiful Soup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, - {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, ] [[package]] name = "sphinx" -version = "4.3.2" +version = "7.1.2" description = "Python documentation generator" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "Sphinx-4.3.2-py3-none-any.whl", hash = "sha256:6a11ea5dd0bdb197f9c2abc2e0ce73e01340464feaece525e64036546d24c851"}, - {file = "Sphinx-4.3.2.tar.gz", hash = "sha256:0a8836751a68306b3fe97ecbe44db786f8479c3bf4b80e3a7f5c838657b4698c"}, + {file = "sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"}, + {file = "sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f"}, ] [package.dependencies] alabaster = ">=0.7,<0.8" -babel = ">=1.3" -colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.18" -imagesize = "*" -Jinja2 = ">=2.3" -packaging = "*" -Pygments = ">=2.0" -requests = ">=2.5.0" -setuptools = "*" -snowballstemmer = ">=1.1" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18.1,<0.21" +imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.13" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" @@ -2019,8 +1994,8 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.920)", "types-pkg-resources", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] +test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] [[package]] name = "sphinx-autobuild" @@ -2060,29 +2035,29 @@ docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-ta [[package]] name = "sphinx-click" -version = "4.4.0" +version = "6.0.0" description = "Sphinx extension that automatically documents click applications" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "sphinx-click-4.4.0.tar.gz", hash = "sha256:cc67692bd28f482c7f01531c61b64e9d2f069bfcf3d24cbbb51d4a84a749fa48"}, - {file = "sphinx_click-4.4.0-py3-none-any.whl", hash = "sha256:2821c10a68fc9ee6ce7c92fad26540d8d8c8f45e6d7258f0e4fb7529ae8fab49"}, + {file = "sphinx_click-6.0.0-py3-none-any.whl", hash = "sha256:1e0a3c83bcb7c55497751b19d07ebe56b5d7b85eb76dd399cf9061b497adc317"}, + {file = "sphinx_click-6.0.0.tar.gz", hash = "sha256:f5d664321dc0c6622ff019f1e1c84e58ce0cecfddeb510e004cf60c2a3ab465b"}, ] [package.dependencies] -click = ">=7.0" +click = ">=8.0" docutils = "*" -sphinx = ">=2.0" +sphinx = ">=4.0" [[package]] name = "sphinxcontrib-applehelp" -version = "1.0.2" -description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +version = "1.0.4" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, + {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, + {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, ] [package.extras] @@ -2106,13 +2081,13 @@ test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.0" +version = "2.0.1" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, + {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, + {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, ] [package.extras] @@ -2165,28 +2140,27 @@ test = ["pytest"] [[package]] name = "stevedore" -version = "3.5.2" +version = "5.2.0" description = "Manage dynamic plugins for Python applications" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "stevedore-3.5.2-py3-none-any.whl", hash = "sha256:fa2630e3d0ad3e22d4914aff2501445815b9a4467a6edc49387c667a38faf5bf"}, - {file = "stevedore-3.5.2.tar.gz", hash = "sha256:cf99f41fc0d5a4f185ca4d3d42b03be9011b0a1ec1a4ea1a282be1b4b306dcc2"}, + {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, + {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, ] [package.dependencies] -importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} pbr = ">=2.0.0,<2.1.0 || >2.1.0" [[package]] name = "tokenize-rt" -version = "5.0.0" +version = "5.2.0" description = "A wrapper around the stdlib `tokenize` which roundtrips." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tokenize_rt-5.0.0-py2.py3-none-any.whl", hash = "sha256:c67772c662c6b3dc65edf66808577968fb10badfc2042e3027196bed4daf9e5a"}, - {file = "tokenize_rt-5.0.0.tar.gz", hash = "sha256:3160bc0c3e8491312d0485171dea861fc160a240f5f5766b72a1165408d10740"}, + {file = "tokenize_rt-5.2.0-py2.py3-none-any.whl", hash = "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289"}, + {file = "tokenize_rt-5.2.0.tar.gz", hash = "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054"}, ] [[package]] @@ -2213,134 +2187,84 @@ files = [ [[package]] name = "tornado" -version = "6.2" +version = "6.4.1" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false -python-versions = ">= 3.7" +python-versions = ">=3.8" files = [ - {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, - {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, - {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, - {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, - {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, + {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, + {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, + {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, ] [[package]] name = "traitlets" -version = "5.9.0" +version = "5.11.2" description = "Traitlets Python configuration system" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, - {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, + {file = "traitlets-5.11.2-py3-none-any.whl", hash = "sha256:98277f247f18b2c5cabaf4af369187754f4fb0e85911d473f72329db8a7f4fae"}, + {file = "traitlets-5.11.2.tar.gz", hash = "sha256:7564b5bf8d38c40fa45498072bf4dc5e8346eb087bbf1e2ae2d8774f6a0f078e"}, ] [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] - -[[package]] -name = "typed-ast" -version = "1.5.5" -description = "a fork of Python 2 and 3 ast modules with type comment support" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, - {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, - {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, - {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, - {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, - {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, - {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, - {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, - {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, - {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, - {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, -] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.5.1)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] [[package]] name = "typeguard" -version = "2.13.3" +version = "4.3.0" description = "Run-time type checker for Python" optional = false -python-versions = ">=3.5.3" +python-versions = ">=3.8" files = [ - {file = "typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1"}, - {file = "typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4"}, + {file = "typeguard-4.3.0-py3-none-any.whl", hash = "sha256:4d24c5b39a117f8a895b9da7a9b3114f04eb63bade45a4492de49b175b6f7dfa"}, + {file = "typeguard-4.3.0.tar.gz", hash = "sha256:92ee6a0aec9135181eae6067ebd617fd9de8d75d714fb548728a4933b1dea651"}, ] +[package.dependencies] +importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} +typing-extensions = ">=4.10.0" + [package.extras] -doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["mypy", "pytest", "typing-extensions"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.3.0)"] +test = ["coverage[toml] (>=7)", "mypy (>=1.2.0)", "pytest (>=7)"] [[package]] name = "typer" -version = "0.9.0" +version = "0.12.3" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, - {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, ] [package.dependencies] -click = ">=7.1.1,<9.0.0" +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" typing-extensions = ">=3.7.4.3" -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] - [[package]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] @@ -2355,42 +2279,40 @@ files = [ [[package]] name = "urllib3" -version = "2.0.7" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, - {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.4.7" +version = "20.26.2" description = "Virtual Python Environment builder" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = ">=3.7" files = [ - {file = "virtualenv-20.4.7-py2.py3-none-any.whl", hash = "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76"}, - {file = "virtualenv-20.4.7.tar.gz", hash = "sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467"}, + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, ] [package.dependencies] -appdirs = ">=1.4.3,<2" -distlib = ">=0.3.1,<1" -filelock = ">=3.0.0,<4" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -six = ">=1.9.0,<2" +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "xonsh (>=0.9.16)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "wrapt" @@ -2473,13 +2395,13 @@ files = [ [[package]] name = "xdoctest" -version = "1.1.3" +version = "1.1.5" description = "A rewrite of the builtin doctest module" optional = false python-versions = ">=3.6" files = [ - {file = "xdoctest-1.1.3-py3-none-any.whl", hash = "sha256:9360535bd1a971ffc216d9613898cedceb81d0fd024587cc3c03c74d14c00a31"}, - {file = "xdoctest-1.1.3.tar.gz", hash = "sha256:84e76a42a11a5926ff66d9d84c616bc101821099672550481ad96549cbdd02ae"}, + {file = "xdoctest-1.1.5-py3-none-any.whl", hash = "sha256:f36fe64d7c0ad0553dbff39ff05c43a0aab69d313466f24a38d00e757182ade0"}, + {file = "xdoctest-1.1.5.tar.gz", hash = "sha256:89b0c3ad7fe03a068e22a457ab18c38fc70c62329c2963f43954b83c29374e66"}, ] [package.dependencies] @@ -2500,20 +2422,20 @@ tests-strict = ["pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pyt [[package]] name = "zipp" -version = "3.15.0" +version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" -python-versions = "^3.7" -content-hash = "a3741ad0d4e88cb58029d364f412b9f731be5938bbdcb707c246b097100cd624" +python-versions = "^3.8" +content-hash = "d119edcc9bde40af26d6e5ea6efc6a7e578e4e7c8ad6c0b91dd56d65cfc3796f" diff --git a/pyproject.toml b/pyproject.toml index c2d8a10..d9f732c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ include = [ ] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.8" construct = "^2.10.68" mako = "^1.2.4" pyserial = "^3.5" @@ -81,6 +81,8 @@ chardet = "^5.2.0" traitlets = "<=5.11.2" line-profiler-pycharm = "^1.1.0" +toml = "^0.10.2" +bandit = "^1.7.8" [tool.poetry.group.dev.dependencies] ruff = "^0.1.0" From 6cff9cb64d1fee5c80057d409c1f5399bda8e645 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sat, 8 Jun 2024 13:56:50 +0300 Subject: [PATCH 50/99] fix typos --- pyxcp/config/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index b3fba83..67f82d7 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -846,7 +846,7 @@ def start(self): class ProfileConvert(Application): - description = "\nConvert legacy configuration file (.json/.toml) to new python based format." + description = "\nConvert legacy configuration file (.json/.toml) to new Python based format." config_file = Unicode(help="Name of legacy config file (.json/.toml).", default_value=None, allow_none=False).tag( config=True @@ -970,7 +970,7 @@ def read_configuration_file(self, file_name: str, emit_warning: bool = True): raise ValueError(f"Unknown file type for config: {suffix}") with pth.open("r") as f: if emit_warning: - self.log.warning(f"Legacy configuration file format ({suffix}), please use python based configuration.") + self.log.warning(f"Legacy configuration file format ({suffix}), please use Python based configuration.") cfg = reader.loads(f.read()) if cfg: cfg = legacy.convert_config(cfg, self.log) From bb562e0531227f3f1f7adf1c0981fb789d1a1cb5 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 23 Jun 2024 06:33:55 +0300 Subject: [PATCH 51/99] today() --- .gitmodules | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..641a09d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "pybind11"] + path = pybind11 + url = https://github.com/pybind/pybind11.git + branch = master From 5ee539a838f2a302a83edec5313c09eaf04f822d Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 23 Jun 2024 11:33:12 +0300 Subject: [PATCH 52/99] today() --- pyxcp/recorder/rekorder.hpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index a0dbfb6..205f9ad 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -239,7 +239,20 @@ inline std::string& trim(std::string& s) { inline std::string current_timestamp() { std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - std::string result{ std::ctime(&now) }; + +#if defined(_MSC_VER) + errno_t err; + char tbuf[128]; + std::string result{}; + + err = ::ctime_s(tbuf, 128, &now); + if (err == 0) { + result = tbuf; + } + +#else + std::string result{ ::ctime(&now) }; +#endif // result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end()); return trim(result); From 438888db89fe443441542ea571124a5be4a74882 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 23 Jun 2024 12:57:18 +0300 Subject: [PATCH 53/99] today() --- build_ext.py | 195 +++++++++++++++++++-------------------------------- 1 file changed, 72 insertions(+), 123 deletions(-) diff --git a/build_ext.py b/build_ext.py index 491e7d5..ae8e995 100644 --- a/build_ext.py +++ b/build_ext.py @@ -1,129 +1,78 @@ +#!/usr/bin/env python + +import multiprocessing as mp import os import platform +import re import subprocess # nosec import sys from pathlib import Path -from typing import Any, Dict - -import setuptools.command.build_py -import setuptools.command.develop -from pybind11.setup_helpers import ( - ParallelCompile, - Pybind11Extension, - build_ext, - naive_recompile, -) -from setuptools import Distribution - - -ROOT_DIRPATH = Path(".") - -if sys.platform == "darwin": - os.environ["CC"] = "clang++" - os.environ["CXX"] = "clang++" - -ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() -PYB11_INCLUDE_DIRS = subprocess.check_output(["pybind11-config", "--includes"]) # nosec -EXT_NAMES = ["pyxcp.recorder.rekorder", "pyxcp.cpp_ext.cpp_ext", "pyxcp.daq_stim.stim"] - -ext_modules = [ - Pybind11Extension( - EXT_NAMES[0], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/recorder", "pyxcp/cpp_ext"], - sources=["pyxcp/recorder/lz4.c", "pyxcp/recorder/lz4hc.c", "pyxcp/recorder/wrap.cpp"], - define_macros=[("EXTENSION_NAME", EXT_NAMES[0]), ("NDEBUG", 1)], - optional=False, - cxx_std=20, - ), - Pybind11Extension( - EXT_NAMES[1], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/cpp_ext"], - sources=["pyxcp/cpp_ext/extension_wrapper.cpp"], - define_macros=[("EXTENSION_NAME", EXT_NAMES[1]), ("NDEBUG", 1)], - optional=False, - cxx_std=20, - ), - Pybind11Extension( - EXT_NAMES[2], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/daq_stim", "pyxcp/cpp_ext"], - sources=["pyxcp/daq_stim/stim.cpp", "pyxcp/daq_stim/stim_wrapper.cpp", "pyxcp/daq_stim/scheduler.cpp"], - define_macros=[("EXTENSION_NAME", EXT_NAMES[2]), ("NDEBUG", 1)], - optional=False, - cxx_std=20, # Extension will use C++20 generators/coroutines. - ), -] - - -class AsamKeyDllAutogen(setuptools.Command): - """Custom command to compile `asamkeydll.exe`.""" - - description = "Compile `asamkeydll.exe`." - - def initialize_options(self): - pass - - def finalize_options(self): - """Post-process options.""" - asamkeydll = os.path.join("pyxcp", "asamkeydll.c") - target = os.path.join("pyxcp", "asamkeydll.exe") - self.arguments = [asamkeydll, f"-o{target}"] - - def run(self): - """Run gcc""" - word_width, _ = platform.architecture() - if sys.platform in ("win32") and word_width == "64bit": - gccCmd = ["gcc", "-m32", "-O3", "-Wall"] - self.announce(" ".join(gccCmd + self.arguments)) - try: - subprocess.check_call(gccCmd + self.arguments) # nosec - except Exception as e: - print(f"Building pyxcp/asamkeydll.exe failed: {e!r}") - else: - print("Successfully build pyxcp/asamkeydll.exe") - - -class CustomBuildPy(setuptools.command.build_py.build_py): - def run(self): - self.run_command("asamkeydll") - super().run() - - -class CustomDevelop(setuptools.command.develop.develop): - def run(self): - self.run_command("asamkeydll") - super().run() - - -def build(setup_kwargs: Dict[str, Any]) -> None: - setup_kwargs.update( - { - "ext_modules": ext_modules, - "cmd_class": dict(build_ext=Pybind11Extension), - "zip_safe": False, - } - ) - - -def invoke_command(distribution: Distribution, name: str) -> None: - cmd = distribution.cmdclass.get(name)(distribution) - print(f"Building target {name!r}...") - cmd.inplace = 1 - cmd.ensure_finalized() - cmd.run() - - -### + +# from pprint import pprint +from tempfile import TemporaryDirectory + + +print("Platform", platform.system()) + +TOP_DIR = Path(__file__).parent + + +def banner(msg: str) -> None: + print("=" * 80) + print(str.center(msg, 80)) + print("=" * 80) + + +def build_extension(debug: bool = False) -> None: + print("CMakeBuild::build_extension()") + + debug = int(os.environ.get("DEBUG", 0)) or debug + cfg = "Debug" if debug else "Release" + + # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON + # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code + # from Python. + cmake_args = [ + # f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", + # "-G Ninja", + f"-DPYTHON_EXECUTABLE={sys.executable}", + f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm + ] + build_args = ["--config Release", "--verbose"] + # Adding CMake arguments set as environment variable + # (needed e.g. to build for ARM OSx on conda-forge) + + # cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 /path/to/src + + if sys.platform.startswith("darwin"): + # Cross-compile support for macOS - respect ARCHFLAGS if set + archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) + if archs: + cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] + + build_temp = Path(TemporaryDirectory(suffix=".build-temp").name) / "extension_it_in" + # build_temp = Path(".") / "build" + print("cwd:", os.getcwd(), "build-dir:", build_temp, "top:", str(TOP_DIR)) + # print("PHILEZ:", os.listdir(TOP_DIR)) + if not build_temp.exists(): + build_temp.mkdir(parents=True) + + banner("Step #1: Configure") + # cmake_args += ["--debug-output"] + print("aufruf:", ["cmake", str(TOP_DIR), *cmake_args]) + subprocess.run(["cmake", str(TOP_DIR), *cmake_args], cwd=build_temp, check=True) # nosec + + cmake_args += [f"--parallel {mp.cpu_count()}"] + + banner("Step #2: Build") + # subprocess.run(["cmake", "--build", ".", *build_args], cwd=build_temp, check=True) # nosec + # build_args += ["-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON"] + subprocess.run(["cmake", "--build", build_temp, *build_args], cwd=TOP_DIR, check=True) # nosec + + banner("Step #3: Install") + # subprocess.run(["cmake", "--install", "."], cwd=build_temp, check=True) # nosec + subprocess.run(["cmake", "--install", build_temp], cwd=TOP_DIR, check=True) # nosec + + if __name__ == "__main__": - distribution = Distribution( - { - "cmdclass": { - "asam_key_dll": AsamKeyDllAutogen, - "CXX_extensions": build_ext, - }, - "name": "pyxcp", - "ext_modules": ext_modules, - "package_dir": {"pyxcp": str(ROOT_DIRPATH / "pyxcp")}, - } - ) - invoke_command(distribution, "CXX_extensions") - invoke_command(distribution, "asam_key_dll") + build_extension() From 3e3cd6dd6cbe80d49c557b2104e32ada77f44aac Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 23 Jun 2024 13:59:07 +0300 Subject: [PATCH 54/99] today() --- CMakeLists.txt | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e4bc958 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,105 @@ + +cmake_minimum_required(VERSION 3.7...3.29) +project(pyxcp_extensions LANGUAGES C CXX) + +find_package(Python COMPONENTS Interpreter Development) +find_package(pybind11 CONFIG) + +SET(CMAKE_C_STANDARD 17) +set(CMAKE_CXX_STANDARD 23) + +message( STATUS "Found pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}: ${pybind11_INCLUDE_DIRS}") + +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/dist") + + +SET(GCC_N_CLANG_BASE_OPTIONS "-std=c++23 -Wall -Wextra -Wpedantic -Warray-bounds -mtune=native -fexceptions") +# target_link_options(${PROJECT_NAME} PUBLIC -flto=auto) + +SET(MSVC_BASE_OPTIONS "/W3 /permissive- /EHsc /bigobj /std:c++latest") + + +if (CMAKE_BUILD_TYPE STREQUAL "DEBUG") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Od /fsanitize=address /Zi") +else () + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox ") # /GL +endif () + + +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(ENV{MACOSX_DEPLOYMENT_TARGET} "11.0") + SET(GCC_N_CLANG_EXTRA_OPTIONS "-stdlib=libc++") + message("Platform is Darwin") +elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows") + message("Platform is WINDOWS") + SET(MSVC_EXTRA_OPTIONS "") +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") + SET(GCC_N_CLANG_EXTRA_OPTIONS "-fcoroutines -fvisibility=hidden -g0") + message("Platform is LINUX") +endif() + + +IF (CMAKE_C_COMPILER_ID STREQUAL "GNU") + +ELSEIF (CMAKE_C_COMPILER_ID MATCHES "Clang") + +ELSEIF (CMAKE_C_COMPILER_ID MATCHES "MSVC") + +ELSE () + +ENDIF () + +IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + +ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + +ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + +ELSE () + + +ENDIF () + +message("Compiling C with: " ${CMAKE_C_COMPILER_ID}) +message("Compiling Cpp with: " ${CMAKE_CXX_COMPILER_ID}) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(EXTENSION_INCS ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/cpp_ext) + +pybind11_add_module(rekorder pyxcp/recorder/wrap.cpp pyxcp/recorder/lz4.c pyxcp/recorder/lz4hc.c) +pybind11_add_module(cpp_ext pyxcp/cpp_ext/extension_wrapper.cpp) +pybind11_add_module(stim pyxcp/daq_stim/stim_wrapper.cpp pyxcp/daq_stim/stim.cpp pyxcp/daq_stim/scheduler.cpp) + +target_include_directories(rekorder PRIVATE ${EXTENSION_INCS} ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/recorder) +target_include_directories(cpp_ext PRIVATE ${EXTENSION_INCS}) +target_include_directories(stim PRIVATE ${EXTENSION_INCS} ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/daq_stim) + +target_compile_options(rekorder PUBLIC "-DEXTENSION_NAME=pyxcp.recorder.rekorder") +target_compile_options(cpp_ext PUBLIC "-DEXTENSION_NAME=pyxcp.cpp_ext.cpp_ext") +target_compile_options(stim PUBLIC "-DEXTENSION_NAME=pyxcp.daq_stim.stim") + +add_executable(asamkeydll ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/asamkeydll.c) +# set_target_properties(MyTarget PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") + +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # CMAKE_SYSTEM_NAME STREQUAL "Windows" +endif() + +IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_N_CLANG_BASE_OPTIONS} ${GCC_N_CLANG_EXTRA_OPTIONS}") + target_link_options(cpp_ext PUBLIC -flto=auto) + target_link_options(stim PUBLIC -flto=auto) + target_link_options(rekorder PUBLIC -flto=auto) +ELSEIF (CMAKE_C_COMPILER_ID MATCHES "MSVC") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MSVC_BASE_OPTIONS} ${MSVC_EXTRA_OPTIONS}") +ENDIF() + +# target_include_directories(preprocessor PUBLIC $) +# target_link_libraries(preprocessor pybind11::headers) +# set_target_properties(preprocessor PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON CXX_VISIBILITY_PRESET ON VISIBILITY_INLINES_HIDDEN ON) + +install(TARGETS rekorder LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/recorder) +install(TARGETS cpp_ext LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/cpp_ext) +install(TARGETS stim LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/daq_stim) +# install(TARGETS asamkeydll LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/) From 197cb15a661578ff39082560582a28fce6d78c64 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 23 Jun 2024 18:22:57 +0300 Subject: [PATCH 55/99] today() --- build_ext.py | 1 + pyproject.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/build_ext.py b/build_ext.py index ae8e995..eca0aec 100644 --- a/build_ext.py +++ b/build_ext.py @@ -75,4 +75,5 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": + print("DIR:", os.listdir()) build_extension() diff --git a/pyproject.toml b/pyproject.toml index d9f732c..bcc67bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,6 +66,7 @@ include = [ { path = "pyxcp/recorder/*.so", format = "wheel" }, { path = "pyxcp/recorder/*.pyd", format = "wheel" }, { path = "pyxcp/*.exe", format = "wheel" }, + { path = "CMakeLists.txt", format = "wheel" }, ] [tool.poetry.dependencies] From 2cd68ddf99ab298ff69b8da2be915194ecc82873 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 23 Jun 2024 19:01:19 +0300 Subject: [PATCH 56/99] today() --- build_ext.py | 1 + 1 file changed, 1 insertion(+) diff --git a/build_ext.py b/build_ext.py index eca0aec..bd18e30 100644 --- a/build_ext.py +++ b/build_ext.py @@ -76,4 +76,5 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": print("DIR:", os.listdir()) + print(subprocess.getoutput(["pybind11-config", "--includes"])) # nosec build_extension() From 0805a70d08d08628f9292890e3c9f13bf5846dd1 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 23 Jun 2024 19:52:40 +0300 Subject: [PATCH 57/99] today() --- build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_ext.py b/build_ext.py index bd18e30..ce89119 100644 --- a/build_ext.py +++ b/build_ext.py @@ -76,5 +76,5 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": print("DIR:", os.listdir()) - print(subprocess.getoutput(["pybind11-config", "--includes"])) # nosec + print(subprocess.getoutput("pybind11-config --includes")) # nosec build_extension() From 4640a2b0b2d5f19771b25b294ab97ebc0fd044a8 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 23 Jun 2024 20:37:07 +0300 Subject: [PATCH 58/99] today() --- build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build_ext.py b/build_ext.py index ce89119..c4da39c 100644 --- a/build_ext.py +++ b/build_ext.py @@ -76,5 +76,6 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": print("DIR:", os.listdir()) - print(subprocess.getoutput("pybind11-config --includes")) # nosec + includes = subprocess.getoutput("pybind11-config --includes") # nosec + os.environ["pybind11_INCLUDE_DIRS"] = includes build_extension() From 76e4215b8dd825257077d33f0a76571a694f6b17 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 24 Jun 2024 06:49:56 +0300 Subject: [PATCH 59/99] today() --- CMakeLists.txt | 3 ++- build_ext.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e4bc958..71a2a6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,8 @@ cmake_minimum_required(VERSION 3.7...3.29) project(pyxcp_extensions LANGUAGES C CXX) find_package(Python COMPONENTS Interpreter Development) -find_package(pybind11 CONFIG) +#find_package(pybind11 CONFIG) +find_package(pybind11 REQUIRED) SET(CMAKE_C_STANDARD 17) set(CMAKE_CXX_STANDARD 23) diff --git a/build_ext.py b/build_ext.py index c4da39c..938112d 100644 --- a/build_ext.py +++ b/build_ext.py @@ -77,5 +77,5 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": print("DIR:", os.listdir()) includes = subprocess.getoutput("pybind11-config --includes") # nosec - os.environ["pybind11_INCLUDE_DIRS"] = includes + # os.environ["pybind11_INCLUDE_DIRS"] = includes build_extension() From e56e65e919f2148b1411ac4f535409aee44cdba1 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 24 Jun 2024 10:41:42 +0300 Subject: [PATCH 60/99] today() --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bcc67bd..151f0cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["poetry-core>=1.0.0", "setuptools>=68.0.0" , "pybind11>=2.11.1"] +requires = ["poetry-core>=1.0.0", "setuptools>=68.0.0" , "pybind11[global]>=2.11.1"] build-backend = "poetry.core.masonry.api" From abb2152e46f6bfabe94bf25258770f44bcb7a9dc Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 24 Jun 2024 12:34:58 +0300 Subject: [PATCH 61/99] today() --- pybind11Config.cmake | 341 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 pybind11Config.cmake diff --git a/pybind11Config.cmake b/pybind11Config.cmake new file mode 100644 index 0000000..a8b0800 --- /dev/null +++ b/pybind11Config.cmake @@ -0,0 +1,341 @@ +# tools/pybind11NewTools.cmake -- Build system for the pybind11 modules +# +# Copyright (c) 2020 Wenzel Jakob and Henry Schreiner +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +if(CMAKE_VERSION VERSION_LESS 3.12) + message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12") +endif() + +include_guard(DIRECTORY) + +get_property( + is_config + TARGET pybind11::headers + PROPERTY IMPORTED) + +if(pybind11_FIND_QUIETLY) + set(_pybind11_quiet QUIET) +else() + set(_pybind11_quiet "") +endif() + +if(NOT Python_FOUND AND NOT Python3_FOUND) + if(NOT DEFINED Python_FIND_IMPLEMENTATIONS) + set(Python_FIND_IMPLEMENTATIONS CPython PyPy) + endif() + + # GitHub Actions like activation + if(NOT DEFINED Python_ROOT_DIR AND DEFINED ENV{pythonLocation}) + set(Python_ROOT_DIR "$ENV{pythonLocation}") + endif() + + # Interpreter should not be found when cross-compiling + if(_PYBIND11_CROSSCOMPILING) + set(_pybind11_interp_component "") + else() + set(_pybind11_interp_component Interpreter) + endif() + + # Development.Module support (required for manylinux) started in 3.18 + if(CMAKE_VERSION VERSION_LESS 3.18) + set(_pybind11_dev_component Development) + else() + set(_pybind11_dev_component Development.Module OPTIONAL_COMPONENTS Development.Embed) + endif() + + # Callers need to be able to access Python_EXECUTABLE + set(_pybind11_global_keyword "") + if(NOT is_config AND NOT DEFINED Python_ARTIFACTS_INTERACTIVE) + set(Python_ARTIFACTS_INTERACTIVE TRUE) + if(NOT CMAKE_VERSION VERSION_LESS 3.24) + set(_pybind11_global_keyword "GLOBAL") + endif() + endif() + + find_package( + Python 3.7 REQUIRED COMPONENTS ${_pybind11_interp_component} ${_pybind11_dev_component} + ${_pybind11_quiet} ${_pybind11_global_keyword}) + + # If we are in submodule mode, export the Python targets to global targets. + # If this behavior is not desired, FindPython _before_ pybind11. + if(NOT is_config + AND Python_ARTIFACTS_INTERACTIVE + AND _pybind11_global_keyword STREQUAL "") + if(TARGET Python::Python) + set_property(TARGET Python::Python PROPERTY IMPORTED_GLOBAL TRUE) + endif() + if(TARGET Python::Interpreter) + set_property(TARGET Python::Interpreter PROPERTY IMPORTED_GLOBAL TRUE) + endif() + if(TARGET Python::Module) + set_property(TARGET Python::Module PROPERTY IMPORTED_GLOBAL TRUE) + endif() + endif() + + # Explicitly export version for callers (including our own functions) + if(NOT is_config AND Python_ARTIFACTS_INTERACTIVE) + set(Python_VERSION + "${Python_VERSION}" + CACHE INTERNAL "") + set(Python_VERSION_MAJOR + "${Python_VERSION_MAJOR}" + CACHE INTERNAL "") + set(Python_VERSION_MINOR + "${Python_VERSION_MINOR}" + CACHE INTERNAL "") + set(Python_VERSION_PATCH + "${Python_VERSION_PATCH}" + CACHE INTERNAL "") + endif() +endif() + +if(Python_FOUND) + set(_Python + Python + CACHE INTERNAL "" FORCE) +elseif(Python3_FOUND) + set(_Python + Python3 + CACHE INTERNAL "" FORCE) +endif() + +if(PYBIND11_MASTER_PROJECT) + if(${_Python}_INTERPRETER_ID MATCHES "PyPy") + message(STATUS "PyPy ${${_Python}_PyPy_VERSION} (Py ${${_Python}_VERSION})") + else() + message(STATUS "${_Python} ${${_Python}_VERSION}") + endif() +endif() + +if(NOT _PYBIND11_CROSSCOMPILING) + # If a user finds Python, they may forget to include the Interpreter component + # and the following two steps require it. It is highly recommended by CMake + # when finding development libraries anyway, so we will require it. + if(NOT DEFINED ${_Python}_EXECUTABLE) + message( + FATAL_ERROR + "${_Python} was found without the Interpreter component. Pybind11 requires this component." + ) + + endif() + + if(DEFINED PYBIND11_PYTHON_EXECUTABLE_LAST AND NOT ${_Python}_EXECUTABLE STREQUAL + PYBIND11_PYTHON_EXECUTABLE_LAST) + # Detect changes to the Python version/binary in subsequent CMake runs, and refresh config if needed + unset(PYTHON_IS_DEBUG CACHE) + unset(PYTHON_MODULE_EXTENSION CACHE) + endif() + + set(PYBIND11_PYTHON_EXECUTABLE_LAST + "${${_Python}_EXECUTABLE}" + CACHE INTERNAL "Python executable during the last CMake run") + + if(NOT DEFINED PYTHON_IS_DEBUG) + # Debug check - see https://stackoverflow.com/questions/646518/python-how-to-detect-debug-Interpreter + execute_process( + COMMAND "${${_Python}_EXECUTABLE}" "-c" + "import sys; sys.exit(hasattr(sys, 'gettotalrefcount'))" + RESULT_VARIABLE _PYTHON_IS_DEBUG) + set(PYTHON_IS_DEBUG + "${_PYTHON_IS_DEBUG}" + CACHE INTERNAL "Python debug status") + endif() + + # Get the suffix - SO is deprecated, should use EXT_SUFFIX, but this is + # required for PyPy3 (as of 7.3.1) + if(NOT DEFINED PYTHON_MODULE_EXTENSION OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX) + execute_process( + COMMAND + "${${_Python}_EXECUTABLE}" "-c" + "import sys, importlib; s = importlib.import_module('distutils.sysconfig' if sys.version_info < (3, 10) else 'sysconfig'); print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO'))" + OUTPUT_VARIABLE _PYTHON_MODULE_EXT_SUFFIX + ERROR_VARIABLE _PYTHON_MODULE_EXT_SUFFIX_ERR + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(_PYTHON_MODULE_EXT_SUFFIX STREQUAL "") + message( + FATAL_ERROR + "pybind11 could not query the module file extension, likely the 'distutils'" + "package is not installed. Full error message:\n${_PYTHON_MODULE_EXT_SUFFIX_ERR}") + endif() + + # This needs to be available for the pybind11_extension function + if(NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX) + get_filename_component(_PYTHON_MODULE_DEBUG_POSTFIX "${_PYTHON_MODULE_EXT_SUFFIX}" NAME_WE) + set(PYTHON_MODULE_DEBUG_POSTFIX + "${_PYTHON_MODULE_DEBUG_POSTFIX}" + CACHE INTERNAL "") + endif() + + if(NOT DEFINED PYTHON_MODULE_EXTENSION) + get_filename_component(_PYTHON_MODULE_EXTENSION "${_PYTHON_MODULE_EXT_SUFFIX}" EXT) + set(PYTHON_MODULE_EXTENSION + "${_PYTHON_MODULE_EXTENSION}" + CACHE INTERNAL "") + endif() + endif() +else() + if(NOT DEFINED PYTHON_IS_DEBUG + OR NOT DEFINED PYTHON_MODULE_EXTENSION + OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX) + include("${CMAKE_CURRENT_LIST_DIR}/pybind11GuessPythonExtSuffix.cmake") + pybind11_guess_python_module_extension("${_Python}") + endif() + # When cross-compiling, we cannot query the Python interpreter, so we require + # the user to set these variables explicitly. + if(NOT DEFINED PYTHON_IS_DEBUG + OR NOT DEFINED PYTHON_MODULE_EXTENSION + OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX) + message( + FATAL_ERROR + "When cross-compiling, you should set the PYTHON_IS_DEBUG, PYTHON_MODULE_EXTENSION and PYTHON_MODULE_DEBUG_POSTFIX \ + variables appropriately before loading pybind11 (e.g. in your CMake toolchain file)") + endif() +endif() + +# Python debug libraries expose slightly different objects before 3.8 +# https://docs.python.org/3.6/c-api/intro.html#debugging-builds +# https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib +if(PYTHON_IS_DEBUG) + set_property( + TARGET pybind11::pybind11 + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG) +endif() + +# Check on every access - since Python can change - do nothing in that case. + +if(DEFINED ${_Python}_INCLUDE_DIRS) + # Only add Python for build - must be added during the import for config + # since it has to be re-discovered. + # + # This needs to be a target to be included after the local pybind11 + # directory, just in case there there is an installed pybind11 sitting + # next to Python's includes. It also ensures Python is a SYSTEM library. + add_library(pybind11::python_headers INTERFACE IMPORTED) + set_property( + TARGET pybind11::python_headers PROPERTY INTERFACE_INCLUDE_DIRECTORIES + "$") + set_property( + TARGET pybind11::pybind11 + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python_headers) + set(pybind11_INCLUDE_DIRS + "${pybind11_INCLUDE_DIR}" "${${_Python}_INCLUDE_DIRS}" + CACHE INTERNAL "Directories where pybind11 and possibly Python headers are located") +endif() + +# In CMake 3.18+, you can find these separately, so include an if +if(TARGET ${_Python}::Python) + set_property( + TARGET pybind11::embed + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Python) +endif() + +# CMake 3.15+ has this +if(TARGET ${_Python}::Module) + set_property( + TARGET pybind11::module + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Module) +else() + set_property( + TARGET pybind11::module + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python_link_helper) +endif() + +# WITHOUT_SOABI and WITH_SOABI will disable the custom extension handling used by pybind11. +# WITH_SOABI is passed on to python_add_library. +function(pybind11_add_module target_name) + cmake_parse_arguments(PARSE_ARGV 1 ARG + "STATIC;SHARED;MODULE;THIN_LTO;OPT_SIZE;NO_EXTRAS;WITHOUT_SOABI" "" "") + + if(ARG_STATIC) + set(lib_type STATIC) + elseif(ARG_SHARED) + set(lib_type SHARED) + else() + set(lib_type MODULE) + endif() + + if("${_Python}" STREQUAL "Python") + python_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) + elseif("${_Python}" STREQUAL "Python3") + python3_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) + else() + message(FATAL_ERROR "Cannot detect FindPython version: ${_Python}") + endif() + + target_link_libraries(${target_name} PRIVATE pybind11::headers) + + if(lib_type STREQUAL "MODULE") + target_link_libraries(${target_name} PRIVATE pybind11::module) + else() + target_link_libraries(${target_name} PRIVATE pybind11::embed) + endif() + + if(MSVC) + target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) + endif() + + # -fvisibility=hidden is required to allow multiple modules compiled against + # different pybind versions to work properly, and for some features (e.g. + # py::module_local). We force it on everything inside the `pybind11` + # namespace; also turning it on for a pybind module compilation here avoids + # potential warnings or issues from having mixed hidden/non-hidden types. + if(NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) + set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") + endif() + + if(NOT DEFINED CMAKE_CUDA_VISIBILITY_PRESET) + set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") + endif() + + # If we don't pass a WITH_SOABI or WITHOUT_SOABI, use our own default handling of extensions + if(NOT ARG_WITHOUT_SOABI AND NOT "WITH_SOABI" IN_LIST ARG_UNPARSED_ARGUMENTS) + pybind11_extension(${target_name}) + endif() + + if(ARG_NO_EXTRAS) + return() + endif() + + if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) + if(ARG_THIN_LTO) + target_link_libraries(${target_name} PRIVATE pybind11::thin_lto) + else() + target_link_libraries(${target_name} PRIVATE pybind11::lto) + endif() + endif() + + if(DEFINED CMAKE_BUILD_TYPE) # see https://github.com/pybind/pybind11/issues/4454 + # Use case-insensitive comparison to match the result of $ + string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) + if(NOT MSVC AND NOT "${uppercase_CMAKE_BUILD_TYPE}" MATCHES DEBUG|RELWITHDEBINFO) + # Strip unnecessary sections of the binary on Linux/macOS + pybind11_strip(${target_name}) + endif() + endif() + + if(MSVC) + target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) + endif() + + if(ARG_OPT_SIZE) + target_link_libraries(${target_name} PRIVATE pybind11::opt_size) + endif() +endfunction() + +function(pybind11_extension name) + # The extension is precomputed + set_target_properties( + ${name} + PROPERTIES PREFIX "" + DEBUG_POSTFIX "${PYTHON_MODULE_DEBUG_POSTFIX}" + SUFFIX "${PYTHON_MODULE_EXTENSION}") +endfunction() From 87754a3bb588ad2fa81c2e2629ac9b6848feeb52 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 24 Jun 2024 14:42:43 +0300 Subject: [PATCH 62/99] today() --- CMakeLists.txt | 2 +- build_ext.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71a2a6c..25071ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(pyxcp_extensions LANGUAGES C CXX) find_package(Python COMPONENTS Interpreter Development) #find_package(pybind11 CONFIG) -find_package(pybind11 REQUIRED) +find_package(pybind11 CONFIG) SET(CMAKE_C_STANDARD 17) set(CMAKE_CXX_STANDARD 23) diff --git a/build_ext.py b/build_ext.py index 938112d..aca7843 100644 --- a/build_ext.py +++ b/build_ext.py @@ -76,6 +76,6 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": print("DIR:", os.listdir()) - includes = subprocess.getoutput("pybind11-config --includes") # nosec - # os.environ["pybind11_INCLUDE_DIRS"] = includes + includes = subprocess.getoutput("pybind11-config --cmakedir") # nosec + os.environ["pybind11_DIR"] = includes build_extension() From 15a335ee94fab49c7405b049249b5daf2e7616d5 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 24 Jun 2024 14:46:23 +0300 Subject: [PATCH 63/99] today() --- CMakeLists.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25071ba..b56a798 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.7...3.29) project(pyxcp_extensions LANGUAGES C CXX) find_package(Python COMPONENTS Interpreter Development) -#find_package(pybind11 CONFIG) find_package(pybind11 CONFIG) SET(CMAKE_C_STANDARD 17) @@ -20,11 +19,11 @@ SET(GCC_N_CLANG_BASE_OPTIONS "-std=c++23 -Wall -Wextra -Wpedantic -Warray-bounds SET(MSVC_BASE_OPTIONS "/W3 /permissive- /EHsc /bigobj /std:c++latest") -if (CMAKE_BUILD_TYPE STREQUAL "DEBUG") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Od /fsanitize=address /Zi") -else () - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox ") # /GL -endif () +# if (CMAKE_BUILD_TYPE STREQUAL "DEBUG") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Od /fsanitize=address /Zi") +# else () +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox ") # /GL +# endif () if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") From 0676e18793eb224b6b41a93626dc29543c407c78 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 25 Jun 2024 07:49:39 +0300 Subject: [PATCH 64/99] today() --- build_ext.py | 1 - poetry.lock | 202 ++++++++++----------- pybind11Config.cmake | 341 ------------------------------------ pyxcp/cpp_ext/helper.hpp | 4 +- pyxcp/cpp_ext/mcobject.hpp | 10 ++ pyxcp/recorder/__init__.py | 4 + pyxcp/recorder/rekorder.hpp | 12 +- pyxcp/recorder/unfolder.hpp | 20 +-- pyxcp/recorder/wrap.cpp | 1 + pyxcp/recorder/writer.hpp | 8 +- 10 files changed, 130 insertions(+), 473 deletions(-) delete mode 100644 pybind11Config.cmake diff --git a/build_ext.py b/build_ext.py index aca7843..edc3f66 100644 --- a/build_ext.py +++ b/build_ext.py @@ -75,7 +75,6 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": - print("DIR:", os.listdir()) includes = subprocess.getoutput("pybind11-config --cmakedir") # nosec os.environ["pybind11_DIR"] = includes build_extension() diff --git a/poetry.lock b/poetry.lock index 480f45c..57346af 100644 --- a/poetry.lock +++ b/poetry.lock @@ -77,13 +77,13 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "bandit" -version = "1.7.8" +version = "1.7.9" description = "Security oriented static analyser for python code." optional = false python-versions = ">=3.8" files = [ - {file = "bandit-1.7.8-py3-none-any.whl", hash = "sha256:509f7af645bc0cd8fd4587abc1a038fc795636671ee8204d502b933aee44f381"}, - {file = "bandit-1.7.8.tar.gz", hash = "sha256:36de50f720856ab24a24dbaa5fee2c66050ed97c1477e0a1159deab1775eab6b"}, + {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"}, + {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"}, ] [package.dependencies] @@ -403,63 +403,63 @@ extras = ["arrow", "cloudpickle", "cryptography", "lz4", "numpy", "ruamel.yaml"] [[package]] name = "coverage" -version = "7.5.3" +version = "7.5.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, + {file = "coverage-7.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99"}, + {file = "coverage-7.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9"}, + {file = "coverage-7.5.4-cp310-cp310-win32.whl", hash = "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8"}, + {file = "coverage-7.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f"}, + {file = "coverage-7.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5"}, + {file = "coverage-7.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078"}, + {file = "coverage-7.5.4-cp311-cp311-win32.whl", hash = "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806"}, + {file = "coverage-7.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d"}, + {file = "coverage-7.5.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233"}, + {file = "coverage-7.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805"}, + {file = "coverage-7.5.4-cp312-cp312-win32.whl", hash = "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b"}, + {file = "coverage-7.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7"}, + {file = "coverage-7.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882"}, + {file = "coverage-7.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f"}, + {file = "coverage-7.5.4-cp38-cp38-win32.whl", hash = "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f"}, + {file = "coverage-7.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633"}, + {file = "coverage-7.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088"}, + {file = "coverage-7.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7"}, + {file = "coverage-7.5.4-cp39-cp39-win32.whl", hash = "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace"}, + {file = "coverage-7.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d"}, + {file = "coverage-7.5.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5"}, + {file = "coverage-7.5.4.tar.gz", hash = "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353"}, ] [package.dependencies] @@ -592,18 +592,18 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.14.0" +version = "3.15.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, - {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] @@ -729,22 +729,22 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.1.0" +version = "7.2.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, - {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, + {file = "importlib_metadata-7.2.1-py3-none-any.whl", hash = "sha256:ffef94b0b66046dd8ea2d619b701fe978d9264d38f3998bc4c27ec3b146a87c8"}, + {file = "importlib_metadata-7.2.1.tar.gz", hash = "sha256:509ecb2ab77071db5137c655e24ceb3eee66e7bbc6574165d0d114d9fc4bbe68"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" @@ -876,18 +876,17 @@ line-profiler = "*" [[package]] name = "livereload" -version = "2.6.3" +version = "2.7.0" description = "Python LiveReload is an awesome tool for web developers" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, + {file = "livereload-2.7.0-py3-none-any.whl", hash = "sha256:19bee55aff51d5ade6ede0dc709189a0f904d3b906d3ea71641ed548acff3246"}, + {file = "livereload-2.7.0.tar.gz", hash = "sha256:f4ba199ef93248902841e298670eebfe1aa9e148e19b343bc57dbf1b74de0513"}, ] [package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} +tornado = "*" [[package]] name = "mako" @@ -1123,7 +1122,7 @@ files = [ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, + {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, ] [[package]] @@ -1223,13 +1222,13 @@ files = [ [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -1356,13 +1355,13 @@ files = [ [[package]] name = "pydantic" -version = "2.7.3" +version = "2.7.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"}, - {file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"}, + {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, + {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, ] [package.dependencies] @@ -1544,13 +1543,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "python-can" -version = "4.4.0" +version = "4.4.2" description = "Controller Area Network interface module for Python" optional = false python-versions = ">=3.8" files = [ - {file = "python_can-4.4.0-py3-none-any.whl", hash = "sha256:ece0013091cafc52f5134c887c43cce215d3a3d6ac90c4674f68a79526f07e43"}, - {file = "python_can-4.4.0.tar.gz", hash = "sha256:69b5bc8364d8f19c9dfc35107610634fe9ff8dda84ee2e54b81458ac8ca11737"}, + {file = "python_can-4.4.2-py3-none-any.whl", hash = "sha256:e956d781b45563c244c1f3c8fe001e292d1857519cff663d7c184673a68879f9"}, + {file = "python_can-4.4.2.tar.gz", hash = "sha256:1c46c0935f39f7a9c3e76b03249af0580689ebf7a1844195e92f87257f009df5"}, ] [package.dependencies] @@ -1566,7 +1565,7 @@ canine = ["python-can-canine (>=0.2.2)"] cantact = ["cantact (>=0.0.7)"] cvector = ["python-can-cvector"] gs-usb = ["gs-usb (>=0.2.1)"] -lint = ["black (==23.12.*)", "mypy (==1.8.*)", "pylint (==3.0.*)", "ruff (==0.1.13)"] +lint = ["black (==24.4.*)", "mypy (==1.10.*)", "pylint (==3.2.*)", "ruff (==0.4.8)"] mf4 = ["asammdf (>=6.0.0)"] neovi = ["filelock", "python-ics (>=2.12)"] nixnet = ["nixnet (>=0.3.2)"] @@ -1854,13 +1853,13 @@ files = [ [[package]] name = "safety" -version = "3.2.2" +version = "3.2.3" description = "Checks installed dependencies for known vulnerabilities and licenses." optional = false python-versions = ">=3.7" files = [ - {file = "safety-3.2.2-py3-none-any.whl", hash = "sha256:1fca683ba6c0b4acb9f74953910639472a384d7b300292c89998a0100f016081"}, - {file = "safety-3.2.2.tar.gz", hash = "sha256:2a5867004aa1e3cedc90000ab726fdad2ce0ae179863c1b1697d7e8ded1b8cdb"}, + {file = "safety-3.2.3-py3-none-any.whl", hash = "sha256:cda1e91749f610337a18b7f21f78267c127e44ebbbbcbbd419c83284279a5024"}, + {file = "safety-3.2.3.tar.gz", hash = "sha256:414154934f1727daf8a6473493944fecb380540c3f00875dc1ae377382f7d83f"}, ] [package.dependencies] @@ -1905,18 +1904,18 @@ typing-extensions = ">=4.7.1" [[package]] name = "setuptools" -version = "70.0.0" +version = "70.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, - {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, + {file = "setuptools-70.1.0-py3-none-any.whl", hash = "sha256:d9b8b771455a97c8a9f3ab3448ebe0b29b5e105f1228bba41028be116985a267"}, + {file = "setuptools-70.1.0.tar.gz", hash = "sha256:01a1e793faa5bd89abc851fa15d0a0db26f160890c7102cd8dce643e886b47f5"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shellingham" @@ -1929,17 +1928,6 @@ files = [ {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - [[package]] name = "snowballstemmer" version = "2.2.0" @@ -2279,13 +2267,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -2296,13 +2284,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.2" +version = "20.26.3" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, - {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, ] [package.dependencies] diff --git a/pybind11Config.cmake b/pybind11Config.cmake deleted file mode 100644 index a8b0800..0000000 --- a/pybind11Config.cmake +++ /dev/null @@ -1,341 +0,0 @@ -# tools/pybind11NewTools.cmake -- Build system for the pybind11 modules -# -# Copyright (c) 2020 Wenzel Jakob and Henry Schreiner -# -# All rights reserved. Use of this source code is governed by a -# BSD-style license that can be found in the LICENSE file. - -if(CMAKE_VERSION VERSION_LESS 3.12) - message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12") -endif() - -include_guard(DIRECTORY) - -get_property( - is_config - TARGET pybind11::headers - PROPERTY IMPORTED) - -if(pybind11_FIND_QUIETLY) - set(_pybind11_quiet QUIET) -else() - set(_pybind11_quiet "") -endif() - -if(NOT Python_FOUND AND NOT Python3_FOUND) - if(NOT DEFINED Python_FIND_IMPLEMENTATIONS) - set(Python_FIND_IMPLEMENTATIONS CPython PyPy) - endif() - - # GitHub Actions like activation - if(NOT DEFINED Python_ROOT_DIR AND DEFINED ENV{pythonLocation}) - set(Python_ROOT_DIR "$ENV{pythonLocation}") - endif() - - # Interpreter should not be found when cross-compiling - if(_PYBIND11_CROSSCOMPILING) - set(_pybind11_interp_component "") - else() - set(_pybind11_interp_component Interpreter) - endif() - - # Development.Module support (required for manylinux) started in 3.18 - if(CMAKE_VERSION VERSION_LESS 3.18) - set(_pybind11_dev_component Development) - else() - set(_pybind11_dev_component Development.Module OPTIONAL_COMPONENTS Development.Embed) - endif() - - # Callers need to be able to access Python_EXECUTABLE - set(_pybind11_global_keyword "") - if(NOT is_config AND NOT DEFINED Python_ARTIFACTS_INTERACTIVE) - set(Python_ARTIFACTS_INTERACTIVE TRUE) - if(NOT CMAKE_VERSION VERSION_LESS 3.24) - set(_pybind11_global_keyword "GLOBAL") - endif() - endif() - - find_package( - Python 3.7 REQUIRED COMPONENTS ${_pybind11_interp_component} ${_pybind11_dev_component} - ${_pybind11_quiet} ${_pybind11_global_keyword}) - - # If we are in submodule mode, export the Python targets to global targets. - # If this behavior is not desired, FindPython _before_ pybind11. - if(NOT is_config - AND Python_ARTIFACTS_INTERACTIVE - AND _pybind11_global_keyword STREQUAL "") - if(TARGET Python::Python) - set_property(TARGET Python::Python PROPERTY IMPORTED_GLOBAL TRUE) - endif() - if(TARGET Python::Interpreter) - set_property(TARGET Python::Interpreter PROPERTY IMPORTED_GLOBAL TRUE) - endif() - if(TARGET Python::Module) - set_property(TARGET Python::Module PROPERTY IMPORTED_GLOBAL TRUE) - endif() - endif() - - # Explicitly export version for callers (including our own functions) - if(NOT is_config AND Python_ARTIFACTS_INTERACTIVE) - set(Python_VERSION - "${Python_VERSION}" - CACHE INTERNAL "") - set(Python_VERSION_MAJOR - "${Python_VERSION_MAJOR}" - CACHE INTERNAL "") - set(Python_VERSION_MINOR - "${Python_VERSION_MINOR}" - CACHE INTERNAL "") - set(Python_VERSION_PATCH - "${Python_VERSION_PATCH}" - CACHE INTERNAL "") - endif() -endif() - -if(Python_FOUND) - set(_Python - Python - CACHE INTERNAL "" FORCE) -elseif(Python3_FOUND) - set(_Python - Python3 - CACHE INTERNAL "" FORCE) -endif() - -if(PYBIND11_MASTER_PROJECT) - if(${_Python}_INTERPRETER_ID MATCHES "PyPy") - message(STATUS "PyPy ${${_Python}_PyPy_VERSION} (Py ${${_Python}_VERSION})") - else() - message(STATUS "${_Python} ${${_Python}_VERSION}") - endif() -endif() - -if(NOT _PYBIND11_CROSSCOMPILING) - # If a user finds Python, they may forget to include the Interpreter component - # and the following two steps require it. It is highly recommended by CMake - # when finding development libraries anyway, so we will require it. - if(NOT DEFINED ${_Python}_EXECUTABLE) - message( - FATAL_ERROR - "${_Python} was found without the Interpreter component. Pybind11 requires this component." - ) - - endif() - - if(DEFINED PYBIND11_PYTHON_EXECUTABLE_LAST AND NOT ${_Python}_EXECUTABLE STREQUAL - PYBIND11_PYTHON_EXECUTABLE_LAST) - # Detect changes to the Python version/binary in subsequent CMake runs, and refresh config if needed - unset(PYTHON_IS_DEBUG CACHE) - unset(PYTHON_MODULE_EXTENSION CACHE) - endif() - - set(PYBIND11_PYTHON_EXECUTABLE_LAST - "${${_Python}_EXECUTABLE}" - CACHE INTERNAL "Python executable during the last CMake run") - - if(NOT DEFINED PYTHON_IS_DEBUG) - # Debug check - see https://stackoverflow.com/questions/646518/python-how-to-detect-debug-Interpreter - execute_process( - COMMAND "${${_Python}_EXECUTABLE}" "-c" - "import sys; sys.exit(hasattr(sys, 'gettotalrefcount'))" - RESULT_VARIABLE _PYTHON_IS_DEBUG) - set(PYTHON_IS_DEBUG - "${_PYTHON_IS_DEBUG}" - CACHE INTERNAL "Python debug status") - endif() - - # Get the suffix - SO is deprecated, should use EXT_SUFFIX, but this is - # required for PyPy3 (as of 7.3.1) - if(NOT DEFINED PYTHON_MODULE_EXTENSION OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX) - execute_process( - COMMAND - "${${_Python}_EXECUTABLE}" "-c" - "import sys, importlib; s = importlib.import_module('distutils.sysconfig' if sys.version_info < (3, 10) else 'sysconfig'); print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO'))" - OUTPUT_VARIABLE _PYTHON_MODULE_EXT_SUFFIX - ERROR_VARIABLE _PYTHON_MODULE_EXT_SUFFIX_ERR - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(_PYTHON_MODULE_EXT_SUFFIX STREQUAL "") - message( - FATAL_ERROR - "pybind11 could not query the module file extension, likely the 'distutils'" - "package is not installed. Full error message:\n${_PYTHON_MODULE_EXT_SUFFIX_ERR}") - endif() - - # This needs to be available for the pybind11_extension function - if(NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX) - get_filename_component(_PYTHON_MODULE_DEBUG_POSTFIX "${_PYTHON_MODULE_EXT_SUFFIX}" NAME_WE) - set(PYTHON_MODULE_DEBUG_POSTFIX - "${_PYTHON_MODULE_DEBUG_POSTFIX}" - CACHE INTERNAL "") - endif() - - if(NOT DEFINED PYTHON_MODULE_EXTENSION) - get_filename_component(_PYTHON_MODULE_EXTENSION "${_PYTHON_MODULE_EXT_SUFFIX}" EXT) - set(PYTHON_MODULE_EXTENSION - "${_PYTHON_MODULE_EXTENSION}" - CACHE INTERNAL "") - endif() - endif() -else() - if(NOT DEFINED PYTHON_IS_DEBUG - OR NOT DEFINED PYTHON_MODULE_EXTENSION - OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX) - include("${CMAKE_CURRENT_LIST_DIR}/pybind11GuessPythonExtSuffix.cmake") - pybind11_guess_python_module_extension("${_Python}") - endif() - # When cross-compiling, we cannot query the Python interpreter, so we require - # the user to set these variables explicitly. - if(NOT DEFINED PYTHON_IS_DEBUG - OR NOT DEFINED PYTHON_MODULE_EXTENSION - OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX) - message( - FATAL_ERROR - "When cross-compiling, you should set the PYTHON_IS_DEBUG, PYTHON_MODULE_EXTENSION and PYTHON_MODULE_DEBUG_POSTFIX \ - variables appropriately before loading pybind11 (e.g. in your CMake toolchain file)") - endif() -endif() - -# Python debug libraries expose slightly different objects before 3.8 -# https://docs.python.org/3.6/c-api/intro.html#debugging-builds -# https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib -if(PYTHON_IS_DEBUG) - set_property( - TARGET pybind11::pybind11 - APPEND - PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG) -endif() - -# Check on every access - since Python can change - do nothing in that case. - -if(DEFINED ${_Python}_INCLUDE_DIRS) - # Only add Python for build - must be added during the import for config - # since it has to be re-discovered. - # - # This needs to be a target to be included after the local pybind11 - # directory, just in case there there is an installed pybind11 sitting - # next to Python's includes. It also ensures Python is a SYSTEM library. - add_library(pybind11::python_headers INTERFACE IMPORTED) - set_property( - TARGET pybind11::python_headers PROPERTY INTERFACE_INCLUDE_DIRECTORIES - "$") - set_property( - TARGET pybind11::pybind11 - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python_headers) - set(pybind11_INCLUDE_DIRS - "${pybind11_INCLUDE_DIR}" "${${_Python}_INCLUDE_DIRS}" - CACHE INTERNAL "Directories where pybind11 and possibly Python headers are located") -endif() - -# In CMake 3.18+, you can find these separately, so include an if -if(TARGET ${_Python}::Python) - set_property( - TARGET pybind11::embed - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Python) -endif() - -# CMake 3.15+ has this -if(TARGET ${_Python}::Module) - set_property( - TARGET pybind11::module - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Module) -else() - set_property( - TARGET pybind11::module - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python_link_helper) -endif() - -# WITHOUT_SOABI and WITH_SOABI will disable the custom extension handling used by pybind11. -# WITH_SOABI is passed on to python_add_library. -function(pybind11_add_module target_name) - cmake_parse_arguments(PARSE_ARGV 1 ARG - "STATIC;SHARED;MODULE;THIN_LTO;OPT_SIZE;NO_EXTRAS;WITHOUT_SOABI" "" "") - - if(ARG_STATIC) - set(lib_type STATIC) - elseif(ARG_SHARED) - set(lib_type SHARED) - else() - set(lib_type MODULE) - endif() - - if("${_Python}" STREQUAL "Python") - python_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) - elseif("${_Python}" STREQUAL "Python3") - python3_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) - else() - message(FATAL_ERROR "Cannot detect FindPython version: ${_Python}") - endif() - - target_link_libraries(${target_name} PRIVATE pybind11::headers) - - if(lib_type STREQUAL "MODULE") - target_link_libraries(${target_name} PRIVATE pybind11::module) - else() - target_link_libraries(${target_name} PRIVATE pybind11::embed) - endif() - - if(MSVC) - target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) - endif() - - # -fvisibility=hidden is required to allow multiple modules compiled against - # different pybind versions to work properly, and for some features (e.g. - # py::module_local). We force it on everything inside the `pybind11` - # namespace; also turning it on for a pybind module compilation here avoids - # potential warnings or issues from having mixed hidden/non-hidden types. - if(NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) - set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") - endif() - - if(NOT DEFINED CMAKE_CUDA_VISIBILITY_PRESET) - set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") - endif() - - # If we don't pass a WITH_SOABI or WITHOUT_SOABI, use our own default handling of extensions - if(NOT ARG_WITHOUT_SOABI AND NOT "WITH_SOABI" IN_LIST ARG_UNPARSED_ARGUMENTS) - pybind11_extension(${target_name}) - endif() - - if(ARG_NO_EXTRAS) - return() - endif() - - if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) - if(ARG_THIN_LTO) - target_link_libraries(${target_name} PRIVATE pybind11::thin_lto) - else() - target_link_libraries(${target_name} PRIVATE pybind11::lto) - endif() - endif() - - if(DEFINED CMAKE_BUILD_TYPE) # see https://github.com/pybind/pybind11/issues/4454 - # Use case-insensitive comparison to match the result of $ - string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) - if(NOT MSVC AND NOT "${uppercase_CMAKE_BUILD_TYPE}" MATCHES DEBUG|RELWITHDEBINFO) - # Strip unnecessary sections of the binary on Linux/macOS - pybind11_strip(${target_name}) - endif() - endif() - - if(MSVC) - target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) - endif() - - if(ARG_OPT_SIZE) - target_link_libraries(${target_name} PRIVATE pybind11::opt_size) - endif() -endfunction() - -function(pybind11_extension name) - # The extension is precomputed - set_target_properties( - ${name} - PROPERTIES PREFIX "" - DEBUG_POSTFIX "${PYTHON_MODULE_DEBUG_POSTFIX}" - SUFFIX "${PYTHON_MODULE_EXTENSION}") -endfunction() diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 6c7d373..f533ecf 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -62,8 +62,8 @@ template<> inline std::string to_binary(const std::string &value) { std::string result; - auto ptr = reinterpret_cast(value.c_str()); - std::size_t length = std::size(value); + auto ptr = reinterpret_cast(value.c_str()); + const std::size_t length = std::size(value); // We are using Pascal strings as serialization format. auto len_bin = to_binary(length); diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index 6bdc67b..18e5965 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -30,6 +30,16 @@ const std::map> TYPE #endif }; +inline std::vector get_data_types() { + std::vector result; + + for (const auto& [k, v] : TYPE_MAP) { + result.emplace_back(k); + } + + return result; +} + class McObject { public: diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 9967443..f583582 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -22,9 +22,13 @@ XcpLogFileUnfolder, _PyXcpLogFileReader, _PyXcpLogFileWriter, + data_types, ) +data_types = data_types() + + @dataclass class XcpLogFileHeader: """ """ diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 205f9ad..297081a 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -240,19 +240,19 @@ inline std::string& trim(std::string& s) { inline std::string current_timestamp() { std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); -#if defined(_MSC_VER) - errno_t err; - char tbuf[128]; + #if defined(_MSC_VER) + errno_t err; + char tbuf[128]; std::string result{}; err = ::ctime_s(tbuf, 128, &now); - if (err == 0) { + if (err == 0) { result = tbuf; } -#else + #else std::string result{ ::ctime(&now) }; -#endif + #endif // result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end()); return trim(result); diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 6697b0f..68591c5 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -177,13 +177,13 @@ void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int #if HAS_FLOAT16 == 1 template<> void set_value(blob_t* buf, std::uint64_t offset, std::float16_t value) { - //set_value(buf, offset, *reinterpret_cast(&value)); + // set_value(buf, offset, *reinterpret_cast(&value)); set_value(buf, offset, std::bit_cast(value)); } template<> void set_value_swapped(blob_t* buf, std::uint64_t offset, std::float16_t value) { - //set_value_swapped(buf, offset, *reinterpret_cast(&value)); + // set_value_swapped(buf, offset, *reinterpret_cast(&value)); set_value_swapped(buf, offset, std::bit_cast(value)); } #endif @@ -191,38 +191,38 @@ void set_value_swapped(blob_t* buf, std::uint64_t offset, std::f #if HAS_BFLOAT16 == 1 template<> void set_value(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { - //set_value(buf, offset, *reinterpret_cast(&value)); + // set_value(buf, offset, *reinterpret_cast(&value)); set_value(buf, offset, std::bit_cast(value)); } template<> void set_value_swapped(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { - //set_value_swapped(buf, offset, *reinterpret_cast(&value)); + // set_value_swapped(buf, offset, *reinterpret_cast(&value)); set_value_swapped(buf, offset, std::bit_cast(value)); } #endif template<> void set_value(blob_t* buf, std::uint64_t offset, float value) { - //set_value(buf, offset, *reinterpret_cast(&value)); + // set_value(buf, offset, *reinterpret_cast(&value)); set_value(buf, offset, std::bit_cast(value)); } template<> void set_value_swapped(blob_t* buf, std::uint64_t offset, float value) { - //set_value_swapped(buf, offset, *reinterpret_cast(&value)); + // set_value_swapped(buf, offset, *reinterpret_cast(&value)); set_value_swapped(buf, offset, std::bit_cast(value)); } template<> void set_value(blob_t* buf, std::uint64_t offset, double value) { - //set_value(buf, offset, *reinterpret_cast(&value)); + // set_value(buf, offset, *reinterpret_cast(&value)); set_value(buf, offset, std::bit_cast(value)); } template<> void set_value_swapped(blob_t* buf, std::uint64_t offset, double value) { - //set_value_swapped(buf, offset, *reinterpret_cast(&value)); + // set_value_swapped(buf, offset, *reinterpret_cast(&value)); set_value_swapped(buf, offset, std::bit_cast(value)); } @@ -808,8 +808,8 @@ class Deserializer { return tmp; } - //template<> - //inline std::string from_binary_str() { + // template<> + // inline std::string from_binary_str() { inline std::string from_binary_str() { auto length = from_binary(); std::string result; diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 71e095a..6e6c937 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -59,6 +59,7 @@ class PyXcpLogFileUnfolder : public XcpLogFileUnfolder { PYBIND11_MODULE(rekorder, m) { m.doc() = "XCP raw frame recorder."; + m.def("data_types", get_data_types); #if 0 version; diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index a6eb47f..32b07d6 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -148,8 +148,7 @@ class XcpLogFileWriter { // printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); const int cp_size = ::LZ4_compress_HC( reinterpret_cast(m_intermediate_storage), - reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), - m_intermediate_storage_offset, + reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), m_intermediate_storage_offset, LZ4_COMPRESSBOUND(m_intermediate_storage_offset), LZ4HC_CLEVEL_MAX ); @@ -170,10 +169,7 @@ class XcpLogFileWriter { container.size_compressed = cp_size; container.size_uncompressed = m_container_size_uncompressed; - _fcopy(reinterpret_cast(ptr(m_offset)), - reinterpret_cast(&container), - detail::CONTAINER_SIZE - ); + _fcopy(reinterpret_cast(ptr(m_offset)), reinterpret_cast(&container), detail::CONTAINER_SIZE); m_offset += (detail::CONTAINER_SIZE + cp_size); m_total_size_uncompressed += m_container_size_uncompressed; From a9ab57387eec9c78b004127c6c4fbe80f7187f76 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 25 Jun 2024 09:10:53 +0300 Subject: [PATCH 65/99] today --- .gitmodules | 8 +- CMakeLists.txt | 210 ++-- build_ext.py | 160 +-- poetry.lock | 4 +- pyproject.toml | 332 ++--- pyxcp/config/__init__.py | 2128 +++++++++++++++---------------- pyxcp/cpp_ext/helper.hpp | 188 +-- pyxcp/cpp_ext/mcobject.hpp | 376 +++--- pyxcp/recorder/__init__.py | 2 +- pyxcp/recorder/reader.hpp | 278 ++-- pyxcp/recorder/rekorder.hpp | 544 ++++---- pyxcp/recorder/unfolder.hpp | 2366 +++++++++++++++++------------------ pyxcp/recorder/wrap.cpp | 352 +++--- pyxcp/recorder/writer.hpp | 570 ++++----- 14 files changed, 3760 insertions(+), 3758 deletions(-) diff --git a/.gitmodules b/.gitmodules index 641a09d..6944698 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ -[submodule "pybind11"] - path = pybind11 - url = https://github.com/pybind/pybind11.git - branch = master +[submodule "pybind11"] + path = pybind11 + url = https://github.com/pybind/pybind11.git + branch = master diff --git a/CMakeLists.txt b/CMakeLists.txt index b56a798..357d431 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,105 +1,105 @@ - -cmake_minimum_required(VERSION 3.7...3.29) -project(pyxcp_extensions LANGUAGES C CXX) - -find_package(Python COMPONENTS Interpreter Development) -find_package(pybind11 CONFIG) - -SET(CMAKE_C_STANDARD 17) -set(CMAKE_CXX_STANDARD 23) - -message( STATUS "Found pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}: ${pybind11_INCLUDE_DIRS}") - -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/dist") - - -SET(GCC_N_CLANG_BASE_OPTIONS "-std=c++23 -Wall -Wextra -Wpedantic -Warray-bounds -mtune=native -fexceptions") -# target_link_options(${PROJECT_NAME} PUBLIC -flto=auto) - -SET(MSVC_BASE_OPTIONS "/W3 /permissive- /EHsc /bigobj /std:c++latest") - - -# if (CMAKE_BUILD_TYPE STREQUAL "DEBUG") -# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Od /fsanitize=address /Zi") -# else () -# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox ") # /GL -# endif () - - -if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") - set(ENV{MACOSX_DEPLOYMENT_TARGET} "11.0") - SET(GCC_N_CLANG_EXTRA_OPTIONS "-stdlib=libc++") - message("Platform is Darwin") -elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows") - message("Platform is WINDOWS") - SET(MSVC_EXTRA_OPTIONS "") -elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") - SET(GCC_N_CLANG_EXTRA_OPTIONS "-fcoroutines -fvisibility=hidden -g0") - message("Platform is LINUX") -endif() - - -IF (CMAKE_C_COMPILER_ID STREQUAL "GNU") - -ELSEIF (CMAKE_C_COMPILER_ID MATCHES "Clang") - -ELSEIF (CMAKE_C_COMPILER_ID MATCHES "MSVC") - -ELSE () - -ENDIF () - -IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - -ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - -ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - -ELSE () - - -ENDIF () - -message("Compiling C with: " ${CMAKE_C_COMPILER_ID}) -message("Compiling Cpp with: " ${CMAKE_CXX_COMPILER_ID}) - -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -set(EXTENSION_INCS ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/cpp_ext) - -pybind11_add_module(rekorder pyxcp/recorder/wrap.cpp pyxcp/recorder/lz4.c pyxcp/recorder/lz4hc.c) -pybind11_add_module(cpp_ext pyxcp/cpp_ext/extension_wrapper.cpp) -pybind11_add_module(stim pyxcp/daq_stim/stim_wrapper.cpp pyxcp/daq_stim/stim.cpp pyxcp/daq_stim/scheduler.cpp) - -target_include_directories(rekorder PRIVATE ${EXTENSION_INCS} ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/recorder) -target_include_directories(cpp_ext PRIVATE ${EXTENSION_INCS}) -target_include_directories(stim PRIVATE ${EXTENSION_INCS} ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/daq_stim) - -target_compile_options(rekorder PUBLIC "-DEXTENSION_NAME=pyxcp.recorder.rekorder") -target_compile_options(cpp_ext PUBLIC "-DEXTENSION_NAME=pyxcp.cpp_ext.cpp_ext") -target_compile_options(stim PUBLIC "-DEXTENSION_NAME=pyxcp.daq_stim.stim") - -add_executable(asamkeydll ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/asamkeydll.c) -# set_target_properties(MyTarget PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") - -if (CMAKE_SIZEOF_VOID_P EQUAL 8) - # CMAKE_SYSTEM_NAME STREQUAL "Windows" -endif() - -IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_N_CLANG_BASE_OPTIONS} ${GCC_N_CLANG_EXTRA_OPTIONS}") - target_link_options(cpp_ext PUBLIC -flto=auto) - target_link_options(stim PUBLIC -flto=auto) - target_link_options(rekorder PUBLIC -flto=auto) -ELSEIF (CMAKE_C_COMPILER_ID MATCHES "MSVC") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MSVC_BASE_OPTIONS} ${MSVC_EXTRA_OPTIONS}") -ENDIF() - -# target_include_directories(preprocessor PUBLIC $) -# target_link_libraries(preprocessor pybind11::headers) -# set_target_properties(preprocessor PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON CXX_VISIBILITY_PRESET ON VISIBILITY_INLINES_HIDDEN ON) - -install(TARGETS rekorder LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/recorder) -install(TARGETS cpp_ext LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/cpp_ext) -install(TARGETS stim LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/daq_stim) -# install(TARGETS asamkeydll LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/) + +cmake_minimum_required(VERSION 3.7...3.29) +project(pyxcp_extensions LANGUAGES C CXX) + +find_package(Python COMPONENTS Interpreter Development) +find_package(pybind11 CONFIG) + +SET(CMAKE_C_STANDARD 17) +set(CMAKE_CXX_STANDARD 23) + +message( STATUS "Found pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}: ${pybind11_INCLUDE_DIRS}") + +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/dist") + + +SET(GCC_N_CLANG_BASE_OPTIONS "-std=c++23 -Wall -Wextra -Wpedantic -Warray-bounds -mtune=native -fexceptions") +# target_link_options(${PROJECT_NAME} PUBLIC -flto=auto) + +SET(MSVC_BASE_OPTIONS "/W3 /permissive- /EHsc /bigobj /std:c++latest") + + +# if (CMAKE_BUILD_TYPE STREQUAL "DEBUG") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Od /fsanitize=address /Zi") +# else () +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox ") # /GL +# endif () + + +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(ENV{MACOSX_DEPLOYMENT_TARGET} "11.0") + SET(GCC_N_CLANG_EXTRA_OPTIONS "-stdlib=libc++") + message("Platform is Darwin") +elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows") + message("Platform is WINDOWS") + SET(MSVC_EXTRA_OPTIONS "") +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") + SET(GCC_N_CLANG_EXTRA_OPTIONS "-fcoroutines -fvisibility=hidden -g0") + message("Platform is LINUX") +endif() + + +IF (CMAKE_C_COMPILER_ID STREQUAL "GNU") + +ELSEIF (CMAKE_C_COMPILER_ID MATCHES "Clang") + +ELSEIF (CMAKE_C_COMPILER_ID MATCHES "MSVC") + +ELSE () + +ENDIF () + +IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + +ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + +ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + +ELSE () + + +ENDIF () + +message("Compiling C with: " ${CMAKE_C_COMPILER_ID}) +message("Compiling Cpp with: " ${CMAKE_CXX_COMPILER_ID}) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(EXTENSION_INCS ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/cpp_ext) + +pybind11_add_module(rekorder pyxcp/recorder/wrap.cpp pyxcp/recorder/lz4.c pyxcp/recorder/lz4hc.c) +pybind11_add_module(cpp_ext pyxcp/cpp_ext/extension_wrapper.cpp) +pybind11_add_module(stim pyxcp/daq_stim/stim_wrapper.cpp pyxcp/daq_stim/stim.cpp pyxcp/daq_stim/scheduler.cpp) + +target_include_directories(rekorder PRIVATE ${EXTENSION_INCS} ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/recorder) +target_include_directories(cpp_ext PRIVATE ${EXTENSION_INCS}) +target_include_directories(stim PRIVATE ${EXTENSION_INCS} ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/daq_stim) + +target_compile_options(rekorder PUBLIC "-DEXTENSION_NAME=pyxcp.recorder.rekorder") +target_compile_options(cpp_ext PUBLIC "-DEXTENSION_NAME=pyxcp.cpp_ext.cpp_ext") +target_compile_options(stim PUBLIC "-DEXTENSION_NAME=pyxcp.daq_stim.stim") + +add_executable(asamkeydll ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/asamkeydll.c) +# set_target_properties(MyTarget PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") + +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # CMAKE_SYSTEM_NAME STREQUAL "Windows" +endif() + +IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_N_CLANG_BASE_OPTIONS} ${GCC_N_CLANG_EXTRA_OPTIONS}") + target_link_options(cpp_ext PUBLIC -flto=auto) + target_link_options(stim PUBLIC -flto=auto) + target_link_options(rekorder PUBLIC -flto=auto) +ELSEIF (CMAKE_C_COMPILER_ID MATCHES "MSVC") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MSVC_BASE_OPTIONS} ${MSVC_EXTRA_OPTIONS}") +ENDIF() + +# target_include_directories(preprocessor PUBLIC $) +# target_link_libraries(preprocessor pybind11::headers) +# set_target_properties(preprocessor PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON CXX_VISIBILITY_PRESET ON VISIBILITY_INLINES_HIDDEN ON) + +install(TARGETS rekorder LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/recorder) +install(TARGETS cpp_ext LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/cpp_ext) +install(TARGETS stim LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/daq_stim) +# install(TARGETS asamkeydll LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/pyxcp/) diff --git a/build_ext.py b/build_ext.py index edc3f66..d53a192 100644 --- a/build_ext.py +++ b/build_ext.py @@ -1,80 +1,80 @@ -#!/usr/bin/env python - -import multiprocessing as mp -import os -import platform -import re -import subprocess # nosec -import sys -from pathlib import Path - -# from pprint import pprint -from tempfile import TemporaryDirectory - - -print("Platform", platform.system()) - -TOP_DIR = Path(__file__).parent - - -def banner(msg: str) -> None: - print("=" * 80) - print(str.center(msg, 80)) - print("=" * 80) - - -def build_extension(debug: bool = False) -> None: - print("CMakeBuild::build_extension()") - - debug = int(os.environ.get("DEBUG", 0)) or debug - cfg = "Debug" if debug else "Release" - - # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON - # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code - # from Python. - cmake_args = [ - # f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", - # "-G Ninja", - f"-DPYTHON_EXECUTABLE={sys.executable}", - f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm - ] - build_args = ["--config Release", "--verbose"] - # Adding CMake arguments set as environment variable - # (needed e.g. to build for ARM OSx on conda-forge) - - # cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 /path/to/src - - if sys.platform.startswith("darwin"): - # Cross-compile support for macOS - respect ARCHFLAGS if set - archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) - if archs: - cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] - - build_temp = Path(TemporaryDirectory(suffix=".build-temp").name) / "extension_it_in" - # build_temp = Path(".") / "build" - print("cwd:", os.getcwd(), "build-dir:", build_temp, "top:", str(TOP_DIR)) - # print("PHILEZ:", os.listdir(TOP_DIR)) - if not build_temp.exists(): - build_temp.mkdir(parents=True) - - banner("Step #1: Configure") - # cmake_args += ["--debug-output"] - print("aufruf:", ["cmake", str(TOP_DIR), *cmake_args]) - subprocess.run(["cmake", str(TOP_DIR), *cmake_args], cwd=build_temp, check=True) # nosec - - cmake_args += [f"--parallel {mp.cpu_count()}"] - - banner("Step #2: Build") - # subprocess.run(["cmake", "--build", ".", *build_args], cwd=build_temp, check=True) # nosec - # build_args += ["-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON"] - subprocess.run(["cmake", "--build", build_temp, *build_args], cwd=TOP_DIR, check=True) # nosec - - banner("Step #3: Install") - # subprocess.run(["cmake", "--install", "."], cwd=build_temp, check=True) # nosec - subprocess.run(["cmake", "--install", build_temp], cwd=TOP_DIR, check=True) # nosec - - -if __name__ == "__main__": - includes = subprocess.getoutput("pybind11-config --cmakedir") # nosec - os.environ["pybind11_DIR"] = includes - build_extension() +#!/usr/bin/env python + +import multiprocessing as mp +import os +import platform +import re +import subprocess # nosec +import sys +from pathlib import Path + +# from pprint import pprint +from tempfile import TemporaryDirectory + + +print("Platform", platform.system()) + +TOP_DIR = Path(__file__).parent + + +def banner(msg: str) -> None: + print("=" * 80) + print(str.center(msg, 80)) + print("=" * 80) + + +def build_extension(debug: bool = False) -> None: + print("CMakeBuild::build_extension()") + + debug = int(os.environ.get("DEBUG", 0)) or debug + cfg = "Debug" if debug else "Release" + + # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON + # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code + # from Python. + cmake_args = [ + # f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", + # "-G Ninja", + f"-DPYTHON_EXECUTABLE={sys.executable}", + f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm + ] + build_args = ["--config Release", "--verbose"] + # Adding CMake arguments set as environment variable + # (needed e.g. to build for ARM OSx on conda-forge) + + # cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 /path/to/src + + if sys.platform.startswith("darwin"): + # Cross-compile support for macOS - respect ARCHFLAGS if set + archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) + if archs: + cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] + + build_temp = Path(TemporaryDirectory(suffix=".build-temp").name) / "extension_it_in" + # build_temp = Path(".") / "build" + print("cwd:", os.getcwd(), "build-dir:", build_temp, "top:", str(TOP_DIR)) + # print("PHILEZ:", os.listdir(TOP_DIR)) + if not build_temp.exists(): + build_temp.mkdir(parents=True) + + banner("Step #1: Configure") + # cmake_args += ["--debug-output"] + print("aufruf:", ["cmake", str(TOP_DIR), *cmake_args]) + subprocess.run(["cmake", str(TOP_DIR), *cmake_args], cwd=build_temp, check=True) # nosec + + cmake_args += [f"--parallel {mp.cpu_count()}"] + + banner("Step #2: Build") + # subprocess.run(["cmake", "--build", ".", *build_args], cwd=build_temp, check=True) # nosec + # build_args += ["-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON"] + subprocess.run(["cmake", "--build", build_temp, *build_args], cwd=TOP_DIR, check=True) # nosec + + banner("Step #3: Install") + # subprocess.run(["cmake", "--install", "."], cwd=build_temp, check=True) # nosec + subprocess.run(["cmake", "--install", build_temp], cwd=TOP_DIR, check=True) # nosec + + +if __name__ == "__main__": + includes = subprocess.getoutput("pybind11-config --cmakedir") # nosec + os.environ["pybind11_DIR"] = includes + build_extension() diff --git a/poetry.lock b/poetry.lock index 57346af..1949f92 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1122,7 +1122,7 @@ files = [ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, + {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] [[package]] @@ -2426,4 +2426,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "d119edcc9bde40af26d6e5ea6efc6a7e578e4e7c8ad6c0b91dd56d65cfc3796f" +content-hash = "55351fe922a2782147c19d96431addbc8911e925f19707c9dba68785fd99a55d" diff --git a/pyproject.toml b/pyproject.toml index 151f0cc..7cb054d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,165 +1,167 @@ - -[build-system] -requires = ["poetry-core>=1.0.0", "setuptools>=68.0.0" , "pybind11[global]>=2.11.1"] -build-backend = "poetry.core.masonry.api" - - -[tool.poetry.dev-dependencies] -Pygments = ">=2.10.0" -bandit = ">=1.7.4" -black = ">=21.10b0" -coverage = {extras = ["toml"], version = ">=6.2"} -darglint = ">=1.8.1" -flake8 = ">=4.0.1" -flake8-bugbear = ">=21.9.2" -flake8-docstrings = ">=1.6.0" -flake8-rst-docstrings = ">=0.2.5" -furo = ">=2021.11.12" -isort = ">=5.10.1" -mypy = ">=0.930" -pep8-naming = ">=0.12.1" -pre-commit = ">=2.16.0" -pre-commit-hooks = ">=4.1.0" -pytest = ">=6.2.5" -pyupgrade = ">=2.29.1" -safety = ">=1.10.3" -sphinx = ">=4.3.2" -sphinx-autobuild = ">=2021.3.14" -sphinx-click = ">=3.0.2" -typeguard = ">=2.13.3" -xdoctest = {extras = ["colors"], version = ">=0.15.10"} -myst-parser = {version = ">=0.16.1"} - -[project] -name = "pyxcp" -version = "0.21.6" -dynamic = ["license", "readme", "authors", "requires-python", "description", "classifiers", "scripts", "dependencies", "optional-dependencies"] - - -[tool.poetry] -authors = ["Christoph Schueler "] -name = "pyxcp" -version = "0.21.6" -readme = "README.md" -description = "Universal Calibration Protocol for Python" -keywords = ["automotive", "ecu", "xcp", "asam", "autosar"] -homepage = "https://github.com/christoph2/pyxcp" -license = "LGPLv3" -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", "Topic :: Software Development", - "Topic :: Scientific/Engineering", - "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12" -] -build = "build_ext.py" -include = [ - { path = "pyxcp/cpp_ext/*.so", format = "wheel" }, - { path = "pyxcp/cpp_ext/*.pyd", format = "wheel" }, - { path = "pyxcp/daq_stim/*.so", format = "wheel" }, - { path = "pyxcp/daq_stim/*.pyd", format = "wheel" }, - { path = "pyxcp/recorder/*.so", format = "wheel" }, - { path = "pyxcp/recorder/*.pyd", format = "wheel" }, - { path = "pyxcp/*.exe", format = "wheel" }, - { path = "CMakeLists.txt", format = "wheel" }, -] - -[tool.poetry.dependencies] -python = "^3.8" -construct = "^2.10.68" -mako = "^1.2.4" -pyserial = "^3.5" -pyusb = "^1.2.1" -python-can = "^4.2.2" -uptime = "^3.0.1" -rich = "^13.6.0" -chardet = "^5.2.0" -traitlets = "<=5.11.2" -line-profiler-pycharm = "^1.1.0" - -toml = "^0.10.2" -bandit = "^1.7.8" -[tool.poetry.group.dev.dependencies] -ruff = "^0.1.0" - -[tool.poetry.scripts] -pyxcp-probe-can-drivers = "pyxcp.scripts.pyxcp_probe_can_drivers:main" -xcp-id-scanner = "pyxcp.scripts.xcp_id_scanner:main" -xcp-fetch-a2l = "pyxcp.scripts.xcp_fetch_a2l:main" -xcp-info = "pyxcp.scripts.xcp_info:main" - -[tool.pytest] -addopts = "--verbose --tb=short --junitxml=result.xml -o junit_family=xunit2" -testpaths = "pyxcp/tests" - -[tool.isort] -profile = "black" -force_single_line = false -lines_after_imports = 2 - -[tool.mypy] -strict = false -warn_unreachable = true -pretty = true -show_column_numbers = true -show_error_context = true - -[tool.flake8] -ignore = ["D203", "E203", "E266", "E501", "W503", "F403", "F401", "BLK100"] -exclude = ''' -/( - \.git - | __pycache__ - | __pypackages__ - | \.mypy_cache - | \.tox - | \.venv - | \.eggs - | _build - | build - | docs - | dist - | experimental -)/ -''' -max-complexity = 10 -count = true -statistics = true -show-source = true -max-line-length = 132 -select = ["B","C","E","F","W","T4","B9"] - -[tool.ruff] -line-length = 132 - -[tool.black] -line-length=132 -include = '\.pyi?$' -exclude = ''' -/( - \.git - | \.mypy_cache - | \.tox - | \.venv - | _build - | build - | docs - | experimental - | __pycache__ - | __pypackages__ - | dist -)/ -''' - -[tool.cibuildwheel] -build-verbosity = 3 -#test-command = "pytest {package}/tests" -#test-command = "pytest -svv pyxcp/tests" -build = "cp3{7,8,9,10,11,12}-*" -skip = ["*-manylinux_i686", "*-musllinux_x86_64", "*-musllinux_i686"] # Skip Linux 32bit and MUSL builds. -build-frontend = "build" + +[build-system] +requires = ["poetry-core>=1.0.0", "setuptools>=68.0.0" , "pybind11[global]>=2.11.1"] +build-backend = "poetry.core.masonry.api" + + +[tool.poetry.dev-dependencies] +Pygments = ">=2.10.0" +bandit = ">=1.7.4" +black = ">=21.10b0" +coverage = {extras = ["toml"], version = ">=6.2"} +darglint = ">=1.8.1" +flake8 = ">=4.0.1" +flake8-bugbear = ">=21.9.2" +flake8-docstrings = ">=1.6.0" +flake8-rst-docstrings = ">=0.2.5" +furo = ">=2021.11.12" +isort = ">=5.10.1" +mypy = ">=0.930" +pep8-naming = ">=0.12.1" +pre-commit = ">=2.16.0" +pre-commit-hooks = ">=4.1.0" +pytest = ">=6.2.5" +pyupgrade = ">=2.29.1" +safety = ">=1.10.3" +sphinx = ">=4.3.2" +sphinx-autobuild = ">=2021.3.14" +sphinx-click = ">=3.0.2" +typeguard = ">=2.13.3" +xdoctest = {extras = ["colors"], version = ">=0.15.10"} +myst-parser = {version = ">=0.16.1"} + +[project] +name = "pyxcp" +version = "0.21.6" +dynamic = ["license", "readme", "authors", "requires-python", "description", "classifiers", "scripts", "dependencies", "optional-dependencies"] + + +[tool.poetry] +authors = ["Christoph Schueler "] +name = "pyxcp" +version = "0.21.6" +readme = "README.md" +description = "Universal Calibration Protocol for Python" +keywords = ["automotive", "ecu", "xcp", "asam", "autosar"] +homepage = "https://github.com/christoph2/pyxcp" +license = "LGPLv3" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", "Topic :: Software Development", + "Topic :: Scientific/Engineering", + "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12" +] +build = "build_ext.py" +include = [ + { path = "pyxcp/cpp_ext/*.so", format = "wheel" }, + { path = "pyxcp/cpp_ext/*.pyd", format = "wheel" }, + { path = "pyxcp/daq_stim/*.so", format = "wheel" }, + { path = "pyxcp/daq_stim/*.pyd", format = "wheel" }, + { path = "pyxcp/recorder/*.so", format = "wheel" }, + { path = "pyxcp/recorder/*.pyd", format = "wheel" }, + { path = "pyxcp/*.exe", format = "wheel" }, + { path = "CMakeLists.txt", format = "wheel" }, +] + +[tool.poetry.dependencies] +python = "^3.8" +construct = "^2.10.68" +mako = "^1.2.4" +pyserial = "^3.5" +pyusb = "^1.2.1" +python-can = "^4.2.2" +uptime = "^3.0.1" +rich = "^13.6.0" +chardet = "^5.2.0" +traitlets = "<=5.11.2" +line-profiler-pycharm = "^1.1.0" + +toml = "^0.10.2" +bandit = "^1.7.8" +[tool.poetry.group.dev.dependencies] +ruff = "^0.1.0" + +pre-commit-hooks = "^4.6.0" +darglint = "^1.8.1" +[tool.poetry.scripts] +pyxcp-probe-can-drivers = "pyxcp.scripts.pyxcp_probe_can_drivers:main" +xcp-id-scanner = "pyxcp.scripts.xcp_id_scanner:main" +xcp-fetch-a2l = "pyxcp.scripts.xcp_fetch_a2l:main" +xcp-info = "pyxcp.scripts.xcp_info:main" + +[tool.pytest] +addopts = "--verbose --tb=short --junitxml=result.xml -o junit_family=xunit2" +testpaths = "pyxcp/tests" + +[tool.isort] +profile = "black" +force_single_line = false +lines_after_imports = 2 + +[tool.mypy] +strict = false +warn_unreachable = true +pretty = true +show_column_numbers = true +show_error_context = true + +[tool.flake8] +ignore = ["D203", "E203", "E266", "E501", "W503", "F403", "F401", "BLK100"] +exclude = ''' +/( + \.git + | __pycache__ + | __pypackages__ + | \.mypy_cache + | \.tox + | \.venv + | \.eggs + | _build + | build + | docs + | dist + | experimental +)/ +''' +max-complexity = 10 +count = true +statistics = true +show-source = true +max-line-length = 132 +select = ["B","C","E","F","W","T4","B9"] + +[tool.ruff] +line-length = 132 + +[tool.black] +line-length=132 +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.mypy_cache + | \.tox + | \.venv + | _build + | build + | docs + | experimental + | __pycache__ + | __pypackages__ + | dist +)/ +''' + +[tool.cibuildwheel] +build-verbosity = 3 +#test-command = "pytest {package}/tests" +#test-command = "pytest -svv pyxcp/tests" +build = "cp3{7,8,9,10,11,12}-*" +skip = ["*-manylinux_i686", "*-musllinux_x86_64", "*-musllinux_i686"] # Skip Linux 32bit and MUSL builds. +build-frontend = "build" diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 67f82d7..0f95e0a 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -1,1064 +1,1064 @@ -#!/usr/bin/env python -import io -import json -import sys -import typing -from pathlib import Path - -import can -import toml -from rich.logging import RichHandler -from rich.prompt import Confirm -from traitlets import ( - Any, - Bool, - Callable, - Dict, - Enum, - Float, - Integer, - List, - TraitError, - Unicode, - Union, -) -from traitlets.config import Application, Instance, SingletonConfigurable - -from pyxcp.config import legacy - - -class CanBase: - has_fd = False - has_bitrate = True - has_data_bitrate = False - has_poll_interval = False - has_receive_own_messages = False - has_timing = False - - OPTIONAL_BASE_PARAMS = ( - "has_fd", - "has_bitrate", - "has_data_bitrate", - "has_poll_interval", - "has_receive_own_messages", - "has_timing", - ) - - CAN_PARAM_MAP = { - "sjw_abr": None, - "tseg1_abr": None, - "tseg2_abr": None, - "sjw_dbr": None, - "tseg1_dbr": None, - "tseg2_dbr": None, - } - - -class CanAlystii(SingletonConfigurable, CanBase): - """CANalyst-II is a USB to CAN Analyzer device produced by Chuangxin Technology.""" - - interface_name = "canalystii" - - has_timing = True - - device = Integer(default_value=None, allow_none=True, help="""Optional USB device number.""").tag(config=True) - rx_queue_size = Integer( - default_value=None, - allow_none=True, - help="""If set, software received message queue can only grow to this many -messages (for all channels) before older messages are dropped """, - ).tag(config=True) - - -class CanTact(SingletonConfigurable, CanBase): - """Interface for CANtact devices from Linklayer Labs""" - - interface_name = "cantact" - - has_poll_interval = True - has_timing = True - - monitor = Bool(default_value=False, allow_none=True, help="""If true, operate in listen-only monitoring mode""").tag( - config=True - ) - - -class Etas(SingletonConfigurable, CanBase): - """ETAS""" - - interface_name = "etas" - - has_fd = True - has_data_bitrate = True - has_receive_own_messages = True - - -class Gs_Usb(SingletonConfigurable, CanBase): - """Geschwister Schneider USB/CAN devices and candleLight USB CAN interfaces.""" - - interface_name = "gs_usb" - - index = Integer( - default_value=None, - allow_none=True, - help="""device number if using automatic scan, starting from 0. -If specified, bus/address shall not be provided.""", - ).tag(config=True) - bus = Integer(default_value=None, allow_none=True, help="""number of the bus that the device is connected to""").tag( - config=True - ) - address = Integer(default_value=None, allow_none=True, help="""address of the device on the bus it is connected to""").tag( - config=True - ) - - -class Neovi(SingletonConfigurable, CanBase): - """Intrepid Control Systems (ICS) neoVI interfaces.""" - - interface_name = "neovi" - - has_fd = True - has_data_bitrate = True - has_receive_own_messages = True - - use_system_timestamp = Bool( - default_value=None, allow_none=True, help="Use system timestamp for can messages instead of the hardware timestamp" - ).tag(config=True) - serial = Unicode( - default_value=None, allow_none=True, help="Serial to connect (optional, will use the first found if not supplied)" - ).tag(config=True) - override_library_name = Unicode( - default_value=None, allow_none=True, help="Absolute path or relative path to the library including filename." - ).tag(config=True) - - -class IsCan(SingletonConfigurable, CanBase): - """Interface for isCAN from Thorsis Technologies GmbH, former ifak system GmbH.""" - - interface_name = "iscan" - - has_poll_interval = True - - -class Ixxat(SingletonConfigurable, CanBase): - """IXXAT Virtual Communication Interface""" - - interface_name = "ixxat" - - has_fd = True - has_data_bitrate = True - has_receive_own_messages = True - - unique_hardware_id = Integer( - default_value=None, - allow_none=True, - help="""UniqueHardwareId to connect (optional, will use the first found if not supplied)""", - ).tag(config=True) - extended = Bool(default_value=None, allow_none=True, help="""Enables the capability to use extended IDs.""").tag(config=True) - rx_fifo_size = Integer(default_value=None, allow_none=True, help="""Receive fifo size""").tag(config=True) - tx_fifo_size = Integer(default_value=None, allow_none=True, help="""Transmit fifo size""").tag(config=True) - ssp_dbr = Integer( - default_value=None, - allow_none=True, - help="Secondary sample point (data). Only takes effect with fd and bitrate switch enabled.", - ).tag(config=True) - - CAN_PARAM_MAP = { - "sjw_abr": "sjw_abr", - "tseg1_abr": "tseg1_abr", - "tseg2_abr": "tseg2_abr", - "sjw_dbr": "sjw_dbr", - "tseg1_dbr": "tseg1_dbr", - "tseg2_dbr": "tseg2_dbr", - } - - -class Kvaser(SingletonConfigurable, CanBase): - """Kvaser's CANLib""" - - interface_name = "kvaser" - - has_fd = True - has_data_bitrate = True - has_receive_own_messages = True - - CAN_PARAM_MAP = { - "sjw_abr": "sjw", - "tseg1_abr": "tseg1", - "tseg2_abr": "tseg2", - } - - accept_virtual = Bool(default_value=None, allow_none=True, help="If virtual channels should be accepted.").tag(config=True) - no_samp = Enum( - [1, 3], - default_value=None, - allow_none=True, - help="""Either 1 or 3. Some CAN controllers can also sample each bit three times. -In this case, the bit will be sampled three quanta in a row, -with the last sample being taken in the edge between TSEG1 and TSEG2. -Three samples should only be used for relatively slow baudrates""", - ).tag(config=True) - driver_mode = Bool(default_value=None, allow_none=True, help="Silent or normal.").tag(config=True) - single_handle = Bool( - default_value=None, - allow_none=True, - help="""Use one Kvaser CANLIB bus handle for both reading and writing. -This can be set if reading and/or writing is done from one thread. """, - ).tag(config=True) - - -class NeouSys(SingletonConfigurable, CanBase): - """Neousys CAN Interface""" - - interface_name = "neousys" - - device = Integer(default_value=None, allow_none=True, help="Device number").tag(config=True) - - -class NiCan(SingletonConfigurable, CanBase): - """National Instruments NI-CAN""" - - interface_name = "nican" - - log_errors = Bool( - default_value=None, - allow_none=True, - help="""If True, communication errors will appear as CAN messages with -``is_error_frame`` set to True and ``arbitration_id`` will identify -the error. """, - ).tag(config=True) - - -class NixNet(SingletonConfigurable, CanBase): - """National Instruments NI-XNET""" - - interface_name = "nixnet" - - has_poll_interval = True - has_receive_own_messages = True - has_timing = True - has_fd = True - - CAN_PARAM_MAP = { - "data_bitrate": "fd_bitrate", - } - - can_termination = Bool(default_value=None, allow_none=True, help="Enable bus termination.") - - -class PCan(SingletonConfigurable, CanBase): - """PCAN Basic API""" - - interface_name = "pcan" - - has_fd = True - has_timing = True - - CAN_PARAM_MAP = { - "sjw_abr": "nom_sjw", - "tseg1_abr": "nom_tseg1", - "tseg2_abr": "nom_tseg2", - "sjw_dbr": "data_sjw", - "tseg1_dbr": "data_tseg1", - "tseg2_dbr": "data_tseg2", - } - - device_id = Integer( - default_value=None, - allow_none=True, - help="""Select the PCAN interface based on its ID. The device ID is a 8/32bit -value that can be configured for each PCAN device. If you set the -device_id parameter, it takes precedence over the channel parameter. -The constructor searches all connected interfaces and initializes the -first one that matches the parameter value. If no device is found, -an exception is raised.""", - ).tag(config=True) - state = Instance(can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) - - f_clock = Enum( - [20000000, 24000000, 30000000, 40000000, 60000000, 80000000], - default_value=None, - allow_none=True, - help="""Ignored if not using CAN-FD. -Pass either f_clock or f_clock_mhz.""", - ).tag(config=True) - f_clock_mhz = Enum( - [20, 24, 30, 40, 60, 80], - default_value=None, - allow_none=True, - help="""Ignored if not using CAN-FD. -Pass either f_clock or f_clock_mhz. """, - ).tag(config=True) - - nom_brp = Integer( - min=1, - max=1024, - default_value=None, - allow_none=True, - help="""Clock prescaler for nominal time quantum. -Ignored if not using CAN-FD.""", - ).tag(config=True) - data_brp = Integer( - min=1, - max=1024, - default_value=None, - allow_none=True, - help="""Clock prescaler for fast data time quantum. -Ignored if not using CAN-FD.""", - ).tag(config=True) - - auto_reset = Bool( - default_value=None, - allow_none=True, - help="""Enable automatic recovery in bus off scenario. -Resetting the driver takes ~500ms during which -it will not be responsive.""", - ).tag(config=True) - - -class Robotell(SingletonConfigurable, CanBase): - """Interface for Chinese Robotell compatible interfaces""" - - interface_name = "robotell" - - ttyBaudrate = Integer( - default_value=None, - allow_none=True, - help="""baudrate of underlying serial or usb device -(Ignored if set via the `channel` parameter, e.g. COM7@11500).""", - ).tag(config=True) - rtscts = Bool(default_value=None, allow_none=True, help="turn hardware handshake (RTS/CTS) on and off.").tag(config=True) - - -class SeeedStudio(SingletonConfigurable, CanBase): - """Seeed USB-Can analyzer interface.""" - - interface_name = "seeedstudio" - - timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) - baudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) - frame_type = Enum(["STD", "EXT"], default_value=None, allow_none=True, help="To select standard or extended messages.").tag( - config=True - ) - operation_mode = Enum( - ["normal", "loopback", "silent", "loopback_and_silent"], default_value=None, allow_none=True, help=""" """ - ).tag(config=True) - - -class Serial(SingletonConfigurable, CanBase): - """A text based interface.""" - - interface_name = "serial" - - has_bitrate = False - - rtscts = Bool(default_value=None, allow_none=True, help="turn hardware handshake (RTS/CTS) on and off.").tag(config=True) - timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) - baudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) - - -class SlCan(SingletonConfigurable, CanBase): - """CAN over Serial / SLCAN.""" - - interface_name = "slcan" - - has_poll_interval = True - - ttyBaudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) - rtscts = Bool(default_value=None, allow_none=True, help="turn hardware handshake (RTS/CTS) on and off.").tag(config=True) - timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) - btr = Integer(default_value=None, allow_none=True, help="BTR register value to set custom can speed.").tag(config=True) - sleep_after_open = Float( - default_value=None, allow_none=True, help="Time to wait in seconds after opening serial connection." - ).tag(config=True) - - -class SocketCan(SingletonConfigurable, CanBase): - """Linux SocketCAN.""" - - interface_name = "socketcan" - - has_fd = True - has_bitrate = False - has_receive_own_messages = True - - local_loopback = Bool( - default_value=None, - allow_none=True, - help="""If local loopback should be enabled on this bus. -Please note that local loopback does not mean that messages sent -on a socket will be readable on the same socket, they will only -be readable on other open sockets on the same machine. More info -can be read on the socketcan documentation: -See https://www.kernel.org/doc/html/latest/networking/can.html#socketcan-local-loopback1""", - ).tag(config=True) - - -class SocketCanD(SingletonConfigurable, CanBase): - """Network-to-CAN bridge as a Linux damon.""" - - interface_name = "socketcand" - - has_bitrate = False - - host = Unicode(default_value=None, allow_none=True, help=""" """).tag(config=True) - port = Integer(default_value=None, allow_none=True, help=""" """).tag(config=True) - - -class Systec(SingletonConfigurable, CanBase): - """SYSTEC interface""" - - interface_name = "systec" - - has_receive_own_messages = True - - state = Instance(can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) - device_number = Integer(min=0, max=254, default_value=None, allow_none=True, help="The device number of the USB-CAN.").tag( - config=True - ) - rx_buffer_entries = Integer( - default_value=None, allow_none=True, help="The maximum number of entries in the receive buffer." - ).tag(config=True) - tx_buffer_entries = Integer( - default_value=None, allow_none=True, help="The maximum number of entries in the transmit buffer." - ).tag(config=True) - - -class Udp_Multicast(SingletonConfigurable, CanBase): - """A virtual interface for CAN communications between multiple processes using UDP over Multicast IP.""" - - interface_name = "udp_multicast" - - has_fd = True - has_bitrate = False - has_receive_own_messages = True - - port = Integer(default_value=None, allow_none=True, help="The IP port to read from and write to.").tag(config=True) - hop_limit = Integer(default_value=None, allow_none=True, help="The hop limit in IPv6 or in IPv4 the time to live (TTL).").tag( - config=True - ) - - -class Usb2Can(SingletonConfigurable, CanBase): - """Interface to a USB2CAN Bus.""" - - interface_name = "usb2can" - - flags = Integer( - default_value=None, allow_none=True, help="Flags to directly pass to open function of the usb2can abstraction layer." - ).tag(config=True) - dll = Unicode(default_value=None, allow_none=True, help="Path to the DLL with the CANAL API to load.").tag(config=True) - serial = Unicode(default_value=None, allow_none=True, help="Alias for `channel` that is provided for legacy reasons.").tag( - config=True - ) - - -class Vector(SingletonConfigurable, CanBase): - """Vector Informatik CAN interfaces.""" - - interface_name = "vector" - - has_fd = True - has_data_bitrate = True - has_poll_interval = True - has_receive_own_messages = True - has_timing = True - - CAN_PARAM_MAP = { - "sjw_abr": "sjw_abr", - "tseg1_abr": "tseg1_abr", - "tseg2_abr": "tseg2_abr", - "sjw_dbr": "sjw_dbr", - "tseg1_dbr": "tseg1_dbr", - "tseg2_dbr": "tseg2_dbr", - } - - serial = Integer( - default_value=None, - allow_none=True, - help="""Serial number of the hardware to be used. -If set, the channel parameter refers to the channels ONLY on the specified hardware. -If set, the `app_name` does not have to be previously defined in -*Vector Hardware Config*.""", - ).tag(config=True) - rx_queue_size = Integer( - min=16, max=32768, default_value=None, allow_none=True, help="Number of messages in receive queue (power of 2)." - ).tag(config=True) - app_name = Unicode(default_value=None, allow_none=True, help="Name of application in *Vector Hardware Config*.").tag( - config=True - ) - - -class Virtual(SingletonConfigurable, CanBase): - """ """ - - interface_name = "virtual" - - has_bitrate = False - has_receive_own_messages = True - - rx_queue_size = Integer( - default_value=None, - allow_none=True, - help="""The size of the reception queue. The reception -queue stores messages until they are read. If the queue reaches -its capacity, it will start dropping the oldest messages to make -room for new ones. If set to 0, the queue has an infinite capacity. -Be aware that this can cause memory leaks if messages are read -with a lower frequency than they arrive on the bus. """, - ).tag(config=True) - preserve_timestamps = Bool( - default_value=None, - allow_none=True, - help="""If set to True, messages transmitted via -will keep the timestamp set in the -:class:`~can.Message` instance. Otherwise, the timestamp value -will be replaced with the current system time.""", - ).tag(config=True) - - -CAN_INTERFACE_MAP = { - "canalystii": CanAlystii, - "cantact": CanTact, - "etas": Etas, - "gs_usb": Gs_Usb, - "iscan": IsCan, - "ixxat": Ixxat, - "kvaser": Kvaser, - "neousys": NeouSys, - "neovi": Neovi, - "nican": NiCan, - "nixnet": NixNet, - "pcan": PCan, - "robotell": Robotell, - "seeedstudio": SeeedStudio, - "serial": Serial, - "slcan": SlCan, - "socketcan": SocketCan, - "socketcand": SocketCanD, - "systec": Systec, - "udp_multicast": Udp_Multicast, - "usb2can": Usb2Can, - "vector": Vector, - "virtual": Virtual, -} - - -class Can(SingletonConfigurable): - VALID_INTERFACES = can.interfaces.VALID_INTERFACES - - interface = Enum(VALID_INTERFACES, default_value=None, allow_none=True, help="CAN interface supported by python-can").tag( - config=True - ) - channel = Any( - default_value=None, allow_none=True, help="Channel identification. Expected type and value is backend dependent." - ).tag(config=True) - max_dlc_required = Bool(False, help="Master to slave frames always to have DLC = MAX_DLC = 8").tag(config=True) - # max_can_fd_dlc = Integer(64, help="").tag(config=True) - padding_value = Integer(0, help="Fill value, if max_dlc_required == True and DLC < MAX_DLC").tag(config=True) - use_default_listener = Bool(True, help="").tag(config=True) - can_id_master = Integer(allow_none=False, help="CAN-ID master -> slave (Bit31= 1: extended identifier)").tag( - config=True - ) # CMD and STIM packets - can_id_slave = Integer(allow_none=True, help="CAN-ID slave -> master (Bit31= 1: extended identifier)").tag( - config=True - ) # RES, ERR, EV, SERV and DAQ packets. - can_id_broadcast = Integer( - default_value=None, allow_none=True, help="Auto detection CAN-ID (Bit31= 1: extended identifier)" - ).tag(config=True) - daq_identifier = List(trait=Integer(), default_value=[], allow_none=True, help="One CAN identifier per DAQ-list.").tag( - config=True - ) - bitrate = Integer(250000, help="CAN bitrate in bits/s (arbitration phase, if CAN FD).").tag(config=True) - receive_own_messages = Bool(False, help="Enable self-reception of sent messages.").tag(config=True) - poll_interval = Float(default_value=None, allow_none=True, help="Poll interval in seconds when reading messages.").tag( - config=True - ) - fd = Bool(False, help="If CAN-FD frames should be supported.").tag(config=True) - data_bitrate = Integer(default_value=None, allow_none=True, help="Which bitrate to use for data phase in CAN FD.").tag( - config=True - ) - sjw_abr = Integer( - default_value=None, allow_none=True, help="Bus timing value sample jump width (arbitration, SJW if CAN classic)." - ).tag(config=True) - tseg1_abr = Integer( - default_value=None, allow_none=True, help="Bus timing value tseg1 (arbitration, TSEG1 if CAN classic)." - ).tag(config=True) - tseg2_abr = Integer( - default_value=None, allow_none=True, help="Bus timing value tseg2 (arbitration, TSEG2, if CAN classic)" - ).tag(config=True) - sjw_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value sample jump width (data).").tag(config=True) - tseg1_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg1 (data).").tag(config=True) - tseg2_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg2 (data).").tag(config=True) - timing = Union( - [Instance(can.BitTiming)], # , Instance(can.BitTimingFd) - default_value=None, - allow_none=True, - help="""Custom bit timing settings. -(.s https://github.com/hardbyte/python-can/blob/develop/can/bit_timing.py) -If this parameter is provided, it takes precedence over all other -timing-related parameters. - """, - ).tag(config=True) - - classes = List( - [ - CanAlystii, - CanTact, - Etas, - Gs_Usb, - Neovi, - IsCan, - Ixxat, - Kvaser, - NeouSys, - NiCan, - NixNet, - PCan, - Robotell, - SeeedStudio, - Serial, - SlCan, - SocketCan, - SocketCanD, - Systec, - Udp_Multicast, - Usb2Can, - Vector, - Virtual, - ] - ) - - def __init__(self, **kws): - super().__init__(**kws) - - if self.parent.layer == "CAN": - if self.interface is None or self.interface not in self.VALID_INTERFACES: - raise TraitError( - f"CAN interface must be one of {sorted(list(self.VALID_INTERFACES))} not the" - " {type(self.interface).__name__} {self.interface}." - ) - self.canalystii = CanAlystii.instance(config=self.config, parent=self) - self.cantact = CanTact.instance(config=self.config, parent=self) - self.etas = Etas.instance(config=self.config, parent=self) - self.gs_usb = Gs_Usb.instance(config=self.config, parent=self) - self.neovi = Neovi.instance(config=self.config, parent=self) - self.iscan = IsCan.instance(config=self.config, parent=self) - self.ixxat = Ixxat.instance(config=self.config, parent=self) - self.kvaser = Kvaser.instance(config=self.config, parent=self) - self.neousys = NeouSys.instance(config=self.config, parent=self) - self.nican = NiCan.instance(config=self.config, parent=self) - self.nixnet = NixNet.instance(config=self.config, parent=self) - self.pcan = PCan.instance(config=self.config, parent=self) - self.robotell = Robotell.instance(config=self.config, parent=self) - self.seeedstudio = SeeedStudio.instance(config=self.config, parent=self) - self.serial = Serial.instance(config=self.config, parent=self) - self.slcan = SlCan.instance(config=self.config, parent=self) - self.socketcan = SocketCan.instance(config=self.config, parent=self) - self.socketcand = SocketCanD.instance(config=self.config, parent=self) - self.systec = Systec.instance(config=self.config, parent=self) - self.udp_multicast = Udp_Multicast.instance(config=self.config, parent=self) - self.usb2can = Usb2Can.instance(config=self.config, parent=self) - self.vector = Vector.instance(config=self.config, parent=self) - self.virtual = Virtual.instance(config=self.config, parent=self) - - -class Eth(SingletonConfigurable): - """Ethernet.""" - - host = Unicode("localhost", help="Hostname or IP address of XCP slave.").tag(config=True) - port = Integer(5555, help="TCP/UDP port to connect.").tag(config=True) - protocol = Enum(["TCP", "UDP"], default_value="UDP", help="").tag(config=True) - ipv6 = Bool(False, help="Use IPv6 if `True` else IPv4.").tag(config=True) - tcp_nodelay = Bool(False, help="*** Expert option *** -- Disable Nagle's algorithm if `True`.").tag(config=True) - bind_to_address = Unicode(default_value=None, allow_none=True, help="Bind to specific local address.").tag(config=True) - bind_to_port = Integer(default_value=None, allow_none=True, help="Bind to specific local port.").tag(config=True) - - -class SxI(SingletonConfigurable): - """SCI and SPI connections.""" - - port = Unicode("COM1", help="Name of communication interface.").tag(config=True) - bitrate = Integer(38400, help="Connection bitrate").tag(config=True) - bytesize = Enum([5, 6, 7, 8], default_value=8, help="Size of byte.").tag(config=True) - parity = Enum(["N", "E", "O", "M", "S"], default_value="N", help="Paritybit calculation.").tag(config=True) - stopbits = Enum([1, 1.5, 2], default_value=1, help="Number of stopbits.").tag(config=True) - mode = Enum( - [ - "ASYNCH_FULL_DUPLEX_MODE", - "SYNCH_FULL_DUPLEX_MODE_BYTE", - "SYNCH_FULL_DUPLEX_MODE_WORD", - "SYNCH_FULL_DUPLEX_MODE_DWORD", - "SYNCH_MASTER_SLAVE_MODE_BYTE", - "SYNCH_MASTER_SLAVE_MODE_WORD", - "SYNCH_MASTER_SLAVE_MODE_DWORD", - ], - default_value="ASYNCH_FULL_DUPLEX_MODE", - help="Asynchronous (SCI) or synchronous (SPI) communication mode.", - ).tag(config=True) - header_format = Enum( - [ - "HEADER_LEN_BYTE", - "HEADER_LEN_CTR_BYTE", - "HEADER_LEN_FILL_BYTE", - "HEADER_LEN_WORD", - "HEADER_LEN_CTR_WORD", - "HEADER_LEN_FILL_WORD", - ], - default_value="HEADER_LEN_CTR_WORD", - help="""XCPonSxI header format. -Number of bytes: - - LEN CTR FILL -______________________________________________________________ -HEADER_LEN_BYTE | 1 X X -HEADER_LEN_CTR_BYTE | 1 1 X -HEADER_LEN_FILL_BYTE | 1 X 1 -HEADER_LEN_WORD | 2 X X -HEADER_LEN_CTR_WORD | 2 2 X -HEADER_LEN_FILL_WORD | 2 X 2 -""", - ).tag(config=True) - tail_format = Enum( - ["NO_CHECKSUM", "CHECKSUM_BYTE", "CHECKSUM_WORD"], default_value="NO_CHECKSUM", help="XCPonSxI tail format." - ).tag(config=True) - framing = Bool(False, help="Enable SCI framing mechanism (ESC chars).").tag(config=True) - esc_sync = Integer(0x01, min=0, max=255, help="SCI framing protocol character SYNC.").tag(config=True) - esc_esc = Integer(0x00, min=0, max=255, help="SCI framing protocol character ESC.").tag(config=True) - - -class Usb(SingletonConfigurable): - """Universal Serial Bus connections.""" - - serial_number = Unicode("", help="Device serial number.").tag(config=True) - configuration_number = Integer(1, help="USB configuration number.").tag(config=True) - interface_number = Integer(2, help="USB interface number.").tag(config=True) - vendor_id = Integer(0, help="USB vendor ID.").tag(config=True) - product_id = Integer(0, help="USB product ID.").tag(config=True) - library = Unicode("", help="Absolute path to USB shared library.").tag(config=True) - header_format = Enum( - [ - "HEADER_LEN_BYTE", - "HEADER_LEN_CTR_BYTE", - "HEADER_LEN_FILL_BYTE", - "HEADER_LEN_WORD", - "HEADER_LEN_CTR_WORD", - "HEADER_LEN_FILL_WORD", - ], - default_value="HEADER_LEN_CTR_WORD", - help="", - ).tag(config=True) - in_ep_number = Integer(1, help="Ingoing USB reply endpoint number (IN-EP for RES/ERR, DAQ, and EV/SERV).").tag(config=True) - in_ep_transfer_type = Enum( - ["BULK_TRANSFER", "INTERRUPT_TRANSFER"], default_value="BULK_TRANSFER", help="Ingoing: Supported USB transfer types." - ).tag(config=True) - in_ep_max_packet_size = Integer(512, help="Ingoing: Maximum packet size of endpoint in bytes.").tag(config=True) - in_ep_polling_interval = Integer(0, help="Ingoing: Polling interval of endpoint.").tag(config=True) - in_ep_message_packing = Enum( - ["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], - default_value="MESSAGE_PACKING_SINGLE", - help="Ingoing: Packing of XCP Messages.", - ).tag(config=True) - in_ep_alignment = Enum( - ["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], - default_value="ALIGNMENT_8_BIT", - help="Ingoing: Alignment border.", - ).tag(config=True) - in_ep_recommended_host_bufsize = Integer(0, help="Ingoing: Recommended host buffer size.").tag(config=True) - out_ep_number = Integer(0, help="Outgoing USB command endpoint number (OUT-EP for CMD and STIM).").tag(config=True) - out_ep_transfer_type = Enum( - ["BULK_TRANSFER", "INTERRUPT_TRANSFER"], default_value="BULK_TRANSFER", help="Outgoing: Supported USB transfer types." - ).tag(config=True) - out_ep_max_packet_size = Integer(512, help="Outgoing: Maximum packet size of endpoint in bytes.").tag(config=True) - out_ep_polling_interval = Integer(0, help="Outgoing: Polling interval of endpoint.").tag(config=True) - out_ep_message_packing = Enum( - ["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], - default_value="MESSAGE_PACKING_SINGLE", - help="Outgoing: Packing of XCP Messages.", - ).tag(config=True) - out_ep_alignment = Enum( - ["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], - default_value="ALIGNMENT_8_BIT", - help="Outgoing: Alignment border.", - ).tag(config=True) - out_ep_recommended_host_bufsize = Integer(0, help="Outgoing: Recommended host buffer size.").tag(config=True) - - -class Transport(SingletonConfigurable): - """ """ - - classes = List([Can, Eth, SxI, Usb]) - - layer = Enum( - ["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=True, help="Choose one of the supported XCP transport layers." - ).tag(config=True) - create_daq_timestamps = Bool(False, help="Record time of frame reception or set timestamp to 0.").tag(config=True) - timeout = Float( - 2.0, - help="""raise `XcpTimeoutError` after `timeout` seconds -if there is no response to a command.""", - ).tag(config=True) - alignment = Enum([1, 2, 4, 8], default_value=1).tag(config=True) - - can = Instance(Can).tag(config=True) - eth = Instance(Eth).tag(config=True) - sxi = Instance(SxI).tag(config=True) - usb = Instance(Usb).tag(config=True) - - def __init__(self, **kws): - super().__init__(**kws) - self.can = Can.instance(config=self.config, parent=self) - self.eth = Eth.instance(config=self.config, parent=self) - self.sxi = SxI.instance(config=self.config, parent=self) - self.usb = Usb.instance(config=self.config, parent=self) - - -class General(SingletonConfigurable): - """ """ - - loglevel = Unicode("INFO", help="Set the log level by value or name.").tag(config=True) - disable_error_handling = Bool(False, help="Disable XCP error-handler for performance reasons.").tag(config=True) - disconnect_response_optional = Bool(False, help="Ignore missing response on DISCONNECT request.").tag(config=True) - seed_n_key_dll = Unicode("", allow_none=False, help="Dynamic library used for slave resource unlocking.").tag(config=True) - seed_n_key_dll_same_bit_width = Bool(False, help="").tag(config=True) - seed_n_key_function = Callable( - default_value=None, - allow_none=True, - help="""Python function used for slave resource unlocking. -Could be used if seed-and-key algorithm is known instead of `seed_n_key_dll`.""", - ).tag(config=True) - stim_support = Bool(False, help="").tag(config=True) - - -class ProfileCreate(Application): - description = "\nCreate a new profile" - - dest_file = Unicode(default_value=None, allow_none=True, help="destination file name").tag(config=True) - aliases = Dict( # type:ignore[assignment] - dict( - d="ProfileCreate.dest_file", - o="ProfileCreate.dest_file", - ) - ) - - def start(self): - self.parent.parent.generate_config_file(sys.stdout, {}) - - -class ProfileConvert(Application): - description = "\nConvert legacy configuration file (.json/.toml) to new Python based format." - - config_file = Unicode(help="Name of legacy config file (.json/.toml).", default_value=None, allow_none=False).tag( - config=True - ) # default_value="pyxcp_conf.py", - - dest_file = Unicode(default_value=None, allow_none=True, help="destination file name").tag(config=True) - - aliases = Dict( # type:ignore[assignment] - dict( - c="ProfileConvert.config_file", - d="ProfileConvert.dest_file", - o="ProfileConvert.dest_file", - ) - ) - - def start(self): - pyxcp = self.parent.parent - pyxcp._read_configuration(self.config_file, emit_warning=False) - if self.dest_file: - dest = Path(self.dest_file) - if dest.exists(): - if not Confirm.ask(f"Destination file [green]{dest.name!r}[/green] already exists. do you want to overwrite it?"): - print("Aborting...") - self.exit(1) - with dest.open("w", encoding="latin1") as out_file: - pyxcp.generate_config_file(out_file) - else: - pyxcp.generate_config_file(sys.stdout) - - -class ProfileApp(Application): - subcommands = Dict( - dict( - create=(ProfileCreate, ProfileCreate.description.splitlines()[0]), - convert=(ProfileConvert, ProfileConvert.description.splitlines()[0]), - ) - ) - - def start(self): - if self.subapp is None: - print(f"No subcommand specified. Must specify one of: {self.subcommands.keys()}") - print() - self.print_description() - self.print_subcommands() - self.exit(1) - else: - self.subapp.start() - - -class PyXCP(Application): - config_file = Unicode(default_value="pyxcp_conf.py", help="base name of config file").tag(config=True) - - classes = List([General, Transport]) - - subcommands = dict( - profile=( - ProfileApp, - """ - Profile stuff - """.strip(), - ) - ) - - def start(self): - if self.subapp: - self.subapp.start() - exit(2) - else: - self._read_configuration(self.config_file) - self._setup_logger() - - def _setup_logger(self): - from pyxcp.types import Command - - # Remove any handlers installed by `traitlets`. - for hdl in self.log.handlers: - self.log.removeHandler(hdl) - - # formatter = logging.Formatter(fmt=self.log_format, datefmt=self.log_datefmt) - - keywords = list(Command.__members__.keys()) + ["ARGS", "KWS"] # Syntax highlight XCP commands and other stuff. - rich_handler = RichHandler( - rich_tracebacks=True, - tracebacks_show_locals=True, - log_time_format=self.log_datefmt, - level=self.log_level, - keywords=keywords, - ) - # rich_handler.setFormatter(formatter) - self.log.addHandler(rich_handler) - - def initialize(self, argv=None): - from pyxcp import __version__ as pyxcp_version - - PyXCP.version = pyxcp_version - PyXCP.name = Path(sys.argv[0]).name - self.parse_command_line(argv[1:]) - self.log.debug(f"pyxcp version: {self.version}") - - def _read_configuration(self, file_name: str, emit_warning: bool = True) -> None: - self.read_configuration_file(file_name, emit_warning) - self.general = General.instance(config=self.config, parent=self) - self.transport = Transport.instance(parent=self) - - def read_configuration_file(self, file_name: str, emit_warning: bool = True): - self.legacy_config: bool = False - - pth = Path(file_name) - if not pth.exists(): - raise FileNotFoundError(f"Configuration file {file_name!r} does not exist.") - suffix = pth.suffix.lower() - if suffix == ".py": - self.load_config_file(pth) - else: - self.legacy_config = True - if suffix == ".json": - reader = json - elif suffix == ".toml": - reader = toml - else: - raise ValueError(f"Unknown file type for config: {suffix}") - with pth.open("r") as f: - if emit_warning: - self.log.warning(f"Legacy configuration file format ({suffix}), please use Python based configuration.") - cfg = reader.loads(f.read()) - if cfg: - cfg = legacy.convert_config(cfg, self.log) - self.config = cfg - return cfg - - flags = Dict( # type:ignore[assignment] - dict( - debug=({"PyXCP": {"log_level": 10}}, "Set loglevel to DEBUG"), - ) - ) - - aliases = Dict( # type:ignore[assignment] - dict( - c="PyXCP.config_file", - log_level="PyXCP.log_level", - l="PyXCP.log_level", - ) - ) - - def _iterate_config_class(self, klass, class_names: typing.List[str], config, out_file: io.IOBase = sys.stdout) -> None: - sub_classes = [] - class_path = ".".join(class_names) - print( - f"""\n# ------------------------------------------------------------------------------ -# {class_path} configuration -# ------------------------------------------------------------------------------""", - end="\n\n", - file=out_file, - ) - if hasattr(klass, "classes"): - kkk = klass.classes - if hasattr(kkk, "default"): - if class_names[-1] not in ("PyXCP"): - sub_classes.extend(kkk.default()) - for name, tr in klass.class_own_traits().items(): - md = tr.metadata - if md.get("config"): - help = md.get("help", "").lstrip() - commented_lines = "\n".join([f"# {line}" for line in help.split("\n")]) - print(f"#{commented_lines}", file=out_file) - value = tr.default() - if isinstance(tr, Instance) and tr.__class__.__name__ not in ("Dict", "List"): - continue - if isinstance(tr, Enum): - print(f"# Choices: {tr.info()}", file=out_file) - else: - print(f"# Type: {tr.info()}", file=out_file) - print(f"# Default: {value!r}", file=out_file) - if name in config: - cfg_value = config[name] - print(f"c.{class_path!s}.{name!s} = {cfg_value!r}", end="\n\n", file=out_file) - else: - print(f"# c.{class_path!s}.{name!s} = {value!r}", end="\n\n", file=out_file) - if class_names is None: - class_names = [] - for sub_klass in sub_classes: - self._iterate_config_class( - sub_klass, class_names + [sub_klass.__name__], config=config.get(sub_klass.__name__, {}), out_file=out_file - ) - - def generate_config_file(self, file_like: io.IOBase, config=None) -> None: - print("#", file=file_like) - print("# Configuration file for pyXCP.", file=file_like) - print("#", file=file_like) - print("c = get_config() # noqa", end="\n\n", file=file_like) - - for klass in self._classes_with_config_traits(): - self._iterate_config_class( - klass, [klass.__name__], config=self.config.get(klass.__name__, {}) if config is None else {}, out_file=file_like - ) - - -application: typing.Optional[PyXCP] = None - - -def create_application() -> PyXCP: - global application - if application is not None: - return application - application = PyXCP() - application.initialize(sys.argv) - application.start() - return application - - -def get_application() -> PyXCP: - global application - if application is None: - application = create_application() - return application +#!/usr/bin/env python +import io +import json +import sys +import typing +from pathlib import Path + +import can +import toml +from rich.logging import RichHandler +from rich.prompt import Confirm +from traitlets import ( + Any, + Bool, + Callable, + Dict, + Enum, + Float, + Integer, + List, + TraitError, + Unicode, + Union, +) +from traitlets.config import Application, Instance, SingletonConfigurable + +from pyxcp.config import legacy + + +class CanBase: + has_fd = False + has_bitrate = True + has_data_bitrate = False + has_poll_interval = False + has_receive_own_messages = False + has_timing = False + + OPTIONAL_BASE_PARAMS = ( + "has_fd", + "has_bitrate", + "has_data_bitrate", + "has_poll_interval", + "has_receive_own_messages", + "has_timing", + ) + + CAN_PARAM_MAP = { + "sjw_abr": None, + "tseg1_abr": None, + "tseg2_abr": None, + "sjw_dbr": None, + "tseg1_dbr": None, + "tseg2_dbr": None, + } + + +class CanAlystii(SingletonConfigurable, CanBase): + """CANalyst-II is a USB to CAN Analyzer device produced by Chuangxin Technology.""" + + interface_name = "canalystii" + + has_timing = True + + device = Integer(default_value=None, allow_none=True, help="""Optional USB device number.""").tag(config=True) + rx_queue_size = Integer( + default_value=None, + allow_none=True, + help="""If set, software received message queue can only grow to this many +messages (for all channels) before older messages are dropped """, + ).tag(config=True) + + +class CanTact(SingletonConfigurable, CanBase): + """Interface for CANtact devices from Linklayer Labs""" + + interface_name = "cantact" + + has_poll_interval = True + has_timing = True + + monitor = Bool(default_value=False, allow_none=True, help="""If true, operate in listen-only monitoring mode""").tag( + config=True + ) + + +class Etas(SingletonConfigurable, CanBase): + """ETAS""" + + interface_name = "etas" + + has_fd = True + has_data_bitrate = True + has_receive_own_messages = True + + +class Gs_Usb(SingletonConfigurable, CanBase): + """Geschwister Schneider USB/CAN devices and candleLight USB CAN interfaces.""" + + interface_name = "gs_usb" + + index = Integer( + default_value=None, + allow_none=True, + help="""device number if using automatic scan, starting from 0. +If specified, bus/address shall not be provided.""", + ).tag(config=True) + bus = Integer(default_value=None, allow_none=True, help="""number of the bus that the device is connected to""").tag( + config=True + ) + address = Integer(default_value=None, allow_none=True, help="""address of the device on the bus it is connected to""").tag( + config=True + ) + + +class Neovi(SingletonConfigurable, CanBase): + """Intrepid Control Systems (ICS) neoVI interfaces.""" + + interface_name = "neovi" + + has_fd = True + has_data_bitrate = True + has_receive_own_messages = True + + use_system_timestamp = Bool( + default_value=None, allow_none=True, help="Use system timestamp for can messages instead of the hardware timestamp" + ).tag(config=True) + serial = Unicode( + default_value=None, allow_none=True, help="Serial to connect (optional, will use the first found if not supplied)" + ).tag(config=True) + override_library_name = Unicode( + default_value=None, allow_none=True, help="Absolute path or relative path to the library including filename." + ).tag(config=True) + + +class IsCan(SingletonConfigurable, CanBase): + """Interface for isCAN from Thorsis Technologies GmbH, former ifak system GmbH.""" + + interface_name = "iscan" + + has_poll_interval = True + + +class Ixxat(SingletonConfigurable, CanBase): + """IXXAT Virtual Communication Interface""" + + interface_name = "ixxat" + + has_fd = True + has_data_bitrate = True + has_receive_own_messages = True + + unique_hardware_id = Integer( + default_value=None, + allow_none=True, + help="""UniqueHardwareId to connect (optional, will use the first found if not supplied)""", + ).tag(config=True) + extended = Bool(default_value=None, allow_none=True, help="""Enables the capability to use extended IDs.""").tag(config=True) + rx_fifo_size = Integer(default_value=None, allow_none=True, help="""Receive fifo size""").tag(config=True) + tx_fifo_size = Integer(default_value=None, allow_none=True, help="""Transmit fifo size""").tag(config=True) + ssp_dbr = Integer( + default_value=None, + allow_none=True, + help="Secondary sample point (data). Only takes effect with fd and bitrate switch enabled.", + ).tag(config=True) + + CAN_PARAM_MAP = { + "sjw_abr": "sjw_abr", + "tseg1_abr": "tseg1_abr", + "tseg2_abr": "tseg2_abr", + "sjw_dbr": "sjw_dbr", + "tseg1_dbr": "tseg1_dbr", + "tseg2_dbr": "tseg2_dbr", + } + + +class Kvaser(SingletonConfigurable, CanBase): + """Kvaser's CANLib""" + + interface_name = "kvaser" + + has_fd = True + has_data_bitrate = True + has_receive_own_messages = True + + CAN_PARAM_MAP = { + "sjw_abr": "sjw", + "tseg1_abr": "tseg1", + "tseg2_abr": "tseg2", + } + + accept_virtual = Bool(default_value=None, allow_none=True, help="If virtual channels should be accepted.").tag(config=True) + no_samp = Enum( + [1, 3], + default_value=None, + allow_none=True, + help="""Either 1 or 3. Some CAN controllers can also sample each bit three times. +In this case, the bit will be sampled three quanta in a row, +with the last sample being taken in the edge between TSEG1 and TSEG2. +Three samples should only be used for relatively slow baudrates""", + ).tag(config=True) + driver_mode = Bool(default_value=None, allow_none=True, help="Silent or normal.").tag(config=True) + single_handle = Bool( + default_value=None, + allow_none=True, + help="""Use one Kvaser CANLIB bus handle for both reading and writing. +This can be set if reading and/or writing is done from one thread. """, + ).tag(config=True) + + +class NeouSys(SingletonConfigurable, CanBase): + """Neousys CAN Interface""" + + interface_name = "neousys" + + device = Integer(default_value=None, allow_none=True, help="Device number").tag(config=True) + + +class NiCan(SingletonConfigurable, CanBase): + """National Instruments NI-CAN""" + + interface_name = "nican" + + log_errors = Bool( + default_value=None, + allow_none=True, + help="""If True, communication errors will appear as CAN messages with +``is_error_frame`` set to True and ``arbitration_id`` will identify +the error. """, + ).tag(config=True) + + +class NixNet(SingletonConfigurable, CanBase): + """National Instruments NI-XNET""" + + interface_name = "nixnet" + + has_poll_interval = True + has_receive_own_messages = True + has_timing = True + has_fd = True + + CAN_PARAM_MAP = { + "data_bitrate": "fd_bitrate", + } + + can_termination = Bool(default_value=None, allow_none=True, help="Enable bus termination.") + + +class PCan(SingletonConfigurable, CanBase): + """PCAN Basic API""" + + interface_name = "pcan" + + has_fd = True + has_timing = True + + CAN_PARAM_MAP = { + "sjw_abr": "nom_sjw", + "tseg1_abr": "nom_tseg1", + "tseg2_abr": "nom_tseg2", + "sjw_dbr": "data_sjw", + "tseg1_dbr": "data_tseg1", + "tseg2_dbr": "data_tseg2", + } + + device_id = Integer( + default_value=None, + allow_none=True, + help="""Select the PCAN interface based on its ID. The device ID is a 8/32bit +value that can be configured for each PCAN device. If you set the +device_id parameter, it takes precedence over the channel parameter. +The constructor searches all connected interfaces and initializes the +first one that matches the parameter value. If no device is found, +an exception is raised.""", + ).tag(config=True) + state = Instance(can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) + + f_clock = Enum( + [20000000, 24000000, 30000000, 40000000, 60000000, 80000000], + default_value=None, + allow_none=True, + help="""Ignored if not using CAN-FD. +Pass either f_clock or f_clock_mhz.""", + ).tag(config=True) + f_clock_mhz = Enum( + [20, 24, 30, 40, 60, 80], + default_value=None, + allow_none=True, + help="""Ignored if not using CAN-FD. +Pass either f_clock or f_clock_mhz. """, + ).tag(config=True) + + nom_brp = Integer( + min=1, + max=1024, + default_value=None, + allow_none=True, + help="""Clock prescaler for nominal time quantum. +Ignored if not using CAN-FD.""", + ).tag(config=True) + data_brp = Integer( + min=1, + max=1024, + default_value=None, + allow_none=True, + help="""Clock prescaler for fast data time quantum. +Ignored if not using CAN-FD.""", + ).tag(config=True) + + auto_reset = Bool( + default_value=None, + allow_none=True, + help="""Enable automatic recovery in bus off scenario. +Resetting the driver takes ~500ms during which +it will not be responsive.""", + ).tag(config=True) + + +class Robotell(SingletonConfigurable, CanBase): + """Interface for Chinese Robotell compatible interfaces""" + + interface_name = "robotell" + + ttyBaudrate = Integer( + default_value=None, + allow_none=True, + help="""baudrate of underlying serial or usb device +(Ignored if set via the `channel` parameter, e.g. COM7@11500).""", + ).tag(config=True) + rtscts = Bool(default_value=None, allow_none=True, help="turn hardware handshake (RTS/CTS) on and off.").tag(config=True) + + +class SeeedStudio(SingletonConfigurable, CanBase): + """Seeed USB-Can analyzer interface.""" + + interface_name = "seeedstudio" + + timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) + baudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) + frame_type = Enum(["STD", "EXT"], default_value=None, allow_none=True, help="To select standard or extended messages.").tag( + config=True + ) + operation_mode = Enum( + ["normal", "loopback", "silent", "loopback_and_silent"], default_value=None, allow_none=True, help=""" """ + ).tag(config=True) + + +class Serial(SingletonConfigurable, CanBase): + """A text based interface.""" + + interface_name = "serial" + + has_bitrate = False + + rtscts = Bool(default_value=None, allow_none=True, help="turn hardware handshake (RTS/CTS) on and off.").tag(config=True) + timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) + baudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) + + +class SlCan(SingletonConfigurable, CanBase): + """CAN over Serial / SLCAN.""" + + interface_name = "slcan" + + has_poll_interval = True + + ttyBaudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) + rtscts = Bool(default_value=None, allow_none=True, help="turn hardware handshake (RTS/CTS) on and off.").tag(config=True) + timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) + btr = Integer(default_value=None, allow_none=True, help="BTR register value to set custom can speed.").tag(config=True) + sleep_after_open = Float( + default_value=None, allow_none=True, help="Time to wait in seconds after opening serial connection." + ).tag(config=True) + + +class SocketCan(SingletonConfigurable, CanBase): + """Linux SocketCAN.""" + + interface_name = "socketcan" + + has_fd = True + has_bitrate = False + has_receive_own_messages = True + + local_loopback = Bool( + default_value=None, + allow_none=True, + help="""If local loopback should be enabled on this bus. +Please note that local loopback does not mean that messages sent +on a socket will be readable on the same socket, they will only +be readable on other open sockets on the same machine. More info +can be read on the socketcan documentation: +See https://www.kernel.org/doc/html/latest/networking/can.html#socketcan-local-loopback1""", + ).tag(config=True) + + +class SocketCanD(SingletonConfigurable, CanBase): + """Network-to-CAN bridge as a Linux damon.""" + + interface_name = "socketcand" + + has_bitrate = False + + host = Unicode(default_value=None, allow_none=True, help=""" """).tag(config=True) + port = Integer(default_value=None, allow_none=True, help=""" """).tag(config=True) + + +class Systec(SingletonConfigurable, CanBase): + """SYSTEC interface""" + + interface_name = "systec" + + has_receive_own_messages = True + + state = Instance(can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) + device_number = Integer(min=0, max=254, default_value=None, allow_none=True, help="The device number of the USB-CAN.").tag( + config=True + ) + rx_buffer_entries = Integer( + default_value=None, allow_none=True, help="The maximum number of entries in the receive buffer." + ).tag(config=True) + tx_buffer_entries = Integer( + default_value=None, allow_none=True, help="The maximum number of entries in the transmit buffer." + ).tag(config=True) + + +class Udp_Multicast(SingletonConfigurable, CanBase): + """A virtual interface for CAN communications between multiple processes using UDP over Multicast IP.""" + + interface_name = "udp_multicast" + + has_fd = True + has_bitrate = False + has_receive_own_messages = True + + port = Integer(default_value=None, allow_none=True, help="The IP port to read from and write to.").tag(config=True) + hop_limit = Integer(default_value=None, allow_none=True, help="The hop limit in IPv6 or in IPv4 the time to live (TTL).").tag( + config=True + ) + + +class Usb2Can(SingletonConfigurable, CanBase): + """Interface to a USB2CAN Bus.""" + + interface_name = "usb2can" + + flags = Integer( + default_value=None, allow_none=True, help="Flags to directly pass to open function of the usb2can abstraction layer." + ).tag(config=True) + dll = Unicode(default_value=None, allow_none=True, help="Path to the DLL with the CANAL API to load.").tag(config=True) + serial = Unicode(default_value=None, allow_none=True, help="Alias for `channel` that is provided for legacy reasons.").tag( + config=True + ) + + +class Vector(SingletonConfigurable, CanBase): + """Vector Informatik CAN interfaces.""" + + interface_name = "vector" + + has_fd = True + has_data_bitrate = True + has_poll_interval = True + has_receive_own_messages = True + has_timing = True + + CAN_PARAM_MAP = { + "sjw_abr": "sjw_abr", + "tseg1_abr": "tseg1_abr", + "tseg2_abr": "tseg2_abr", + "sjw_dbr": "sjw_dbr", + "tseg1_dbr": "tseg1_dbr", + "tseg2_dbr": "tseg2_dbr", + } + + serial = Integer( + default_value=None, + allow_none=True, + help="""Serial number of the hardware to be used. +If set, the channel parameter refers to the channels ONLY on the specified hardware. +If set, the `app_name` does not have to be previously defined in +*Vector Hardware Config*.""", + ).tag(config=True) + rx_queue_size = Integer( + min=16, max=32768, default_value=None, allow_none=True, help="Number of messages in receive queue (power of 2)." + ).tag(config=True) + app_name = Unicode(default_value=None, allow_none=True, help="Name of application in *Vector Hardware Config*.").tag( + config=True + ) + + +class Virtual(SingletonConfigurable, CanBase): + """ """ + + interface_name = "virtual" + + has_bitrate = False + has_receive_own_messages = True + + rx_queue_size = Integer( + default_value=None, + allow_none=True, + help="""The size of the reception queue. The reception +queue stores messages until they are read. If the queue reaches +its capacity, it will start dropping the oldest messages to make +room for new ones. If set to 0, the queue has an infinite capacity. +Be aware that this can cause memory leaks if messages are read +with a lower frequency than they arrive on the bus. """, + ).tag(config=True) + preserve_timestamps = Bool( + default_value=None, + allow_none=True, + help="""If set to True, messages transmitted via +will keep the timestamp set in the +:class:`~can.Message` instance. Otherwise, the timestamp value +will be replaced with the current system time.""", + ).tag(config=True) + + +CAN_INTERFACE_MAP = { + "canalystii": CanAlystii, + "cantact": CanTact, + "etas": Etas, + "gs_usb": Gs_Usb, + "iscan": IsCan, + "ixxat": Ixxat, + "kvaser": Kvaser, + "neousys": NeouSys, + "neovi": Neovi, + "nican": NiCan, + "nixnet": NixNet, + "pcan": PCan, + "robotell": Robotell, + "seeedstudio": SeeedStudio, + "serial": Serial, + "slcan": SlCan, + "socketcan": SocketCan, + "socketcand": SocketCanD, + "systec": Systec, + "udp_multicast": Udp_Multicast, + "usb2can": Usb2Can, + "vector": Vector, + "virtual": Virtual, +} + + +class Can(SingletonConfigurable): + VALID_INTERFACES = can.interfaces.VALID_INTERFACES + + interface = Enum(VALID_INTERFACES, default_value=None, allow_none=True, help="CAN interface supported by python-can").tag( + config=True + ) + channel = Any( + default_value=None, allow_none=True, help="Channel identification. Expected type and value is backend dependent." + ).tag(config=True) + max_dlc_required = Bool(False, help="Master to slave frames always to have DLC = MAX_DLC = 8").tag(config=True) + # max_can_fd_dlc = Integer(64, help="").tag(config=True) + padding_value = Integer(0, help="Fill value, if max_dlc_required == True and DLC < MAX_DLC").tag(config=True) + use_default_listener = Bool(True, help="").tag(config=True) + can_id_master = Integer(allow_none=False, help="CAN-ID master -> slave (Bit31= 1: extended identifier)").tag( + config=True + ) # CMD and STIM packets + can_id_slave = Integer(allow_none=True, help="CAN-ID slave -> master (Bit31= 1: extended identifier)").tag( + config=True + ) # RES, ERR, EV, SERV and DAQ packets. + can_id_broadcast = Integer( + default_value=None, allow_none=True, help="Auto detection CAN-ID (Bit31= 1: extended identifier)" + ).tag(config=True) + daq_identifier = List(trait=Integer(), default_value=[], allow_none=True, help="One CAN identifier per DAQ-list.").tag( + config=True + ) + bitrate = Integer(250000, help="CAN bitrate in bits/s (arbitration phase, if CAN FD).").tag(config=True) + receive_own_messages = Bool(False, help="Enable self-reception of sent messages.").tag(config=True) + poll_interval = Float(default_value=None, allow_none=True, help="Poll interval in seconds when reading messages.").tag( + config=True + ) + fd = Bool(False, help="If CAN-FD frames should be supported.").tag(config=True) + data_bitrate = Integer(default_value=None, allow_none=True, help="Which bitrate to use for data phase in CAN FD.").tag( + config=True + ) + sjw_abr = Integer( + default_value=None, allow_none=True, help="Bus timing value sample jump width (arbitration, SJW if CAN classic)." + ).tag(config=True) + tseg1_abr = Integer( + default_value=None, allow_none=True, help="Bus timing value tseg1 (arbitration, TSEG1 if CAN classic)." + ).tag(config=True) + tseg2_abr = Integer( + default_value=None, allow_none=True, help="Bus timing value tseg2 (arbitration, TSEG2, if CAN classic)" + ).tag(config=True) + sjw_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value sample jump width (data).").tag(config=True) + tseg1_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg1 (data).").tag(config=True) + tseg2_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg2 (data).").tag(config=True) + timing = Union( + [Instance(can.BitTiming)], # , Instance(can.BitTimingFd) + default_value=None, + allow_none=True, + help="""Custom bit timing settings. +(.s https://github.com/hardbyte/python-can/blob/develop/can/bit_timing.py) +If this parameter is provided, it takes precedence over all other +timing-related parameters. + """, + ).tag(config=True) + + classes = List( + [ + CanAlystii, + CanTact, + Etas, + Gs_Usb, + Neovi, + IsCan, + Ixxat, + Kvaser, + NeouSys, + NiCan, + NixNet, + PCan, + Robotell, + SeeedStudio, + Serial, + SlCan, + SocketCan, + SocketCanD, + Systec, + Udp_Multicast, + Usb2Can, + Vector, + Virtual, + ] + ) + + def __init__(self, **kws): + super().__init__(**kws) + + if self.parent.layer == "CAN": + if self.interface is None or self.interface not in self.VALID_INTERFACES: + raise TraitError( + f"CAN interface must be one of {sorted(list(self.VALID_INTERFACES))} not the" + " {type(self.interface).__name__} {self.interface}." + ) + self.canalystii = CanAlystii.instance(config=self.config, parent=self) + self.cantact = CanTact.instance(config=self.config, parent=self) + self.etas = Etas.instance(config=self.config, parent=self) + self.gs_usb = Gs_Usb.instance(config=self.config, parent=self) + self.neovi = Neovi.instance(config=self.config, parent=self) + self.iscan = IsCan.instance(config=self.config, parent=self) + self.ixxat = Ixxat.instance(config=self.config, parent=self) + self.kvaser = Kvaser.instance(config=self.config, parent=self) + self.neousys = NeouSys.instance(config=self.config, parent=self) + self.nican = NiCan.instance(config=self.config, parent=self) + self.nixnet = NixNet.instance(config=self.config, parent=self) + self.pcan = PCan.instance(config=self.config, parent=self) + self.robotell = Robotell.instance(config=self.config, parent=self) + self.seeedstudio = SeeedStudio.instance(config=self.config, parent=self) + self.serial = Serial.instance(config=self.config, parent=self) + self.slcan = SlCan.instance(config=self.config, parent=self) + self.socketcan = SocketCan.instance(config=self.config, parent=self) + self.socketcand = SocketCanD.instance(config=self.config, parent=self) + self.systec = Systec.instance(config=self.config, parent=self) + self.udp_multicast = Udp_Multicast.instance(config=self.config, parent=self) + self.usb2can = Usb2Can.instance(config=self.config, parent=self) + self.vector = Vector.instance(config=self.config, parent=self) + self.virtual = Virtual.instance(config=self.config, parent=self) + + +class Eth(SingletonConfigurable): + """Ethernet.""" + + host = Unicode("localhost", help="Hostname or IP address of XCP slave.").tag(config=True) + port = Integer(5555, help="TCP/UDP port to connect.").tag(config=True) + protocol = Enum(["TCP", "UDP"], default_value="UDP", help="").tag(config=True) + ipv6 = Bool(False, help="Use IPv6 if `True` else IPv4.").tag(config=True) + tcp_nodelay = Bool(False, help="*** Expert option *** -- Disable Nagle's algorithm if `True`.").tag(config=True) + bind_to_address = Unicode(default_value=None, allow_none=True, help="Bind to specific local address.").tag(config=True) + bind_to_port = Integer(default_value=None, allow_none=True, help="Bind to specific local port.").tag(config=True) + + +class SxI(SingletonConfigurable): + """SCI and SPI connections.""" + + port = Unicode("COM1", help="Name of communication interface.").tag(config=True) + bitrate = Integer(38400, help="Connection bitrate").tag(config=True) + bytesize = Enum([5, 6, 7, 8], default_value=8, help="Size of byte.").tag(config=True) + parity = Enum(["N", "E", "O", "M", "S"], default_value="N", help="Paritybit calculation.").tag(config=True) + stopbits = Enum([1, 1.5, 2], default_value=1, help="Number of stopbits.").tag(config=True) + mode = Enum( + [ + "ASYNCH_FULL_DUPLEX_MODE", + "SYNCH_FULL_DUPLEX_MODE_BYTE", + "SYNCH_FULL_DUPLEX_MODE_WORD", + "SYNCH_FULL_DUPLEX_MODE_DWORD", + "SYNCH_MASTER_SLAVE_MODE_BYTE", + "SYNCH_MASTER_SLAVE_MODE_WORD", + "SYNCH_MASTER_SLAVE_MODE_DWORD", + ], + default_value="ASYNCH_FULL_DUPLEX_MODE", + help="Asynchronous (SCI) or synchronous (SPI) communication mode.", + ).tag(config=True) + header_format = Enum( + [ + "HEADER_LEN_BYTE", + "HEADER_LEN_CTR_BYTE", + "HEADER_LEN_FILL_BYTE", + "HEADER_LEN_WORD", + "HEADER_LEN_CTR_WORD", + "HEADER_LEN_FILL_WORD", + ], + default_value="HEADER_LEN_CTR_WORD", + help="""XCPonSxI header format. +Number of bytes: + + LEN CTR FILL +______________________________________________________________ +HEADER_LEN_BYTE | 1 X X +HEADER_LEN_CTR_BYTE | 1 1 X +HEADER_LEN_FILL_BYTE | 1 X 1 +HEADER_LEN_WORD | 2 X X +HEADER_LEN_CTR_WORD | 2 2 X +HEADER_LEN_FILL_WORD | 2 X 2 +""", + ).tag(config=True) + tail_format = Enum( + ["NO_CHECKSUM", "CHECKSUM_BYTE", "CHECKSUM_WORD"], default_value="NO_CHECKSUM", help="XCPonSxI tail format." + ).tag(config=True) + framing = Bool(False, help="Enable SCI framing mechanism (ESC chars).").tag(config=True) + esc_sync = Integer(0x01, min=0, max=255, help="SCI framing protocol character SYNC.").tag(config=True) + esc_esc = Integer(0x00, min=0, max=255, help="SCI framing protocol character ESC.").tag(config=True) + + +class Usb(SingletonConfigurable): + """Universal Serial Bus connections.""" + + serial_number = Unicode("", help="Device serial number.").tag(config=True) + configuration_number = Integer(1, help="USB configuration number.").tag(config=True) + interface_number = Integer(2, help="USB interface number.").tag(config=True) + vendor_id = Integer(0, help="USB vendor ID.").tag(config=True) + product_id = Integer(0, help="USB product ID.").tag(config=True) + library = Unicode("", help="Absolute path to USB shared library.").tag(config=True) + header_format = Enum( + [ + "HEADER_LEN_BYTE", + "HEADER_LEN_CTR_BYTE", + "HEADER_LEN_FILL_BYTE", + "HEADER_LEN_WORD", + "HEADER_LEN_CTR_WORD", + "HEADER_LEN_FILL_WORD", + ], + default_value="HEADER_LEN_CTR_WORD", + help="", + ).tag(config=True) + in_ep_number = Integer(1, help="Ingoing USB reply endpoint number (IN-EP for RES/ERR, DAQ, and EV/SERV).").tag(config=True) + in_ep_transfer_type = Enum( + ["BULK_TRANSFER", "INTERRUPT_TRANSFER"], default_value="BULK_TRANSFER", help="Ingoing: Supported USB transfer types." + ).tag(config=True) + in_ep_max_packet_size = Integer(512, help="Ingoing: Maximum packet size of endpoint in bytes.").tag(config=True) + in_ep_polling_interval = Integer(0, help="Ingoing: Polling interval of endpoint.").tag(config=True) + in_ep_message_packing = Enum( + ["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], + default_value="MESSAGE_PACKING_SINGLE", + help="Ingoing: Packing of XCP Messages.", + ).tag(config=True) + in_ep_alignment = Enum( + ["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], + default_value="ALIGNMENT_8_BIT", + help="Ingoing: Alignment border.", + ).tag(config=True) + in_ep_recommended_host_bufsize = Integer(0, help="Ingoing: Recommended host buffer size.").tag(config=True) + out_ep_number = Integer(0, help="Outgoing USB command endpoint number (OUT-EP for CMD and STIM).").tag(config=True) + out_ep_transfer_type = Enum( + ["BULK_TRANSFER", "INTERRUPT_TRANSFER"], default_value="BULK_TRANSFER", help="Outgoing: Supported USB transfer types." + ).tag(config=True) + out_ep_max_packet_size = Integer(512, help="Outgoing: Maximum packet size of endpoint in bytes.").tag(config=True) + out_ep_polling_interval = Integer(0, help="Outgoing: Polling interval of endpoint.").tag(config=True) + out_ep_message_packing = Enum( + ["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], + default_value="MESSAGE_PACKING_SINGLE", + help="Outgoing: Packing of XCP Messages.", + ).tag(config=True) + out_ep_alignment = Enum( + ["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], + default_value="ALIGNMENT_8_BIT", + help="Outgoing: Alignment border.", + ).tag(config=True) + out_ep_recommended_host_bufsize = Integer(0, help="Outgoing: Recommended host buffer size.").tag(config=True) + + +class Transport(SingletonConfigurable): + """ """ + + classes = List([Can, Eth, SxI, Usb]) + + layer = Enum( + ["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=True, help="Choose one of the supported XCP transport layers." + ).tag(config=True) + create_daq_timestamps = Bool(False, help="Record time of frame reception or set timestamp to 0.").tag(config=True) + timeout = Float( + 2.0, + help="""raise `XcpTimeoutError` after `timeout` seconds +if there is no response to a command.""", + ).tag(config=True) + alignment = Enum([1, 2, 4, 8], default_value=1).tag(config=True) + + can = Instance(Can).tag(config=True) + eth = Instance(Eth).tag(config=True) + sxi = Instance(SxI).tag(config=True) + usb = Instance(Usb).tag(config=True) + + def __init__(self, **kws): + super().__init__(**kws) + self.can = Can.instance(config=self.config, parent=self) + self.eth = Eth.instance(config=self.config, parent=self) + self.sxi = SxI.instance(config=self.config, parent=self) + self.usb = Usb.instance(config=self.config, parent=self) + + +class General(SingletonConfigurable): + """ """ + + loglevel = Unicode("INFO", help="Set the log level by value or name.").tag(config=True) + disable_error_handling = Bool(False, help="Disable XCP error-handler for performance reasons.").tag(config=True) + disconnect_response_optional = Bool(False, help="Ignore missing response on DISCONNECT request.").tag(config=True) + seed_n_key_dll = Unicode("", allow_none=False, help="Dynamic library used for slave resource unlocking.").tag(config=True) + seed_n_key_dll_same_bit_width = Bool(False, help="").tag(config=True) + seed_n_key_function = Callable( + default_value=None, + allow_none=True, + help="""Python function used for slave resource unlocking. +Could be used if seed-and-key algorithm is known instead of `seed_n_key_dll`.""", + ).tag(config=True) + stim_support = Bool(False, help="").tag(config=True) + + +class ProfileCreate(Application): + description = "\nCreate a new profile" + + dest_file = Unicode(default_value=None, allow_none=True, help="destination file name").tag(config=True) + aliases = Dict( # type:ignore[assignment] + dict( + d="ProfileCreate.dest_file", + o="ProfileCreate.dest_file", + ) + ) + + def start(self): + self.parent.parent.generate_config_file(sys.stdout, {}) + + +class ProfileConvert(Application): + description = "\nConvert legacy configuration file (.json/.toml) to new Python based format." + + config_file = Unicode(help="Name of legacy config file (.json/.toml).", default_value=None, allow_none=False).tag( + config=True + ) # default_value="pyxcp_conf.py", + + dest_file = Unicode(default_value=None, allow_none=True, help="destination file name").tag(config=True) + + aliases = Dict( # type:ignore[assignment] + dict( + c="ProfileConvert.config_file", + d="ProfileConvert.dest_file", + o="ProfileConvert.dest_file", + ) + ) + + def start(self): + pyxcp = self.parent.parent + pyxcp._read_configuration(self.config_file, emit_warning=False) + if self.dest_file: + dest = Path(self.dest_file) + if dest.exists(): + if not Confirm.ask(f"Destination file [green]{dest.name!r}[/green] already exists. do you want to overwrite it?"): + print("Aborting...") + self.exit(1) + with dest.open("w", encoding="latin1") as out_file: + pyxcp.generate_config_file(out_file) + else: + pyxcp.generate_config_file(sys.stdout) + + +class ProfileApp(Application): + subcommands = Dict( + dict( + create=(ProfileCreate, ProfileCreate.description.splitlines()[0]), + convert=(ProfileConvert, ProfileConvert.description.splitlines()[0]), + ) + ) + + def start(self): + if self.subapp is None: + print(f"No subcommand specified. Must specify one of: {self.subcommands.keys()}") + print() + self.print_description() + self.print_subcommands() + self.exit(1) + else: + self.subapp.start() + + +class PyXCP(Application): + config_file = Unicode(default_value="pyxcp_conf.py", help="base name of config file").tag(config=True) + + classes = List([General, Transport]) + + subcommands = dict( + profile=( + ProfileApp, + """ + Profile stuff + """.strip(), + ) + ) + + def start(self): + if self.subapp: + self.subapp.start() + exit(2) + else: + self._read_configuration(self.config_file) + self._setup_logger() + + def _setup_logger(self): + from pyxcp.types import Command + + # Remove any handlers installed by `traitlets`. + for hdl in self.log.handlers: + self.log.removeHandler(hdl) + + # formatter = logging.Formatter(fmt=self.log_format, datefmt=self.log_datefmt) + + keywords = list(Command.__members__.keys()) + ["ARGS", "KWS"] # Syntax highlight XCP commands and other stuff. + rich_handler = RichHandler( + rich_tracebacks=True, + tracebacks_show_locals=True, + log_time_format=self.log_datefmt, + level=self.log_level, + keywords=keywords, + ) + # rich_handler.setFormatter(formatter) + self.log.addHandler(rich_handler) + + def initialize(self, argv=None): + from pyxcp import __version__ as pyxcp_version + + PyXCP.version = pyxcp_version + PyXCP.name = Path(sys.argv[0]).name + self.parse_command_line(argv[1:]) + self.log.debug(f"pyxcp version: {self.version}") + + def _read_configuration(self, file_name: str, emit_warning: bool = True) -> None: + self.read_configuration_file(file_name, emit_warning) + self.general = General.instance(config=self.config, parent=self) + self.transport = Transport.instance(parent=self) + + def read_configuration_file(self, file_name: str, emit_warning: bool = True): + self.legacy_config: bool = False + + pth = Path(file_name) + if not pth.exists(): + raise FileNotFoundError(f"Configuration file {file_name!r} does not exist.") + suffix = pth.suffix.lower() + if suffix == ".py": + self.load_config_file(pth) + else: + self.legacy_config = True + if suffix == ".json": + reader = json + elif suffix == ".toml": + reader = toml + else: + raise ValueError(f"Unknown file type for config: {suffix}") + with pth.open("r") as f: + if emit_warning: + self.log.warning(f"Legacy configuration file format ({suffix}), please use Python based configuration.") + cfg = reader.loads(f.read()) + if cfg: + cfg = legacy.convert_config(cfg, self.log) + self.config = cfg + return cfg + + flags = Dict( # type:ignore[assignment] + dict( + debug=({"PyXCP": {"log_level": 10}}, "Set loglevel to DEBUG"), + ) + ) + + aliases = Dict( # type:ignore[assignment] + dict( + c="PyXCP.config_file", + log_level="PyXCP.log_level", + l="PyXCP.log_level", + ) + ) + + def _iterate_config_class(self, klass, class_names: typing.List[str], config, out_file: io.IOBase = sys.stdout) -> None: + sub_classes = [] + class_path = ".".join(class_names) + print( + f"""\n# ------------------------------------------------------------------------------ +# {class_path} configuration +# ------------------------------------------------------------------------------""", + end="\n\n", + file=out_file, + ) + if hasattr(klass, "classes"): + kkk = klass.classes + if hasattr(kkk, "default"): + if class_names[-1] not in ("PyXCP"): + sub_classes.extend(kkk.default()) + for name, tr in klass.class_own_traits().items(): + md = tr.metadata + if md.get("config"): + help = md.get("help", "").lstrip() + commented_lines = "\n".join([f"# {line}" for line in help.split("\n")]) + print(f"#{commented_lines}", file=out_file) + value = tr.default() + if isinstance(tr, Instance) and tr.__class__.__name__ not in ("Dict", "List"): + continue + if isinstance(tr, Enum): + print(f"# Choices: {tr.info()}", file=out_file) + else: + print(f"# Type: {tr.info()}", file=out_file) + print(f"# Default: {value!r}", file=out_file) + if name in config: + cfg_value = config[name] + print(f"c.{class_path!s}.{name!s} = {cfg_value!r}", end="\n\n", file=out_file) + else: + print(f"# c.{class_path!s}.{name!s} = {value!r}", end="\n\n", file=out_file) + if class_names is None: + class_names = [] + for sub_klass in sub_classes: + self._iterate_config_class( + sub_klass, class_names + [sub_klass.__name__], config=config.get(sub_klass.__name__, {}), out_file=out_file + ) + + def generate_config_file(self, file_like: io.IOBase, config=None) -> None: + print("#", file=file_like) + print("# Configuration file for pyXCP.", file=file_like) + print("#", file=file_like) + print("c = get_config() # noqa", end="\n\n", file=file_like) + + for klass in self._classes_with_config_traits(): + self._iterate_config_class( + klass, [klass.__name__], config=self.config.get(klass.__name__, {}) if config is None else {}, out_file=file_like + ) + + +application: typing.Optional[PyXCP] = None + + +def create_application() -> PyXCP: + global application + if application is not None: + return application + application = PyXCP() + application.initialize(sys.argv) + application.start() + return application + + +def get_application() -> PyXCP: + global application + if application is None: + application = create_application() + return application diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index f533ecf..6f17312 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -1,94 +1,94 @@ - -#if !defined(__HELPER_HPP) - #define __HELPER_HPP - - #include - #include - - #if __cplusplus >= 202302L - #include - - #if defined(__STDCPP_BFLOAT16_T__) - #define HAS_BFLOAT16 (1) - #else - #define HAS_BFLOAT16 (0) - #endif - - #if defined(__STDCPP_FLOAT16_T__) - #define HAS_FLOAT16 (1) - #else - #define HAS_FLOAT16 (0) - #endif - #else - #define HAS_FLOAT16 (0) - #define HAS_BFLOAT16 (0) - #endif - -template -constexpr void DBG_PRINTN(Args &&...args) noexcept { - ((std::cout << std::forward(args) << " "), ...); -} - -// NOTE: C++23 has std::byteswap() -constexpr auto _bswap(std::uint64_t v) noexcept { - return ((v & UINT64_C(0x0000'0000'0000'00FF)) << 56) | ((v & UINT64_C(0x0000'0000'0000'FF00)) << 40) | - ((v & UINT64_C(0x0000'0000'00FF'0000)) << 24) | ((v & UINT64_C(0x0000'0000'FF00'0000)) << 8) | - ((v & UINT64_C(0x0000'00FF'0000'0000)) >> 8) | ((v & UINT64_C(0x0000'FF00'0000'0000)) >> 24) | - ((v & UINT64_C(0x00FF'0000'0000'0000)) >> 40) | ((v & UINT64_C(0xFF00'0000'0000'0000)) >> 56); -} - -constexpr auto _bswap(std::uint32_t v) noexcept { - return ((v & UINT32_C(0x0000'00FF)) << 24) | ((v & UINT32_C(0x0000'FF00)) << 8) | ((v & UINT32_C(0x00FF'0000)) >> 8) | - ((v & UINT32_C(0xFF00'0000)) >> 24); -} - -constexpr auto _bswap(std::uint16_t v) noexcept { - return ((v & UINT16_C(0x00FF)) << 8) | ((v & UINT16_C(0xFF00)) >> 8); -} - -template -inline std::string to_binary(const T &value) { - std::string result; - - auto ptr = reinterpret_cast(&value); - for (std::size_t idx = 0; idx < sizeof(T); ++idx) { - auto ch = ptr[idx]; - result.push_back(ch); - } - return result; -} - -template<> -inline std::string to_binary(const std::string &value) { - std::string result; - - auto ptr = reinterpret_cast(value.c_str()); - const std::size_t length = std::size(value); - - // We are using Pascal strings as serialization format. - auto len_bin = to_binary(length); - std::copy(len_bin.begin(), len_bin.end(), std::back_inserter(result)); - for (std::size_t idx = 0; idx < length; ++idx) { - auto ch = ptr[idx]; - result.push_back(ch); - } - return result; -} - -inline auto bool_to_string(bool value) { - return (value == true) ? "True" : "False"; -} - -inline auto byte_order_to_string(int value) { - switch (value) { - case 0: - return "INTEL"; - case 1: - return "MOTOROLA"; - default: - return ""; - } - return ""; -} - -#endif // __HELPER_HPP + +#if !defined(__HELPER_HPP) + #define __HELPER_HPP + + #include + #include + + #if __cplusplus >= 202302L + #include + + #if defined(__STDCPP_BFLOAT16_T__) + #define HAS_BFLOAT16 (1) + #else + #define HAS_BFLOAT16 (0) + #endif + + #if defined(__STDCPP_FLOAT16_T__) + #define HAS_FLOAT16 (1) + #else + #define HAS_FLOAT16 (0) + #endif + #else + #define HAS_FLOAT16 (0) + #define HAS_BFLOAT16 (0) + #endif + +template +constexpr void DBG_PRINTN(Args &&...args) noexcept { + ((std::cout << std::forward(args) << " "), ...); +} + +// NOTE: C++23 has std::byteswap() +constexpr auto _bswap(std::uint64_t v) noexcept { + return ((v & UINT64_C(0x0000'0000'0000'00FF)) << 56) | ((v & UINT64_C(0x0000'0000'0000'FF00)) << 40) | + ((v & UINT64_C(0x0000'0000'00FF'0000)) << 24) | ((v & UINT64_C(0x0000'0000'FF00'0000)) << 8) | + ((v & UINT64_C(0x0000'00FF'0000'0000)) >> 8) | ((v & UINT64_C(0x0000'FF00'0000'0000)) >> 24) | + ((v & UINT64_C(0x00FF'0000'0000'0000)) >> 40) | ((v & UINT64_C(0xFF00'0000'0000'0000)) >> 56); +} + +constexpr auto _bswap(std::uint32_t v) noexcept { + return ((v & UINT32_C(0x0000'00FF)) << 24) | ((v & UINT32_C(0x0000'FF00)) << 8) | ((v & UINT32_C(0x00FF'0000)) >> 8) | + ((v & UINT32_C(0xFF00'0000)) >> 24); +} + +constexpr auto _bswap(std::uint16_t v) noexcept { + return ((v & UINT16_C(0x00FF)) << 8) | ((v & UINT16_C(0xFF00)) >> 8); +} + +template +inline std::string to_binary(const T &value) { + std::string result; + + auto ptr = reinterpret_cast(&value); + for (std::size_t idx = 0; idx < sizeof(T); ++idx) { + auto ch = ptr[idx]; + result.push_back(ch); + } + return result; +} + +template<> +inline std::string to_binary(const std::string &value) { + std::string result; + + auto ptr = reinterpret_cast(value.c_str()); + const std::size_t length = std::size(value); + + // We are using Pascal strings as serialization format. + auto len_bin = to_binary(length); + std::copy(len_bin.begin(), len_bin.end(), std::back_inserter(result)); + for (std::size_t idx = 0; idx < length; ++idx) { + auto ch = ptr[idx]; + result.push_back(ch); + } + return result; +} + +inline auto bool_to_string(bool value) { + return (value == true) ? "True" : "False"; +} + +inline auto byte_order_to_string(int value) { + switch (value) { + case 0: + return "INTEL"; + case 1: + return "MOTOROLA"; + default: + return ""; + } + return ""; +} + +#endif // __HELPER_HPP diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index 18e5965..7aaa1c7 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -1,188 +1,188 @@ - -#if !defined(__MC_OBJECT_HPP) - #define __MC_OBJECT_HPP - - #include - #include - #include - #include - #include - #include - - #include "helper.hpp" - -const std::map> TYPE_MAP = { - { "U8", { 0, 1 } }, - { "I8", { 1, 1 } }, - { "U16", { 2, 2 } }, - { "I16", { 3, 2 } }, - { "U32", { 4, 4 } }, - { "I32", { 5, 4 } }, - { "U64", { 6, 8 } }, - { "I64", { 7, 8 } }, - { "F32", { 8, 4 } }, - { "F64", { 9, 8 } }, - #if HAS_FLOAT16 - { "F16", { 10, 2 } }, - #endif - #if HAS_BFLOAT16 - { "BF16", { 11, 2 } }, - #endif -}; - -inline std::vector get_data_types() { - std::vector result; - - for (const auto& [k, v] : TYPE_MAP) { - result.emplace_back(k); - } - - return result; -} - -class McObject { - public: - - explicit McObject( - std::string_view name, std::uint32_t address, std::uint8_t ext, std::uint16_t length, const std::string& data_type, - const std::vector& components = std::vector() - ) : - m_name(name), - m_address(address), - m_ext(ext), - m_length(length), - m_data_type(data_type), - m_type_index(-1), - m_components(components) { - if (data_type != "") { - std::string dt_toupper; - - dt_toupper.resize(data_type.size()); - - std::transform(data_type.begin(), data_type.end(), dt_toupper.begin(), [](unsigned char c) -> unsigned char { - return std::toupper(c); - }); - - if (!TYPE_MAP.contains(dt_toupper)) { - throw std::runtime_error("Invalid data type: " + data_type); - } - - const auto [ti, len] = TYPE_MAP.at(dt_toupper); - m_type_index = ti; - m_length = len; - } - } - - McObject(const McObject& obj) = default; - McObject(McObject&& obj) = default; - McObject& operator=(const McObject&) = default; - McObject& operator=(McObject&&) = default; - - const std::string& get_name() const { - return m_name; - } - - void set_name(std::string_view name) { - m_name = name; - } - - std::uint32_t get_address() const { - return m_address; - } - - void set_address(std::uint32_t address) { - m_address = address; - } - - std::uint8_t get_ext() const { - return m_ext; - } - - void set_ext(std::uint8_t ext) { - m_ext = ext; - } - - const std::string& get_data_type() const { - return m_data_type; - } - - void set_data_type(const std::string& value) { - m_data_type = value; - } - - std::uint16_t get_length() const { - return m_length; - } - - void set_length(std::uint16_t length) { - m_length = length; - } - - std::int32_t get_type_index() const { - return m_type_index; - } - - const std::vector& get_components() const { - return m_components; - } - - void add_component(const McObject& obj) { - m_components.emplace_back(obj); - } - - bool operator==(const McObject& other) const { - return (m_name == other.m_name) && (m_address == other.m_address) && (m_ext == other.m_ext) && - (m_length == other.m_length) && (m_data_type == other.m_data_type) && - (std::equal(m_components.begin(), m_components.end(), other.m_components.begin(), other.m_components.end())); - } - - std::string dumps() const { - std::stringstream ss; - - ss << to_binary(m_name); - ss << to_binary(m_address); - ss << to_binary(m_ext); - ss << to_binary(m_length); - ss << to_binary(m_data_type); - ss << to_binary(m_type_index); - - std::size_t ccount = m_components.size(); - ss << to_binary(ccount); - for (const auto& obj : m_components) { - ss << obj.dumps(); - } - return ss.str(); - } - - private: - - std::string m_name; - std::uint32_t m_address; - std::uint8_t m_ext; - std::uint16_t m_length; - std::string m_data_type; - std::int16_t m_type_index; - std::vector m_components{}; -}; - -std::string mc_components_to_string(const std::vector& components); - -std::string to_string(const McObject& obj) { - std::stringstream ss; - - ss << "McObject(name='" << obj.get_name() << "', address=" << obj.get_address() - << ", ext=" << static_cast(obj.get_ext()) << ", data_type='" << obj.get_data_type() - << "', length=" << obj.get_length() << ", components=[" << mc_components_to_string(obj.get_components()) << "])"; - return ss.str(); -} - -std::string mc_components_to_string(const std::vector& components) { - std::stringstream ss; - - for (const auto& obj : components) { - ss << to_string(obj) << ",\n "; - } - return ss.str(); -} - -#endif // __MC_OBJECT_HPP + +#if !defined(__MC_OBJECT_HPP) + #define __MC_OBJECT_HPP + + #include + #include + #include + #include + #include + #include + + #include "helper.hpp" + +const std::map> TYPE_MAP = { + { "U8", { 0, 1 } }, + { "I8", { 1, 1 } }, + { "U16", { 2, 2 } }, + { "I16", { 3, 2 } }, + { "U32", { 4, 4 } }, + { "I32", { 5, 4 } }, + { "U64", { 6, 8 } }, + { "I64", { 7, 8 } }, + { "F32", { 8, 4 } }, + { "F64", { 9, 8 } }, + #if HAS_FLOAT16 + { "F16", { 10, 2 } }, + #endif + #if HAS_BFLOAT16 + { "BF16", { 11, 2 } }, + #endif +}; + +inline std::vector get_data_types() { + std::vector result; + + for (const auto& [k, v] : TYPE_MAP) { + result.emplace_back(k); + } + + return result; +} + +class McObject { + public: + + explicit McObject( + std::string_view name, std::uint32_t address, std::uint8_t ext, std::uint16_t length, const std::string& data_type, + const std::vector& components = std::vector() + ) : + m_name(name), + m_address(address), + m_ext(ext), + m_length(length), + m_data_type(data_type), + m_type_index(-1), + m_components(components) { + if (data_type != "") { + std::string dt_toupper; + + dt_toupper.resize(data_type.size()); + + std::transform(data_type.begin(), data_type.end(), dt_toupper.begin(), [](unsigned char c) -> unsigned char { + return std::toupper(c); + }); + + if (!TYPE_MAP.contains(dt_toupper)) { + throw std::runtime_error("Invalid data type: " + data_type); + } + + const auto [ti, len] = TYPE_MAP.at(dt_toupper); + m_type_index = ti; + m_length = len; + } + } + + McObject(const McObject& obj) = default; + McObject(McObject&& obj) = default; + McObject& operator=(const McObject&) = default; + McObject& operator=(McObject&&) = default; + + const std::string& get_name() const { + return m_name; + } + + void set_name(std::string_view name) { + m_name = name; + } + + std::uint32_t get_address() const { + return m_address; + } + + void set_address(std::uint32_t address) { + m_address = address; + } + + std::uint8_t get_ext() const { + return m_ext; + } + + void set_ext(std::uint8_t ext) { + m_ext = ext; + } + + const std::string& get_data_type() const { + return m_data_type; + } + + void set_data_type(const std::string& value) { + m_data_type = value; + } + + std::uint16_t get_length() const { + return m_length; + } + + void set_length(std::uint16_t length) { + m_length = length; + } + + std::int32_t get_type_index() const { + return m_type_index; + } + + const std::vector& get_components() const { + return m_components; + } + + void add_component(const McObject& obj) { + m_components.emplace_back(obj); + } + + bool operator==(const McObject& other) const { + return (m_name == other.m_name) && (m_address == other.m_address) && (m_ext == other.m_ext) && + (m_length == other.m_length) && (m_data_type == other.m_data_type) && + (std::equal(m_components.begin(), m_components.end(), other.m_components.begin(), other.m_components.end())); + } + + std::string dumps() const { + std::stringstream ss; + + ss << to_binary(m_name); + ss << to_binary(m_address); + ss << to_binary(m_ext); + ss << to_binary(m_length); + ss << to_binary(m_data_type); + ss << to_binary(m_type_index); + + std::size_t ccount = m_components.size(); + ss << to_binary(ccount); + for (const auto& obj : m_components) { + ss << obj.dumps(); + } + return ss.str(); + } + + private: + + std::string m_name; + std::uint32_t m_address; + std::uint8_t m_ext; + std::uint16_t m_length; + std::string m_data_type; + std::int16_t m_type_index; + std::vector m_components{}; +}; + +std::string mc_components_to_string(const std::vector& components); + +std::string to_string(const McObject& obj) { + std::stringstream ss; + + ss << "McObject(name='" << obj.get_name() << "', address=" << obj.get_address() + << ", ext=" << static_cast(obj.get_ext()) << ", data_type='" << obj.get_data_type() + << "', length=" << obj.get_length() << ", components=[" << mc_components_to_string(obj.get_components()) << "])"; + return ss.str(); +} + +std::string mc_components_to_string(const std::vector& components) { + std::stringstream ss; + + for (const auto& obj : components) { + ss << to_string(obj) << ",\n "; + } + return ss.str(); +} + +#endif // __MC_OBJECT_HPP diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index f583582..61af50e 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -26,7 +26,7 @@ ) -data_types = data_types() +DATA_TYPES = data_types() @dataclass diff --git a/pyxcp/recorder/reader.hpp b/pyxcp/recorder/reader.hpp index 63b527e..6f8012f 100644 --- a/pyxcp/recorder/reader.hpp +++ b/pyxcp/recorder/reader.hpp @@ -1,139 +1,139 @@ - -#ifndef RECORDER_READER_HPP -#define RECORDER_READER_HPP - -#include - -class XcpLogFileReader { - public: - - explicit XcpLogFileReader(const std::string &file_name) { - if (!file_name.ends_with(detail::FILE_EXTENSION)) { - m_file_name = file_name + detail::FILE_EXTENSION; - } else { - m_file_name = file_name; - } - - m_mmap = new mio::mmap_source(m_file_name); - blob_t magic[detail::MAGIC_SIZE + 1]; - - read_bytes(0UL, detail::MAGIC_SIZE, magic); - if (memcmp(detail::MAGIC.c_str(), magic, detail::MAGIC_SIZE) != 0) { - throw std::runtime_error("Invalid file magic."); - } - m_offset = detail::MAGIC_SIZE; - - read_bytes(m_offset, detail::FILE_HEADER_SIZE, reinterpret_cast(&m_header)); - // printf("Sizes: %u %u %.3f\n", m_header.size_uncompressed, - // m_header.size_compressed, - // float(m_header.size_uncompressed) / float(m_header.size_compressed)); - if (m_header.hdr_size != detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE) { - throw std::runtime_error("File header size does not match."); - } - if (detail::VERSION != m_header.version) { - throw std::runtime_error("File version mismatch."); - } - - if (m_header.num_containers < 1) { - throw std::runtime_error("At least one container required."); - } - - m_offset += detail::FILE_HEADER_SIZE; - - if ((m_header.options & XMRAW_HAS_METADATA) == XMRAW_HAS_METADATA) { - std::size_t metadata_length = 0; - std::size_t data_start = m_offset + sizeof(std::size_t); - - read_bytes(m_offset, sizeof(std::size_t), reinterpret_cast(&metadata_length)); - - std::copy(ptr(data_start), ptr(data_start + metadata_length), std::back_inserter(m_metadata)); - // std::cout << "Metadata: " << m_metadata << std::endl; - m_offset += (metadata_length + sizeof(std::size_t)); - } - } - - [[nodiscard]] FileHeaderType get_header() const noexcept { - return m_header; - } - - [[nodiscard]] auto get_header_as_tuple() const noexcept -> HeaderTuple { - auto hdr = get_header(); - - return std::make_tuple( - hdr.version, hdr.options, hdr.num_containers, hdr.record_count, hdr.size_uncompressed, hdr.size_compressed, - (double)((std::uint64_t)(((double)hdr.size_uncompressed / (double)hdr.size_compressed * 100.0) + 0.5)) / 100.0 - ); - } - - [[nodiscard]] auto get_metadata() const noexcept { - return m_metadata; - } - - void reset() noexcept { - m_current_container = 0; - m_offset = file_header_size(); - } - - std::optional next_block() { - auto container = ContainerHeaderType{}; - auto frame = frame_header_t{}; - std::uint64_t boffs = 0; - auto result = FrameVector{}; - payload_t payload; - - if (m_current_container >= m_header.num_containers) { - return std::nullopt; - } - read_bytes(m_offset, detail::CONTAINER_SIZE, reinterpret_cast(&container)); - __ALIGN auto buffer = new blob_t[container.size_uncompressed]; - m_offset += detail::CONTAINER_SIZE; - result.reserve(container.record_count); - const int uc_size = ::LZ4_decompress_safe( - reinterpret_cast(ptr(m_offset)), reinterpret_cast(buffer), container.size_compressed, - container.size_uncompressed - ); - if (uc_size < 0) { - throw std::runtime_error("LZ4 decompression failed."); - } - boffs = 0; - for (std::uint64_t idx = 0; idx < container.record_count; ++idx) { - _fcopy(reinterpret_cast(&frame), reinterpret_cast(&(buffer[boffs])), sizeof(frame_header_t)); - boffs += sizeof(frame_header_t); - result.emplace_back( - frame.category, frame.counter, frame.timestamp, frame.length, create_payload(frame.length, &buffer[boffs]) - ); - boffs += frame.length; - } - m_offset += container.size_compressed; - m_current_container += 1; - delete[] buffer; - - return std::optional{ result }; - } - - ~XcpLogFileReader() noexcept { - delete m_mmap; - } - - protected: - - [[nodiscard]] blob_t const *ptr(std::uint64_t pos = 0) const { - return reinterpret_cast(m_mmap->data() + pos); - } - - void read_bytes(std::uint64_t pos, std::uint64_t count, blob_t *buf) const { - auto addr = reinterpret_cast(ptr(pos)); - _fcopy(reinterpret_cast(buf), addr, count); - } - - private: - - std::string m_file_name; - std::uint64_t m_offset{ 0 }; - std::uint64_t m_current_container{ 0 }; - mio::mmap_source *m_mmap{ nullptr }; - FileHeaderType m_header; - std::string m_metadata; -}; - -#endif // RECORDER_READER_HPP + +#ifndef RECORDER_READER_HPP +#define RECORDER_READER_HPP + +#include + +class XcpLogFileReader { + public: + + explicit XcpLogFileReader(const std::string &file_name) { + if (!file_name.ends_with(detail::FILE_EXTENSION)) { + m_file_name = file_name + detail::FILE_EXTENSION; + } else { + m_file_name = file_name; + } + + m_mmap = new mio::mmap_source(m_file_name); + blob_t magic[detail::MAGIC_SIZE + 1]; + + read_bytes(0UL, detail::MAGIC_SIZE, magic); + if (memcmp(detail::MAGIC.c_str(), magic, detail::MAGIC_SIZE) != 0) { + throw std::runtime_error("Invalid file magic."); + } + m_offset = detail::MAGIC_SIZE; + + read_bytes(m_offset, detail::FILE_HEADER_SIZE, reinterpret_cast(&m_header)); + // printf("Sizes: %u %u %.3f\n", m_header.size_uncompressed, + // m_header.size_compressed, + // float(m_header.size_uncompressed) / float(m_header.size_compressed)); + if (m_header.hdr_size != detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE) { + throw std::runtime_error("File header size does not match."); + } + if (detail::VERSION != m_header.version) { + throw std::runtime_error("File version mismatch."); + } + + if (m_header.num_containers < 1) { + throw std::runtime_error("At least one container required."); + } + + m_offset += detail::FILE_HEADER_SIZE; + + if ((m_header.options & XMRAW_HAS_METADATA) == XMRAW_HAS_METADATA) { + std::size_t metadata_length = 0; + std::size_t data_start = m_offset + sizeof(std::size_t); + + read_bytes(m_offset, sizeof(std::size_t), reinterpret_cast(&metadata_length)); + + std::copy(ptr(data_start), ptr(data_start + metadata_length), std::back_inserter(m_metadata)); + // std::cout << "Metadata: " << m_metadata << std::endl; + m_offset += (metadata_length + sizeof(std::size_t)); + } + } + + [[nodiscard]] FileHeaderType get_header() const noexcept { + return m_header; + } + + [[nodiscard]] auto get_header_as_tuple() const noexcept -> HeaderTuple { + auto hdr = get_header(); + + return std::make_tuple( + hdr.version, hdr.options, hdr.num_containers, hdr.record_count, hdr.size_uncompressed, hdr.size_compressed, + (double)((std::uint64_t)(((double)hdr.size_uncompressed / (double)hdr.size_compressed * 100.0) + 0.5)) / 100.0 + ); + } + + [[nodiscard]] auto get_metadata() const noexcept { + return m_metadata; + } + + void reset() noexcept { + m_current_container = 0; + m_offset = file_header_size(); + } + + std::optional next_block() { + auto container = ContainerHeaderType{}; + auto frame = frame_header_t{}; + std::uint64_t boffs = 0; + auto result = FrameVector{}; + payload_t payload; + + if (m_current_container >= m_header.num_containers) { + return std::nullopt; + } + read_bytes(m_offset, detail::CONTAINER_SIZE, reinterpret_cast(&container)); + __ALIGN auto buffer = new blob_t[container.size_uncompressed]; + m_offset += detail::CONTAINER_SIZE; + result.reserve(container.record_count); + const int uc_size = ::LZ4_decompress_safe( + reinterpret_cast(ptr(m_offset)), reinterpret_cast(buffer), container.size_compressed, + container.size_uncompressed + ); + if (uc_size < 0) { + throw std::runtime_error("LZ4 decompression failed."); + } + boffs = 0; + for (std::uint64_t idx = 0; idx < container.record_count; ++idx) { + _fcopy(reinterpret_cast(&frame), reinterpret_cast(&(buffer[boffs])), sizeof(frame_header_t)); + boffs += sizeof(frame_header_t); + result.emplace_back( + frame.category, frame.counter, frame.timestamp, frame.length, create_payload(frame.length, &buffer[boffs]) + ); + boffs += frame.length; + } + m_offset += container.size_compressed; + m_current_container += 1; + delete[] buffer; + + return std::optional{ result }; + } + + ~XcpLogFileReader() noexcept { + delete m_mmap; + } + + protected: + + [[nodiscard]] blob_t const *ptr(std::uint64_t pos = 0) const { + return reinterpret_cast(m_mmap->data() + pos); + } + + void read_bytes(std::uint64_t pos, std::uint64_t count, blob_t *buf) const { + auto addr = reinterpret_cast(ptr(pos)); + _fcopy(reinterpret_cast(buf), addr, count); + } + + private: + + std::string m_file_name; + std::uint64_t m_offset{ 0 }; + std::uint64_t m_current_container{ 0 }; + mio::mmap_source *m_mmap{ nullptr }; + FileHeaderType m_header; + std::string m_metadata; +}; + +#endif // RECORDER_READER_HPP diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 297081a..8eebab8 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -1,272 +1,272 @@ - -#if !defined(__REKORDER_HPP) - #define __REKORDER_HPP - - #if !defined(STANDALONE_REKORDER) - #define STANDALONE_REKORDER 0 - #endif /* STANDALONE_REKORDER */ - - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include "blockmem.hpp" - #include "event.hpp" - #include "tsqueue.hpp" - - #if defined(_WIN32) - #include - #include - #include - #endif /* _WIN32 */ - - #include "lz4hc.h" - #include "mio.hpp" - - #if STANDALONE_REKORDER == 0 - #include - #include - #include - -namespace py = pybind11; -using namespace pybind11::literals; - #endif /* STANDALONE_REKORDER */ - - #if !defined(__BIGGEST_ALIGNMENT__) - #define __BIGGEST_ALIGNMENT__ (8) - #endif - - #define __ALIGNMENT_REQUIREMENT __BIGGEST_ALIGNMENT__ - #define __ALIGN alignas(__ALIGNMENT_REQUIREMENT) - -constexpr auto kilobytes(std::uint32_t value) -> std::uint32_t { - return value * 1024; -} - -constexpr auto megabytes(std::uint32_t value) -> std::uint32_t { - return kilobytes(value) * 1024; -} - -constexpr std::uint16_t XCP_PAYLOAD_MAX = 0xFFFFUL; - -constexpr std::uint16_t XMRAW_HAS_METADATA = 0x0004UL; - - /* - byte-order is, where applicable little ending (LSB first). - */ - #pragma pack(push) - #pragma pack(1) - -struct FileHeaderType { - std::uint16_t hdr_size; - std::uint16_t version; - std::uint16_t options; - std::uint64_t num_containers; - std::uint64_t record_count; - std::uint64_t size_compressed; - std::uint64_t size_uncompressed; -}; - -using HeaderTuple = std::tuple; - -static_assert(sizeof(FileHeaderType) == 38); - -struct ContainerHeaderType { - std::uint32_t record_count; - std::uint32_t size_compressed; - std::uint32_t size_uncompressed; -}; - -using blob_t = unsigned char; -using blob_string = std::basic_string; - - #if STANDALONE_REKORDER == 1 -using payload_t = std::shared_ptr; - #else -using payload_t = py::array_t; - #endif /* STANDALONE_REKORDER */ - -struct frame_header_t { - std::uint8_t category{ 0 }; - std::uint16_t counter{ 0 }; - double timestamp{ 0.0 }; - std::uint16_t length{ 0 }; -}; - - #pragma pack(pop) - -using FrameTuple = std::tuple; -using FrameVector = std::vector; -using FrameTupleWriter = std::tuple; - -enum class FrameCategory : std::uint8_t { - META, - CMD, - RES, - ERR, - EV, - SERV, - DAQ, - STIM, -}; - -namespace detail { -const std::string FILE_EXTENSION(".xmraw"); -const std::string MAGIC{ "ASAMINT::XCP_RAW" }; -constexpr auto MAGIC_SIZE = 16; -constexpr auto VERSION = 0x0100; -constexpr auto FILE_HEADER_SIZE = sizeof(FileHeaderType); -constexpr auto CONTAINER_SIZE = sizeof(ContainerHeaderType); -} // namespace detail - -constexpr auto file_header_size() -> std::uint64_t { - return (detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE); -} - -using rounding_func_t = std::function; - -inline rounding_func_t create_rounding_func(std::uint64_t multiple) { - return [multiple](std::uint64_t value) { - return (value + (multiple - 1)) & ~(multiple - 1); - }; -} - -const auto round_to_alignment = create_rounding_func(__ALIGNMENT_REQUIREMENT); - -inline void _fcopy(char* dest, char const * src, std::uint64_t n) noexcept { - for (std::uint64_t i = 0; i < n; ++i) { - dest[i] = src[i]; - } -} - - #if STANDALONE_REKORDER == 1 -inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { - return payload.get(); -} - -inline payload_t create_payload(std::uint64_t size, blob_t const * data) noexcept { - auto pl = std::make_shared(size); - _fcopy(reinterpret_cast(pl.get()), reinterpret_cast(data), size); - return pl; -} - #else -inline payload_t create_payload(std::uint64_t size, blob_t const * data) { - return py::array_t(size, data); -} - -inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { - py::buffer_info buf = payload.request(); - - return static_cast(buf.ptr); -} - #endif /* STANDALONE_REKORDER */ - - #ifdef _WIN32 -inline std::string error_string(std::string_view func, std::error_code error_code) { - LPSTR messageBuffer = nullptr; - std::ostringstream ss; - - size_t size = FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, - static_cast(error_code.value()), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL - ); - - std::string message(messageBuffer, size); - LocalFree(messageBuffer); - - ss << "[ERROR] "; - ss << func << ": "; - ss << message; - return ss.str(); -} - -inline std::error_code get_last_error() { - return std::error_code(GetLastError(), std::system_category()); -} - - #else -inline std::string error_string(std::string_view func, std::error_code error_code) { - std::ostringstream ss; - - auto message = strerror(static_cast(error_code.value())); - - ss << "[ERROR] "; - ss << func << ": "; - ss << message; - return ss.str(); -} - -inline std::error_code get_last_error() { - return std::error_code(errno, std::system_category()); -} - - #endif // _WIN32 - -inline std::string& ltrim(std::string& s) { - auto it = std::find_if(s.begin(), s.end(), [](char c) { return !std::isspace(c, std::locale::classic()); }); - s.erase(s.begin(), it); - return s; -} - -inline std::string& rtrim(std::string& s) { - auto it = std::find_if(s.rbegin(), s.rend(), [](char c) { return !std::isspace(c, std::locale::classic()); }); - s.erase(it.base(), s.end()); - return s; -} - -inline std::string& trim(std::string& s) { - return ltrim(rtrim(s)); -} - -inline std::string current_timestamp() { - std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - - #if defined(_MSC_VER) - errno_t err; - char tbuf[128]; - std::string result{}; - - err = ::ctime_s(tbuf, 128, &now); - if (err == 0) { - result = tbuf; - } - - #else - std::string result{ ::ctime(&now) }; - #endif - - // result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end()); - return trim(result); -} - -inline void hexdump(blob_t const * buf, std::uint16_t sz) { - for (std::uint16_t idx = 0; idx < sz; ++idx) { - printf("%02X ", buf[idx]); - } - printf("\n\r"); -} - - #include "reader.hpp" - #include "unfolder.hpp" - #include "writer.hpp" - -#endif // __REKORDER_HPP + +#if !defined(__REKORDER_HPP) + #define __REKORDER_HPP + + #if !defined(STANDALONE_REKORDER) + #define STANDALONE_REKORDER 0 + #endif /* STANDALONE_REKORDER */ + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include "blockmem.hpp" + #include "event.hpp" + #include "tsqueue.hpp" + + #if defined(_WIN32) + #include + #include + #include + #endif /* _WIN32 */ + + #include "lz4hc.h" + #include "mio.hpp" + + #if STANDALONE_REKORDER == 0 + #include + #include + #include + +namespace py = pybind11; +using namespace pybind11::literals; + #endif /* STANDALONE_REKORDER */ + + #if !defined(__BIGGEST_ALIGNMENT__) + #define __BIGGEST_ALIGNMENT__ (8) + #endif + + #define __ALIGNMENT_REQUIREMENT __BIGGEST_ALIGNMENT__ + #define __ALIGN alignas(__ALIGNMENT_REQUIREMENT) + +constexpr auto kilobytes(std::uint32_t value) -> std::uint32_t { + return value * 1024; +} + +constexpr auto megabytes(std::uint32_t value) -> std::uint32_t { + return kilobytes(value) * 1024; +} + +constexpr std::uint16_t XCP_PAYLOAD_MAX = 0xFFFFUL; + +constexpr std::uint16_t XMRAW_HAS_METADATA = 0x0004UL; + + /* + byte-order is, where applicable little ending (LSB first). + */ + #pragma pack(push) + #pragma pack(1) + +struct FileHeaderType { + std::uint16_t hdr_size; + std::uint16_t version; + std::uint16_t options; + std::uint64_t num_containers; + std::uint64_t record_count; + std::uint64_t size_compressed; + std::uint64_t size_uncompressed; +}; + +using HeaderTuple = std::tuple; + +static_assert(sizeof(FileHeaderType) == 38); + +struct ContainerHeaderType { + std::uint32_t record_count; + std::uint32_t size_compressed; + std::uint32_t size_uncompressed; +}; + +using blob_t = unsigned char; +using blob_string = std::basic_string; + + #if STANDALONE_REKORDER == 1 +using payload_t = std::shared_ptr; + #else +using payload_t = py::array_t; + #endif /* STANDALONE_REKORDER */ + +struct frame_header_t { + std::uint8_t category{ 0 }; + std::uint16_t counter{ 0 }; + double timestamp{ 0.0 }; + std::uint16_t length{ 0 }; +}; + + #pragma pack(pop) + +using FrameTuple = std::tuple; +using FrameVector = std::vector; +using FrameTupleWriter = std::tuple; + +enum class FrameCategory : std::uint8_t { + META, + CMD, + RES, + ERR, + EV, + SERV, + DAQ, + STIM, +}; + +namespace detail { +const std::string FILE_EXTENSION(".xmraw"); +const std::string MAGIC{ "ASAMINT::XCP_RAW" }; +constexpr auto MAGIC_SIZE = 16; +constexpr auto VERSION = 0x0100; +constexpr auto FILE_HEADER_SIZE = sizeof(FileHeaderType); +constexpr auto CONTAINER_SIZE = sizeof(ContainerHeaderType); +} // namespace detail + +constexpr auto file_header_size() -> std::uint64_t { + return (detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE); +} + +using rounding_func_t = std::function; + +inline rounding_func_t create_rounding_func(std::uint64_t multiple) { + return [multiple](std::uint64_t value) { + return (value + (multiple - 1)) & ~(multiple - 1); + }; +} + +const auto round_to_alignment = create_rounding_func(__ALIGNMENT_REQUIREMENT); + +inline void _fcopy(char* dest, char const * src, std::uint64_t n) noexcept { + for (std::uint64_t i = 0; i < n; ++i) { + dest[i] = src[i]; + } +} + + #if STANDALONE_REKORDER == 1 +inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { + return payload.get(); +} + +inline payload_t create_payload(std::uint64_t size, blob_t const * data) noexcept { + auto pl = std::make_shared(size); + _fcopy(reinterpret_cast(pl.get()), reinterpret_cast(data), size); + return pl; +} + #else +inline payload_t create_payload(std::uint64_t size, blob_t const * data) { + return py::array_t(size, data); +} + +inline blob_t* get_payload_ptr(const payload_t& payload) noexcept { + py::buffer_info buf = payload.request(); + + return static_cast(buf.ptr); +} + #endif /* STANDALONE_REKORDER */ + + #ifdef _WIN32 +inline std::string error_string(std::string_view func, std::error_code error_code) { + LPSTR messageBuffer = nullptr; + std::ostringstream ss; + + size_t size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + static_cast(error_code.value()), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL + ); + + std::string message(messageBuffer, size); + LocalFree(messageBuffer); + + ss << "[ERROR] "; + ss << func << ": "; + ss << message; + return ss.str(); +} + +inline std::error_code get_last_error() { + return std::error_code(GetLastError(), std::system_category()); +} + + #else +inline std::string error_string(std::string_view func, std::error_code error_code) { + std::ostringstream ss; + + auto message = strerror(static_cast(error_code.value())); + + ss << "[ERROR] "; + ss << func << ": "; + ss << message; + return ss.str(); +} + +inline std::error_code get_last_error() { + return std::error_code(errno, std::system_category()); +} + + #endif // _WIN32 + +inline std::string& ltrim(std::string& s) { + auto it = std::find_if(s.begin(), s.end(), [](char c) { return !std::isspace(c, std::locale::classic()); }); + s.erase(s.begin(), it); + return s; +} + +inline std::string& rtrim(std::string& s) { + auto it = std::find_if(s.rbegin(), s.rend(), [](char c) { return !std::isspace(c, std::locale::classic()); }); + s.erase(it.base(), s.end()); + return s; +} + +inline std::string& trim(std::string& s) { + return ltrim(rtrim(s)); +} + +inline std::string current_timestamp() { + std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + + #if defined(_MSC_VER) + errno_t err; + char tbuf[128]; + std::string result{}; + + err = ::ctime_s(tbuf, 128, &now); + if (err == 0) { + result = tbuf; + } + + #else + std::string result{ ::ctime(&now) }; + #endif + + // result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end()); + return trim(result); +} + +inline void hexdump(blob_t const * buf, std::uint16_t sz) { + for (std::uint16_t idx = 0; idx < sz; ++idx) { + printf("%02X ", buf[idx]); + } + printf("\n\r"); +} + + #include "reader.hpp" + #include "unfolder.hpp" + #include "writer.hpp" + +#endif // __REKORDER_HPP diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 68591c5..7e5573f 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -1,1183 +1,1183 @@ - -#ifndef RECORDER_UNFOLDER_HPP -#define RECORDER_UNFOLDER_HPP - -#include -#include -#include -#include -#include -#include - -#include "daqlist.hpp" -#include "helper.hpp" -#include "mcobject.hpp" -#include "writer.hpp" - -using measurement_value_t = std::variant; -using measurement_tuple_t = std::tuple>; -using measurement_callback_t = std::function)>; - -template -auto get_value(blob_t const * buf, std::uint64_t offset) -> Ty { - return *reinterpret_cast(&buf[offset]); -} - -template -auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> Ty { - return _bswap(get_value(buf, offset)); -} - -#if HAS_FLOAT16 == 1 -template<> -auto get_value(blob_t const * buf, std::uint64_t offset) -> std::float16_t { - auto tmp = get_value(buf, offset); - - return std::bit_cast(tmp); -} -#endif - -#if HAS_BFLOAT16 == 1 -template<> -auto get_value(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { - auto tmp = get_value(buf, offset); - - return std::bit_cast(tmp); -} -#endif - -template<> -auto get_value(blob_t const * buf, std::uint64_t offset) -> float { - auto tmp = get_value(buf, offset); - - return std::bit_cast(tmp); -} - -template<> -auto get_value(blob_t const * buf, std::uint64_t offset) -> double { - auto tmp = get_value(buf, offset); - - return std::bit_cast(tmp); -} - -#if HAS_FLOAT16 == 1 -template<> -auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::float16_t { - auto tmp = get_value_swapped(buf, offset); - - return std::bit_cast(tmp); -} -#endif - -#if HAS_BFLOAT16 == 1 -template<> -auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { - auto tmp = get_value_swapped(buf, offset); - - return std::bit_cast(tmp); -} -#endif - -template<> -auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> float { - auto tmp = get_value_swapped(buf, offset); - - return std::bit_cast(tmp); -} - -template<> -auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> double { - auto tmp = get_value_swapped(buf, offset); - - return std::bit_cast(tmp); -} - -template<> -auto get_value(blob_t const * buf, std::uint64_t offset) -> std::int16_t { - return static_cast(get_value(buf, offset)); -} - -template<> -auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::int16_t { - return static_cast(get_value_swapped(buf, offset)); -} - -template<> -auto get_value(blob_t const * buf, std::uint64_t offset) -> std::int32_t { - return static_cast(get_value(buf, offset)); -} - -template<> -auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::int32_t { - return static_cast(get_value_swapped(buf, offset)); -} - -template<> -auto get_value(blob_t const * buf, std::uint64_t offset) -> std::int64_t { - return static_cast(get_value(buf, offset)); -} - -template<> -auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::int64_t { - return static_cast(get_value_swapped(buf, offset)); -} - -///////////////////////////////////////////////////// -///////////////////////////////////////////////////// -template -void set_value(blob_t* buf, std::uint64_t offset, Ty value) { - ::memcpy(&buf[offset], &value, sizeof(Ty)); -} - -template -void set_value_swapped(blob_t* buf, std::uint64_t offset, Ty value) { - set_value(buf, offset, _bswap(value)); -} - -template<> -void set_value(blob_t* buf, std::uint64_t offset, std::int8_t value) { - buf[offset] = static_cast(value); -} - -template<> -void set_value(blob_t* buf, std::uint64_t offset, std::uint8_t value) { - buf[offset] = static_cast(value); -} - -template<> -void set_value(blob_t* buf, std::uint64_t offset, std::int16_t value) { - set_value(buf, offset, static_cast(value)); -} - -template<> -void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int16_t value) { - set_value_swapped(buf, offset, static_cast(value)); -} - -template<> -void set_value(blob_t* buf, std::uint64_t offset, std::int32_t value) { - set_value(buf, offset, static_cast(value)); -} - -template<> -void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int32_t value) { - set_value_swapped(buf, offset, static_cast(value)); -} - -template<> -void set_value(blob_t* buf, std::uint64_t offset, std::int64_t value) { - set_value(buf, offset, static_cast(value)); -} - -template<> -void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int64_t value) { - set_value_swapped(buf, offset, static_cast(value)); -} - -#if HAS_FLOAT16 == 1 -template<> -void set_value(blob_t* buf, std::uint64_t offset, std::float16_t value) { - // set_value(buf, offset, *reinterpret_cast(&value)); - set_value(buf, offset, std::bit_cast(value)); -} - -template<> -void set_value_swapped(blob_t* buf, std::uint64_t offset, std::float16_t value) { - // set_value_swapped(buf, offset, *reinterpret_cast(&value)); - set_value_swapped(buf, offset, std::bit_cast(value)); -} -#endif - -#if HAS_BFLOAT16 == 1 -template<> -void set_value(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { - // set_value(buf, offset, *reinterpret_cast(&value)); - set_value(buf, offset, std::bit_cast(value)); -} - -template<> -void set_value_swapped(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { - // set_value_swapped(buf, offset, *reinterpret_cast(&value)); - set_value_swapped(buf, offset, std::bit_cast(value)); -} -#endif - -template<> -void set_value(blob_t* buf, std::uint64_t offset, float value) { - // set_value(buf, offset, *reinterpret_cast(&value)); - set_value(buf, offset, std::bit_cast(value)); -} - -template<> -void set_value_swapped(blob_t* buf, std::uint64_t offset, float value) { - // set_value_swapped(buf, offset, *reinterpret_cast(&value)); - set_value_swapped(buf, offset, std::bit_cast(value)); -} - -template<> -void set_value(blob_t* buf, std::uint64_t offset, double value) { - // set_value(buf, offset, *reinterpret_cast(&value)); - set_value(buf, offset, std::bit_cast(value)); -} - -template<> -void set_value_swapped(blob_t* buf, std::uint64_t offset, double value) { - // set_value_swapped(buf, offset, *reinterpret_cast(&value)); - set_value_swapped(buf, offset, std::bit_cast(value)); -} - -/* -** Get primitive datatypes, consider byte-order. -*/ -struct Getter { - Getter() = default; - - explicit Getter(bool requires_swap, std::uint8_t id_size, std::uint8_t ts_size) : m_id_size(id_size), m_ts_size(ts_size) { - int8 = get_value; - uint8 = get_value; - - if (requires_swap) { - int16 = get_value_swapped; - int32 = get_value_swapped; - int64 = get_value_swapped; - uint16 = get_value_swapped; - uint32 = get_value_swapped; - uint64 = get_value_swapped; - float_ = get_value_swapped; - double_ = get_value_swapped; -#if HAS_FLOAT16 == 1 - float16 = get_value_swapped; -#endif -#if HAS_BFLOAT16 == 1 - bfloat16 = get_value_swapped; -#endif - } else { - int16 = get_value; - int32 = get_value; - int64 = get_value; - uint16 = get_value; - uint32 = get_value; - uint64 = get_value; - float_ = get_value; - double_ = get_value; -#if HAS_FLOAT16 == 1 - float16 = get_value; -#endif -#if HAS_BFLOAT16 == 1 - bfloat16 = get_value; -#endif - } - } - - std::uint32_t get_timestamp(blob_t const * buf) { - switch (m_ts_size) { - case 0: - return 0; - case 1: - return uint8(buf, m_id_size); - case 2: - return uint16(buf, m_id_size); - case 4: - return uint32(buf, m_id_size); - default: - throw std::runtime_error("Unsupported timestamp size: " + std::to_string(m_ts_size)); - } - } - - measurement_value_t reader(std::uint16_t tp, blob_t const * buf, std::uint16_t offset) const { - switch (tp) { - case 0: - return static_cast(uint8(buf, offset)); - case 1: - return int8(buf, offset); - case 2: - return static_cast(uint16(buf, offset)); - case 3: - return int16(buf, offset); - case 4: - return static_cast(uint32(buf, offset)); - case 5: - return int32(buf, offset); - case 6: - return uint64(buf, offset); - case 7: - return int64(buf, offset); - case 8: - return float_(buf, offset); - case 9: - return double_(buf, offset); -#if HAS_FLOAT16 == 1 - case 10: - return float16(buf, offset); -#endif -#if HAS_BFLOAT16 == 1 - case 11: - return bfloat16(buf, offset); -#endif - default: - throw std::runtime_error("Unsupported data type: " + std::to_string(tp)); - } - } - - void set_first_pids(const std::vector& daq_lists, const std::vector& first_pids) { - m_first_pids = first_pids; - - if (m_id_size == 1) { - // In case of 1-byte ID field (absolute ODT number) we need a mapping. - std::uint16_t daq_list_num = 0; - for (const auto& daq_list : daq_lists) { - auto first_pid = m_first_pids[daq_list_num]; - - for (std::uint16_t idx = first_pid; idx < daq_list.get_odt_count() + first_pid; ++idx) { - m_odt_to_daq_map[idx] = { daq_list_num, (idx - first_pid) }; - } - daq_list_num++; - } - } - } - - std::tuple get_id(blob_t const * buf) { - std::uint16_t odt_num = 0; - - switch (m_id_size) { - case 1: - odt_num = uint8(buf, 0); // Get 1-byte ODT number... - return m_odt_to_daq_map[odt_num]; // ...and return mapped values. - case 2: - return { uint8(buf, 1), uint8(buf, 0) }; - case 3: - return { uint16(buf, 1), uint8(buf, 0) }; - case 4: - return { uint16(buf, 2), uint8(buf, 0) }; - default: - throw std::runtime_error("Unsupported ID size: " + std::to_string(m_id_size)); - } - } - - std::uint8_t m_id_size; - std::uint8_t m_ts_size; - std::function int8; - std::function uint8; - std::function int16; - std::function int32; - std::function int64; - std::function uint16; - std::function uint32; - std::function uint64; - std::function float_; - std::function double_; -#if HAS_FLOAT16 == 1 - std::function float16; -#endif -#if HAS_BFLOAT16 == 1 - std::function bfloat16; -#endif - std::vector m_first_pids; - std::map> m_odt_to_daq_map; -}; - -////////////////////////////////////////////////////////////////////////////////////////////// -struct Setter { - Setter() = default; - - explicit Setter(bool requires_swap, std::uint8_t id_size, std::uint8_t ts_size) : m_id_size(id_size), m_ts_size(ts_size) { - int8 = set_value; - uint8 = set_value; - - if (requires_swap) { - int16 = set_value_swapped; - int32 = set_value_swapped; - int64 = set_value_swapped; - uint16 = set_value_swapped; - uint32 = set_value_swapped; - uint64 = set_value_swapped; - float_ = set_value_swapped; - double_ = set_value_swapped; -#if HAS_FLOAT16 == 1 - float16 = set_value_swapped; -#endif -#if HAS_BFLOAT16 == 1 - bfloat16 = set_value_swapped; -#endif - } else { - int16 = set_value; - int32 = set_value; - int64 = set_value; - uint16 = set_value; - uint32 = set_value; - uint64 = set_value; - float_ = set_value; - double_ = set_value; -#if HAS_FLOAT16 == 1 - float16 = set_value; -#endif -#if HAS_BFLOAT16 == 1 - bfloat16 = set_value; -#endif - } - } - - void set_timestamp(blob_t* buf, std::uint32_t timestamp) { - switch (m_ts_size) { - case 0: - break; - case 1: - uint8(buf, m_id_size, timestamp); - break; - case 2: - uint16(buf, m_id_size, timestamp); - break; - case 4: - uint32(buf, m_id_size, timestamp); - break; - default: - throw std::runtime_error("Unsupported timestamp size: " + std::to_string(m_ts_size)); - } - } - - void writer(std::uint16_t tp, blob_t* buf, std::uint16_t offset, const measurement_value_t& value) { - switch (tp) { - case 0: - uint8(buf, offset, static_cast(std::get(value))); - break; - case 1: - int8(buf, offset, static_cast(std::get(value))); - break; - case 2: - uint16(buf, offset, static_cast(std::get(value))); - break; - case 3: - int16(buf, offset, static_cast(std::get(value))); - break; - case 4: - uint32(buf, offset, static_cast(std::get(value))); - break; - case 5: - int32(buf, offset, static_cast(std::get(value))); - break; - case 6: - uint64(buf, offset, std::get(value)); - break; - case 7: - int64(buf, offset, std::get(value)); - break; - case 8: - float_(buf, offset, static_cast(std::get(value))); - break; - case 9: - double_(buf, offset, static_cast(std::get(value))); - break; -#if HAS_FLOAT16 == 1 - case 10: - float16(buf, offset, static_cast(std::get(value))); - break; -#endif -#if HAS_BFLOAT16 == 1 - case 11: - bfloat16(buf, offset, static_cast(std::get(value))); - break; -#endif - default: - throw std::runtime_error("Unsupported data type: " + std::to_string(tp)); - } - } - -#if 0 - std::tuple set_id(blob_t const * buf) { - std::uint16_t odt_num = 0; - - switch (m_id_size) { - case 1: - odt_num = uint8(buf, 0); // Get 1-byte ODT number... - return m_odt_to_daq_map[odt_num]; // ...and return mapped values. - case 2: - return { uint8(buf, 1), uint8(buf, 0) }; - case 3: - return { uint16(buf, 1), uint8(buf, 0) }; - case 4: - return { uint16(buf, 2), uint8(buf, 0) }; - default: - throw std::runtime_error("Unsupported ID size: " + std::to_string(m_id_size)); - } - } -#endif - std::uint8_t m_id_size; - std::uint8_t m_ts_size; - std::function int8; - std::function uint8; - std::function int16; - std::function int32; - std::function int64; - std::function uint16; - std::function uint32; - std::function uint64; - std::function float_; - std::function double_; -#if HAS_FLOAT16 == 1 - std::function float16; -#endif -#if HAS_BFLOAT16 == 1 - std::function bfloat16; -#endif - std::map> m_odt_to_daq_map; -}; - -////////////////////////////////////////////////////////////////////////////////////////////// - -struct MeasurementParameters { - MeasurementParameters() = default; - - MeasurementParameters(MeasurementParameters&&) = default; - MeasurementParameters(const MeasurementParameters&) = default; - MeasurementParameters& operator=(MeasurementParameters&&) = default; - MeasurementParameters& operator=(const MeasurementParameters&) = default; - - explicit MeasurementParameters( - std::uint8_t byte_order, std::uint8_t id_field_size, bool timestamps_supported, bool ts_fixed, bool prescaler_supported, - bool selectable_timestamps, double ts_scale_factor, std::uint8_t ts_size, std::uint16_t min_daq, - const std::vector& daq_lists, const std::vector& first_pids - ) : - m_byte_order(byte_order), - m_id_field_size(id_field_size), - m_timestamps_supported(timestamps_supported), - m_ts_fixed(ts_fixed), - m_prescaler_supported(prescaler_supported), - m_selectable_timestamps(selectable_timestamps), - m_ts_scale_factor(ts_scale_factor), - m_ts_size(ts_size), - m_min_daq(min_daq), - m_daq_lists(daq_lists), - m_first_pids(first_pids) { - } - - std::string dumps() const { - std::stringstream ss; - - ss << to_binary(m_byte_order); - ss << to_binary(m_id_field_size); - ss << to_binary(m_timestamps_supported); - ss << to_binary(m_ts_fixed); - ss << to_binary(m_prescaler_supported); - ss << to_binary(m_selectable_timestamps); - ss << to_binary(m_ts_scale_factor); - ss << to_binary(m_ts_size); - ss << to_binary(m_min_daq); - - std::size_t dl_count = m_daq_lists.size(); - ss << to_binary(dl_count); - for (const auto& daq_list : m_daq_lists) { - ss << daq_list.dumps(); - } - - std::size_t fp_count = m_first_pids.size(); - ss << to_binary(fp_count); - for (const auto& fp : m_first_pids) { - ss << to_binary(fp); - } - - return to_binary(std::size(ss.str())) + ss.str(); - } - - auto get_byte_order() const noexcept { - return m_byte_order; - } - - auto get_id_field_size() const noexcept { - return m_id_field_size; - } - - auto get_timestamps_supported() const noexcept { - return m_timestamps_supported; - } - - auto get_ts_fixed() const noexcept { - return m_ts_fixed; - } - - auto get_prescaler_supported() const noexcept { - return m_prescaler_supported; - } - - auto get_selectable_timestamps() const noexcept { - return m_selectable_timestamps; - } - - auto get_ts_scale_factor() const noexcept { - return m_ts_scale_factor; - } - - auto get_ts_size() const noexcept { - return m_ts_size; - } - - auto get_min_daq() const noexcept { - return m_min_daq; - } - - auto get_daq_lists() const noexcept { - return m_daq_lists; - } - - auto get_first_pids() const noexcept { - return m_first_pids; - } - - std::uint8_t m_byte_order; - std::uint8_t m_id_field_size; - bool m_timestamps_supported; - bool m_ts_fixed; - bool m_prescaler_supported; - bool m_selectable_timestamps; - double m_ts_scale_factor; - std::uint8_t m_ts_size; - std::uint16_t m_min_daq; - std::vector m_daq_lists; - std::vector m_first_pids; -}; - -class Deserializer { - public: - - explicit Deserializer(const std::string& buf) : m_buf(buf) { - } - - MeasurementParameters run() { - std::uint8_t byte_order; - std::uint8_t id_field_size; - bool timestamps_supported; - bool ts_fixed; - bool prescaler_supported; - bool selectable_timestamps; - double ts_scale_factor; - std::uint8_t ts_size; - std::uint16_t min_daq; - std::size_t dl_count; - std::vector daq_lists; - std::size_t fp_count; - std::vector first_pids; - - byte_order = from_binary(); - id_field_size = from_binary(); - timestamps_supported = from_binary(); - ts_fixed = from_binary(); - prescaler_supported = from_binary(); - selectable_timestamps = from_binary(); - ts_scale_factor = from_binary(); - ts_size = from_binary(); - min_daq = from_binary(); - dl_count = from_binary(); - - for (std::size_t i = 0; i < dl_count; i++) { - daq_lists.push_back(create_daq_list()); - } - - fp_count = from_binary(); - for (std::size_t i = 0; i < fp_count; i++) { - first_pids.push_back(from_binary()); - } - - return MeasurementParameters( - byte_order, id_field_size, timestamps_supported, ts_fixed, prescaler_supported, selectable_timestamps, ts_scale_factor, - ts_size, min_daq, daq_lists, first_pids - ); - } - - protected: - - DaqList create_daq_list() { - std::string name; - std::uint16_t event_num; - bool stim; - bool enable_timestamps; - std::vector measurements; - std::vector measurements_opt; - std::vector header_names; - - std::uint16_t odt_count; - std::uint16_t total_entries; - std::uint16_t total_length; - - flatten_odts_t flatten_odts; - - std::vector initializer_list{}; - - name = from_binary_str(); - event_num = from_binary(); - stim = from_binary(); - enable_timestamps = from_binary(); - - odt_count = from_binary(); // not used - total_entries = from_binary(); // not used - total_length = from_binary(); // not used - - std::size_t meas_size = from_binary(); - for (std::size_t i = 0; i < meas_size; ++i) { - // name, address, ext, dt_name - auto meas = create_mc_object(); - measurements.push_back(meas); - initializer_list.push_back({ meas.get_name(), meas.get_address(), meas.get_ext(), meas.get_data_type() }); - } - - std::size_t meas_opt_size = from_binary(); - for (std::size_t i = 0; i < meas_opt_size; ++i) { - measurements_opt.emplace_back(create_bin()); - } - - std::size_t hname_size = from_binary(); - for (std::size_t i = 0; i < hname_size; ++i) { - auto header = from_binary_str(); - header_names.push_back(header); - } - - auto odts = create_flatten_odts(); - - auto result = DaqList(name, event_num, stim, enable_timestamps, initializer_list); - result.set_measurements_opt(measurements_opt); - return result; - } - - flatten_odts_t create_flatten_odts() { - std::string name; - std::uint32_t address; - std::uint8_t ext; - std::uint16_t size; - std::int16_t type_index; - - flatten_odts_t odts; - - std::size_t odt_count = from_binary(); - for (std::size_t i = 0; i < odt_count; ++i) { - std::vector> flatten_odt{}; - std::size_t odt_entry_count = from_binary(); - for (std::size_t j = 0; j < odt_entry_count; ++j) { - name = from_binary_str(); - address = from_binary(); - ext = from_binary(); - size = from_binary(); - type_index = from_binary(); - flatten_odt.push_back(std::make_tuple(name, address, ext, size, type_index)); - } - odts.push_back(flatten_odt); - } - - return odts; - } - - McObject create_mc_object() { - std::string name; - std::uint32_t address; - std::uint8_t ext; - std::uint16_t length; - std::string data_type; - std::int16_t type_index; - std::vector components{}; - - name = from_binary_str(); - address = from_binary(); - ext = from_binary(); - length = from_binary(); - data_type = from_binary_str(); - type_index = from_binary(); // not used - std::size_t comp_size = from_binary(); - for (auto i = 0U; i < comp_size; i++) { - components.push_back(create_mc_object()); - } - - return McObject(name, address, ext, length, data_type, components); - } - - Bin create_bin() { - std::uint16_t size; - std::uint16_t residual_capacity; - std::vector entries{}; - - size = from_binary(); - residual_capacity = from_binary(); - std::size_t entry_size = from_binary(); - for (auto i = 0U; i < entry_size; i++) { - entries.push_back(create_mc_object()); - } - - return Bin(size, residual_capacity, entries); - } - - template - inline T from_binary() { - auto tmp = *reinterpret_cast(&m_buf[m_offset]); - m_offset += sizeof(T); - return tmp; - } - - // template<> - // inline std::string from_binary_str() { - inline std::string from_binary_str() { - auto length = from_binary(); - std::string result; - auto start = m_buf.cbegin() + m_offset; - - std::copy(start, start + length, std::back_inserter(result)); - m_offset += length; - return result; - } - - private: - - std::string m_buf; - std::size_t m_offset = 0; -}; - -class DaqListState { - public: - - enum class state_t : std::uint8_t { - IDLE = 0, - COLLECTING = 1, - FINISHED = 2, - _IGNORE = 3, // Duplicate frame. - _ERROR = 4, // Out-of-order/missing sequence/ODT number. - }; - - DaqListState( - std::uint16_t daq_list_num, std::uint16_t num_odts, std::uint16_t total_entries, bool enable_timestamps, - std::uint16_t initial_offset, const flatten_odts_t& flatten_odts, const Getter& getter, MeasurementParameters params - ) : - m_daq_list_num(daq_list_num), - m_num_odts(num_odts), - m_total_entries(total_entries), - m_enable_timestamps(enable_timestamps), - m_initial_offset(initial_offset), - m_next_odt(0), - m_current_idx(0), - m_timestamp0(0.0), - m_timestamp1(0.0), - m_state(state_t::IDLE), - m_buffer{}, - m_flatten_odts(flatten_odts), - m_getter(getter), - m_params(params) { - m_buffer.resize(m_total_entries); - } - - state_t check_state(uint16_t odt_num) { - if ((m_state == state_t::IDLE) && (odt_num == 0x00)) { - // "synch pulse". - if (m_num_odts == 0x01) { - resetSM(); - return state_t::FINISHED; - } else { - m_state = state_t::COLLECTING; - m_next_odt = 1; - } - } else if (m_state == state_t::COLLECTING) { - if (odt_num == m_next_odt) { - m_next_odt++; - if (m_next_odt == m_num_odts) { - resetSM(); - return state_t::FINISHED; - } - } else { - resetSM(); - return state_t::_ERROR; - } - } - return m_state; - } - - bool feed(uint16_t odt_num, double timestamp, const std::string& payload) { - auto state = check_state(odt_num); - auto finished = false; - - if (state == state_t::COLLECTING) { - m_timestamp0 = timestamp; - parse_Odt(odt_num, payload); - } else if (state == state_t::FINISHED) { - m_timestamp0 = timestamp; - parse_Odt(odt_num, payload); - finished = true; - } - return finished; - } - - void add_result(std::vector& result_buffer) { - result_buffer.emplace_back(m_daq_list_num, m_timestamp0, m_timestamp1, m_buffer); - } - - void add_result(measurement_tuple_t& result_buffer) { - result_buffer = { m_daq_list_num, m_timestamp0, m_timestamp1, m_buffer }; - } - - protected: - - void resetSM() { - m_state = state_t::IDLE; - m_next_odt = 0; - m_timestamp0 = 0.0; - } - - void parse_Odt(uint16_t odt_num, const std::string& payload) { - auto offset = m_initial_offset; // consider ID field size. - auto payload_data = reinterpret_cast(payload.data()); - auto payload_size = std::size(payload); - - if (odt_num == 0) { - m_current_idx = 0; - if (m_params.m_timestamps_supported && - (m_params.m_ts_fixed || (m_params.m_selectable_timestamps && m_enable_timestamps == true))) { - m_timestamp1 = static_cast(m_getter.get_timestamp(payload_data)) * m_params.m_ts_scale_factor; - offset += m_params.m_ts_size; - } else { - m_timestamp1 = 0.0; - } - } - - for (const auto& param : m_flatten_odts[odt_num]) { - const auto& [name, address, ext, size, type_index] = param; - - if (offset >= payload_size) { - throw std::runtime_error( - "Offset is out of range! " + std::to_string(offset) + " >= " + std::to_string(payload_size) - ); - } - - m_buffer[m_current_idx++] = m_getter.reader(type_index, payload_data, offset); - offset += size; - } - } - - private: - - std::uint16_t m_daq_list_num = 0; - std::uint16_t m_num_odts = 0; - std::uint16_t m_total_entries = 0; - bool m_enable_timestamps = false; - std::uint16_t m_initial_offset; - std::uint16_t m_next_odt = 0; - std::uint16_t m_current_idx = 0; - double m_timestamp0 = 0.0; - double m_timestamp1 = 0.0; - state_t m_state = state_t::IDLE; - std::vector m_buffer; - flatten_odts_t m_flatten_odts; - Getter m_getter; - MeasurementParameters m_params; -}; - -auto requires_swap(std::uint8_t byte_order) -> bool { - // INTEL(LITTLE)=0, MOTOROLA(BIG)=1 - std::endian target_byte_order = (byte_order == 1) ? std::endian::big : std::endian::little; - return (target_byte_order != std::endian::native) ? true : false; -} - -class DAQProcessor { - public: - - explicit DAQProcessor(const MeasurementParameters& params) : m_params(params) { - create_state_vars(params); - } - - DAQProcessor() = delete; - virtual ~DAQProcessor() = default; - - std::optional feed(double timestamp, const std::string& payload) noexcept { - const auto data = reinterpret_cast(payload.data()); - auto [daq_num, odt_num] = m_getter.get_id(data); - - if (m_state[daq_num].feed(odt_num, timestamp, payload)) { - // DAQ list completed. - measurement_tuple_t result; - - m_state[daq_num].add_result(result); // get_result()??? - return result; - } - return std::nullopt; - } - - private: - - void create_state_vars(const MeasurementParameters& params) noexcept { - m_getter = Getter(requires_swap(params.m_byte_order), params.m_id_field_size, params.m_ts_size); - for (std::uint16_t idx = 0; idx < params.m_daq_lists.size(); ++idx) { - m_state.emplace_back(DaqListState( - idx, params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), - params.m_daq_lists[idx].get_enable_timestamps(), params.m_id_field_size, params.m_daq_lists[idx].get_flatten_odts(), - m_getter, params - )); - } - m_getter.set_first_pids(m_params.m_daq_lists, m_params.m_first_pids); - } - - MeasurementParameters m_params; - Getter m_getter; - std::map m_first_pids; - std::vector m_state; -}; - -class DAQPolicyBase { - public: - - virtual ~DAQPolicyBase() { - } - - virtual void set_parameters(const MeasurementParameters& params) noexcept { - initialize(); - } - - virtual void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) = 0; - - virtual void initialize() = 0; - - virtual void finalize() = 0; -}; - -class DaqRecorderPolicy : public DAQPolicyBase { - public: - - ~DaqRecorderPolicy() { - finalize(); - } - - DaqRecorderPolicy() = default; - - void set_parameters(const MeasurementParameters& params) noexcept override { - m_params = params; - DAQPolicyBase::set_parameters(params); - } - - void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) override { - if (frame_cat != static_cast(FrameCategory::DAQ)) { - // Only record DAQ frames for now. - return; - } - m_writer->add_frame(frame_cat, counter, timestamp, static_cast(payload.size()), payload.c_str()); - } - - void create_writer(const std::string& file_name, std::uint32_t prealloc, std::uint32_t chunk_size, std::string_view metadata) { - m_writer = std::make_unique(file_name, prealloc, chunk_size, metadata); - } - - void initialize() override { - // TODO: Save meta-data. - } - - void finalize() override { - m_writer->finalize(); - } - - private: - - std::unique_ptr m_writer; - MeasurementParameters m_params; -}; - -class DaqOnlinePolicy : public DAQPolicyBase { - public: - - ~DaqOnlinePolicy() { - } - - DaqOnlinePolicy() = default; - - void set_parameters(const MeasurementParameters& params) noexcept { - m_unfolder = std::make_unique(params); - DAQPolicyBase::set_parameters(params); - } - - virtual void on_daq_list( - std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement - ) = 0; - - void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) { - if (frame_cat != static_cast(FrameCategory::DAQ)) { - return; - } - auto result = m_unfolder->feed(timestamp, payload); - if (result) { - const auto& [daq_list, ts0, ts1, meas] = *result; - on_daq_list(daq_list, ts0, ts1, meas); - } - } - - virtual void initialize() { - } - - virtual void finalize() { - } - - private: - - std::unique_ptr m_unfolder; -}; - -class XcpLogFileUnfolder { - public: - - explicit XcpLogFileUnfolder(const std::string& file_name) : m_reader(file_name) { - auto metadata = m_reader.get_metadata(); - if (metadata != "") { - auto des = Deserializer(metadata); - m_params = des.run(); - m_unfolder = std::make_unique(m_params); - } else { - // cannot proceed!!! - } - } - - XcpLogFileUnfolder() = delete; - virtual ~XcpLogFileUnfolder() = default; - - void run() { - const auto converter = [](const blob_t* in_str, std::size_t length) -> std::string { - std::string result; - result.resize(length); - - for (std::size_t idx = 0; idx < length; ++idx) { - result[idx] = static_cast(in_str[idx]); - } - - return result; - }; - - while (true) { - const auto& block = m_reader.next_block(); - if (!block) { - return; - } - - for (const auto& [frame_cat, counter, timestamp, length, payload] : block.value()) { - auto str_data = converter(payload.data(), std::size(payload)); - if (frame_cat != static_cast(FrameCategory::DAQ)) { - continue; - } - auto result = m_unfolder->feed(timestamp, str_data); - if (result) { - const auto& [daq_list, ts0, ts1, meas] = *result; - on_daq_list(daq_list, ts0, ts1, meas); - } - } - } - } - - virtual void on_daq_list( - std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement - ) = 0; - - MeasurementParameters get_parameters() const { - return m_params; - } - - auto get_daq_lists() const { - return m_params.m_daq_lists; - } - - auto get_header() const { - return m_reader.get_header(); - } - - private: - - XcpLogFileReader m_reader; - std::unique_ptr m_unfolder; - MeasurementParameters m_params; -}; - -#endif // RECORDER_UNFOLDER_HPP + +#ifndef RECORDER_UNFOLDER_HPP +#define RECORDER_UNFOLDER_HPP + +#include +#include +#include +#include +#include +#include + +#include "daqlist.hpp" +#include "helper.hpp" +#include "mcobject.hpp" +#include "writer.hpp" + +using measurement_value_t = std::variant; +using measurement_tuple_t = std::tuple>; +using measurement_callback_t = std::function)>; + +template +auto get_value(blob_t const * buf, std::uint64_t offset) -> Ty { + return *reinterpret_cast(&buf[offset]); +} + +template +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> Ty { + return _bswap(get_value(buf, offset)); +} + +#if HAS_FLOAT16 == 1 +template<> +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::float16_t { + auto tmp = get_value(buf, offset); + + return std::bit_cast(tmp); +} +#endif + +#if HAS_BFLOAT16 == 1 +template<> +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { + auto tmp = get_value(buf, offset); + + return std::bit_cast(tmp); +} +#endif + +template<> +auto get_value(blob_t const * buf, std::uint64_t offset) -> float { + auto tmp = get_value(buf, offset); + + return std::bit_cast(tmp); +} + +template<> +auto get_value(blob_t const * buf, std::uint64_t offset) -> double { + auto tmp = get_value(buf, offset); + + return std::bit_cast(tmp); +} + +#if HAS_FLOAT16 == 1 +template<> +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::float16_t { + auto tmp = get_value_swapped(buf, offset); + + return std::bit_cast(tmp); +} +#endif + +#if HAS_BFLOAT16 == 1 +template<> +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::bfloat16_t { + auto tmp = get_value_swapped(buf, offset); + + return std::bit_cast(tmp); +} +#endif + +template<> +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> float { + auto tmp = get_value_swapped(buf, offset); + + return std::bit_cast(tmp); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> double { + auto tmp = get_value_swapped(buf, offset); + + return std::bit_cast(tmp); +} + +template<> +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::int16_t { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::int16_t { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::int32_t { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::int32_t { + return static_cast(get_value_swapped(buf, offset)); +} + +template<> +auto get_value(blob_t const * buf, std::uint64_t offset) -> std::int64_t { + return static_cast(get_value(buf, offset)); +} + +template<> +auto get_value_swapped(blob_t const * buf, std::uint64_t offset) -> std::int64_t { + return static_cast(get_value_swapped(buf, offset)); +} + +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// +template +void set_value(blob_t* buf, std::uint64_t offset, Ty value) { + ::memcpy(&buf[offset], &value, sizeof(Ty)); +} + +template +void set_value_swapped(blob_t* buf, std::uint64_t offset, Ty value) { + set_value(buf, offset, _bswap(value)); +} + +template<> +void set_value(blob_t* buf, std::uint64_t offset, std::int8_t value) { + buf[offset] = static_cast(value); +} + +template<> +void set_value(blob_t* buf, std::uint64_t offset, std::uint8_t value) { + buf[offset] = static_cast(value); +} + +template<> +void set_value(blob_t* buf, std::uint64_t offset, std::int16_t value) { + set_value(buf, offset, static_cast(value)); +} + +template<> +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int16_t value) { + set_value_swapped(buf, offset, static_cast(value)); +} + +template<> +void set_value(blob_t* buf, std::uint64_t offset, std::int32_t value) { + set_value(buf, offset, static_cast(value)); +} + +template<> +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int32_t value) { + set_value_swapped(buf, offset, static_cast(value)); +} + +template<> +void set_value(blob_t* buf, std::uint64_t offset, std::int64_t value) { + set_value(buf, offset, static_cast(value)); +} + +template<> +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::int64_t value) { + set_value_swapped(buf, offset, static_cast(value)); +} + +#if HAS_FLOAT16 == 1 +template<> +void set_value(blob_t* buf, std::uint64_t offset, std::float16_t value) { + // set_value(buf, offset, *reinterpret_cast(&value)); + set_value(buf, offset, std::bit_cast(value)); +} + +template<> +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::float16_t value) { + // set_value_swapped(buf, offset, *reinterpret_cast(&value)); + set_value_swapped(buf, offset, std::bit_cast(value)); +} +#endif + +#if HAS_BFLOAT16 == 1 +template<> +void set_value(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { + // set_value(buf, offset, *reinterpret_cast(&value)); + set_value(buf, offset, std::bit_cast(value)); +} + +template<> +void set_value_swapped(blob_t* buf, std::uint64_t offset, std::bfloat16_t value) { + // set_value_swapped(buf, offset, *reinterpret_cast(&value)); + set_value_swapped(buf, offset, std::bit_cast(value)); +} +#endif + +template<> +void set_value(blob_t* buf, std::uint64_t offset, float value) { + // set_value(buf, offset, *reinterpret_cast(&value)); + set_value(buf, offset, std::bit_cast(value)); +} + +template<> +void set_value_swapped(blob_t* buf, std::uint64_t offset, float value) { + // set_value_swapped(buf, offset, *reinterpret_cast(&value)); + set_value_swapped(buf, offset, std::bit_cast(value)); +} + +template<> +void set_value(blob_t* buf, std::uint64_t offset, double value) { + // set_value(buf, offset, *reinterpret_cast(&value)); + set_value(buf, offset, std::bit_cast(value)); +} + +template<> +void set_value_swapped(blob_t* buf, std::uint64_t offset, double value) { + // set_value_swapped(buf, offset, *reinterpret_cast(&value)); + set_value_swapped(buf, offset, std::bit_cast(value)); +} + +/* +** Get primitive datatypes, consider byte-order. +*/ +struct Getter { + Getter() = default; + + explicit Getter(bool requires_swap, std::uint8_t id_size, std::uint8_t ts_size) : m_id_size(id_size), m_ts_size(ts_size) { + int8 = get_value; + uint8 = get_value; + + if (requires_swap) { + int16 = get_value_swapped; + int32 = get_value_swapped; + int64 = get_value_swapped; + uint16 = get_value_swapped; + uint32 = get_value_swapped; + uint64 = get_value_swapped; + float_ = get_value_swapped; + double_ = get_value_swapped; +#if HAS_FLOAT16 == 1 + float16 = get_value_swapped; +#endif +#if HAS_BFLOAT16 == 1 + bfloat16 = get_value_swapped; +#endif + } else { + int16 = get_value; + int32 = get_value; + int64 = get_value; + uint16 = get_value; + uint32 = get_value; + uint64 = get_value; + float_ = get_value; + double_ = get_value; +#if HAS_FLOAT16 == 1 + float16 = get_value; +#endif +#if HAS_BFLOAT16 == 1 + bfloat16 = get_value; +#endif + } + } + + std::uint32_t get_timestamp(blob_t const * buf) { + switch (m_ts_size) { + case 0: + return 0; + case 1: + return uint8(buf, m_id_size); + case 2: + return uint16(buf, m_id_size); + case 4: + return uint32(buf, m_id_size); + default: + throw std::runtime_error("Unsupported timestamp size: " + std::to_string(m_ts_size)); + } + } + + measurement_value_t reader(std::uint16_t tp, blob_t const * buf, std::uint16_t offset) const { + switch (tp) { + case 0: + return static_cast(uint8(buf, offset)); + case 1: + return int8(buf, offset); + case 2: + return static_cast(uint16(buf, offset)); + case 3: + return int16(buf, offset); + case 4: + return static_cast(uint32(buf, offset)); + case 5: + return int32(buf, offset); + case 6: + return uint64(buf, offset); + case 7: + return int64(buf, offset); + case 8: + return float_(buf, offset); + case 9: + return double_(buf, offset); +#if HAS_FLOAT16 == 1 + case 10: + return float16(buf, offset); +#endif +#if HAS_BFLOAT16 == 1 + case 11: + return bfloat16(buf, offset); +#endif + default: + throw std::runtime_error("Unsupported data type: " + std::to_string(tp)); + } + } + + void set_first_pids(const std::vector& daq_lists, const std::vector& first_pids) { + m_first_pids = first_pids; + + if (m_id_size == 1) { + // In case of 1-byte ID field (absolute ODT number) we need a mapping. + std::uint16_t daq_list_num = 0; + for (const auto& daq_list : daq_lists) { + auto first_pid = m_first_pids[daq_list_num]; + + for (std::uint16_t idx = first_pid; idx < daq_list.get_odt_count() + first_pid; ++idx) { + m_odt_to_daq_map[idx] = { daq_list_num, (idx - first_pid) }; + } + daq_list_num++; + } + } + } + + std::tuple get_id(blob_t const * buf) { + std::uint16_t odt_num = 0; + + switch (m_id_size) { + case 1: + odt_num = uint8(buf, 0); // Get 1-byte ODT number... + return m_odt_to_daq_map[odt_num]; // ...and return mapped values. + case 2: + return { uint8(buf, 1), uint8(buf, 0) }; + case 3: + return { uint16(buf, 1), uint8(buf, 0) }; + case 4: + return { uint16(buf, 2), uint8(buf, 0) }; + default: + throw std::runtime_error("Unsupported ID size: " + std::to_string(m_id_size)); + } + } + + std::uint8_t m_id_size; + std::uint8_t m_ts_size; + std::function int8; + std::function uint8; + std::function int16; + std::function int32; + std::function int64; + std::function uint16; + std::function uint32; + std::function uint64; + std::function float_; + std::function double_; +#if HAS_FLOAT16 == 1 + std::function float16; +#endif +#if HAS_BFLOAT16 == 1 + std::function bfloat16; +#endif + std::vector m_first_pids; + std::map> m_odt_to_daq_map; +}; + +////////////////////////////////////////////////////////////////////////////////////////////// +struct Setter { + Setter() = default; + + explicit Setter(bool requires_swap, std::uint8_t id_size, std::uint8_t ts_size) : m_id_size(id_size), m_ts_size(ts_size) { + int8 = set_value; + uint8 = set_value; + + if (requires_swap) { + int16 = set_value_swapped; + int32 = set_value_swapped; + int64 = set_value_swapped; + uint16 = set_value_swapped; + uint32 = set_value_swapped; + uint64 = set_value_swapped; + float_ = set_value_swapped; + double_ = set_value_swapped; +#if HAS_FLOAT16 == 1 + float16 = set_value_swapped; +#endif +#if HAS_BFLOAT16 == 1 + bfloat16 = set_value_swapped; +#endif + } else { + int16 = set_value; + int32 = set_value; + int64 = set_value; + uint16 = set_value; + uint32 = set_value; + uint64 = set_value; + float_ = set_value; + double_ = set_value; +#if HAS_FLOAT16 == 1 + float16 = set_value; +#endif +#if HAS_BFLOAT16 == 1 + bfloat16 = set_value; +#endif + } + } + + void set_timestamp(blob_t* buf, std::uint32_t timestamp) { + switch (m_ts_size) { + case 0: + break; + case 1: + uint8(buf, m_id_size, timestamp); + break; + case 2: + uint16(buf, m_id_size, timestamp); + break; + case 4: + uint32(buf, m_id_size, timestamp); + break; + default: + throw std::runtime_error("Unsupported timestamp size: " + std::to_string(m_ts_size)); + } + } + + void writer(std::uint16_t tp, blob_t* buf, std::uint16_t offset, const measurement_value_t& value) { + switch (tp) { + case 0: + uint8(buf, offset, static_cast(std::get(value))); + break; + case 1: + int8(buf, offset, static_cast(std::get(value))); + break; + case 2: + uint16(buf, offset, static_cast(std::get(value))); + break; + case 3: + int16(buf, offset, static_cast(std::get(value))); + break; + case 4: + uint32(buf, offset, static_cast(std::get(value))); + break; + case 5: + int32(buf, offset, static_cast(std::get(value))); + break; + case 6: + uint64(buf, offset, std::get(value)); + break; + case 7: + int64(buf, offset, std::get(value)); + break; + case 8: + float_(buf, offset, static_cast(std::get(value))); + break; + case 9: + double_(buf, offset, static_cast(std::get(value))); + break; +#if HAS_FLOAT16 == 1 + case 10: + float16(buf, offset, static_cast(std::get(value))); + break; +#endif +#if HAS_BFLOAT16 == 1 + case 11: + bfloat16(buf, offset, static_cast(std::get(value))); + break; +#endif + default: + throw std::runtime_error("Unsupported data type: " + std::to_string(tp)); + } + } + +#if 0 + std::tuple set_id(blob_t const * buf) { + std::uint16_t odt_num = 0; + + switch (m_id_size) { + case 1: + odt_num = uint8(buf, 0); // Get 1-byte ODT number... + return m_odt_to_daq_map[odt_num]; // ...and return mapped values. + case 2: + return { uint8(buf, 1), uint8(buf, 0) }; + case 3: + return { uint16(buf, 1), uint8(buf, 0) }; + case 4: + return { uint16(buf, 2), uint8(buf, 0) }; + default: + throw std::runtime_error("Unsupported ID size: " + std::to_string(m_id_size)); + } + } +#endif + std::uint8_t m_id_size; + std::uint8_t m_ts_size; + std::function int8; + std::function uint8; + std::function int16; + std::function int32; + std::function int64; + std::function uint16; + std::function uint32; + std::function uint64; + std::function float_; + std::function double_; +#if HAS_FLOAT16 == 1 + std::function float16; +#endif +#if HAS_BFLOAT16 == 1 + std::function bfloat16; +#endif + std::map> m_odt_to_daq_map; +}; + +////////////////////////////////////////////////////////////////////////////////////////////// + +struct MeasurementParameters { + MeasurementParameters() = default; + + MeasurementParameters(MeasurementParameters&&) = default; + MeasurementParameters(const MeasurementParameters&) = default; + MeasurementParameters& operator=(MeasurementParameters&&) = default; + MeasurementParameters& operator=(const MeasurementParameters&) = default; + + explicit MeasurementParameters( + std::uint8_t byte_order, std::uint8_t id_field_size, bool timestamps_supported, bool ts_fixed, bool prescaler_supported, + bool selectable_timestamps, double ts_scale_factor, std::uint8_t ts_size, std::uint16_t min_daq, + const std::vector& daq_lists, const std::vector& first_pids + ) : + m_byte_order(byte_order), + m_id_field_size(id_field_size), + m_timestamps_supported(timestamps_supported), + m_ts_fixed(ts_fixed), + m_prescaler_supported(prescaler_supported), + m_selectable_timestamps(selectable_timestamps), + m_ts_scale_factor(ts_scale_factor), + m_ts_size(ts_size), + m_min_daq(min_daq), + m_daq_lists(daq_lists), + m_first_pids(first_pids) { + } + + std::string dumps() const { + std::stringstream ss; + + ss << to_binary(m_byte_order); + ss << to_binary(m_id_field_size); + ss << to_binary(m_timestamps_supported); + ss << to_binary(m_ts_fixed); + ss << to_binary(m_prescaler_supported); + ss << to_binary(m_selectable_timestamps); + ss << to_binary(m_ts_scale_factor); + ss << to_binary(m_ts_size); + ss << to_binary(m_min_daq); + + std::size_t dl_count = m_daq_lists.size(); + ss << to_binary(dl_count); + for (const auto& daq_list : m_daq_lists) { + ss << daq_list.dumps(); + } + + std::size_t fp_count = m_first_pids.size(); + ss << to_binary(fp_count); + for (const auto& fp : m_first_pids) { + ss << to_binary(fp); + } + + return to_binary(std::size(ss.str())) + ss.str(); + } + + auto get_byte_order() const noexcept { + return m_byte_order; + } + + auto get_id_field_size() const noexcept { + return m_id_field_size; + } + + auto get_timestamps_supported() const noexcept { + return m_timestamps_supported; + } + + auto get_ts_fixed() const noexcept { + return m_ts_fixed; + } + + auto get_prescaler_supported() const noexcept { + return m_prescaler_supported; + } + + auto get_selectable_timestamps() const noexcept { + return m_selectable_timestamps; + } + + auto get_ts_scale_factor() const noexcept { + return m_ts_scale_factor; + } + + auto get_ts_size() const noexcept { + return m_ts_size; + } + + auto get_min_daq() const noexcept { + return m_min_daq; + } + + auto get_daq_lists() const noexcept { + return m_daq_lists; + } + + auto get_first_pids() const noexcept { + return m_first_pids; + } + + std::uint8_t m_byte_order; + std::uint8_t m_id_field_size; + bool m_timestamps_supported; + bool m_ts_fixed; + bool m_prescaler_supported; + bool m_selectable_timestamps; + double m_ts_scale_factor; + std::uint8_t m_ts_size; + std::uint16_t m_min_daq; + std::vector m_daq_lists; + std::vector m_first_pids; +}; + +class Deserializer { + public: + + explicit Deserializer(const std::string& buf) : m_buf(buf) { + } + + MeasurementParameters run() { + std::uint8_t byte_order; + std::uint8_t id_field_size; + bool timestamps_supported; + bool ts_fixed; + bool prescaler_supported; + bool selectable_timestamps; + double ts_scale_factor; + std::uint8_t ts_size; + std::uint16_t min_daq; + std::size_t dl_count; + std::vector daq_lists; + std::size_t fp_count; + std::vector first_pids; + + byte_order = from_binary(); + id_field_size = from_binary(); + timestamps_supported = from_binary(); + ts_fixed = from_binary(); + prescaler_supported = from_binary(); + selectable_timestamps = from_binary(); + ts_scale_factor = from_binary(); + ts_size = from_binary(); + min_daq = from_binary(); + dl_count = from_binary(); + + for (std::size_t i = 0; i < dl_count; i++) { + daq_lists.push_back(create_daq_list()); + } + + fp_count = from_binary(); + for (std::size_t i = 0; i < fp_count; i++) { + first_pids.push_back(from_binary()); + } + + return MeasurementParameters( + byte_order, id_field_size, timestamps_supported, ts_fixed, prescaler_supported, selectable_timestamps, ts_scale_factor, + ts_size, min_daq, daq_lists, first_pids + ); + } + + protected: + + DaqList create_daq_list() { + std::string name; + std::uint16_t event_num; + bool stim; + bool enable_timestamps; + std::vector measurements; + std::vector measurements_opt; + std::vector header_names; + + std::uint16_t odt_count; + std::uint16_t total_entries; + std::uint16_t total_length; + + flatten_odts_t flatten_odts; + + std::vector initializer_list{}; + + name = from_binary_str(); + event_num = from_binary(); + stim = from_binary(); + enable_timestamps = from_binary(); + + odt_count = from_binary(); // not used + total_entries = from_binary(); // not used + total_length = from_binary(); // not used + + std::size_t meas_size = from_binary(); + for (std::size_t i = 0; i < meas_size; ++i) { + // name, address, ext, dt_name + auto meas = create_mc_object(); + measurements.push_back(meas); + initializer_list.push_back({ meas.get_name(), meas.get_address(), meas.get_ext(), meas.get_data_type() }); + } + + std::size_t meas_opt_size = from_binary(); + for (std::size_t i = 0; i < meas_opt_size; ++i) { + measurements_opt.emplace_back(create_bin()); + } + + std::size_t hname_size = from_binary(); + for (std::size_t i = 0; i < hname_size; ++i) { + auto header = from_binary_str(); + header_names.push_back(header); + } + + auto odts = create_flatten_odts(); + + auto result = DaqList(name, event_num, stim, enable_timestamps, initializer_list); + result.set_measurements_opt(measurements_opt); + return result; + } + + flatten_odts_t create_flatten_odts() { + std::string name; + std::uint32_t address; + std::uint8_t ext; + std::uint16_t size; + std::int16_t type_index; + + flatten_odts_t odts; + + std::size_t odt_count = from_binary(); + for (std::size_t i = 0; i < odt_count; ++i) { + std::vector> flatten_odt{}; + std::size_t odt_entry_count = from_binary(); + for (std::size_t j = 0; j < odt_entry_count; ++j) { + name = from_binary_str(); + address = from_binary(); + ext = from_binary(); + size = from_binary(); + type_index = from_binary(); + flatten_odt.push_back(std::make_tuple(name, address, ext, size, type_index)); + } + odts.push_back(flatten_odt); + } + + return odts; + } + + McObject create_mc_object() { + std::string name; + std::uint32_t address; + std::uint8_t ext; + std::uint16_t length; + std::string data_type; + std::int16_t type_index; + std::vector components{}; + + name = from_binary_str(); + address = from_binary(); + ext = from_binary(); + length = from_binary(); + data_type = from_binary_str(); + type_index = from_binary(); // not used + std::size_t comp_size = from_binary(); + for (auto i = 0U; i < comp_size; i++) { + components.push_back(create_mc_object()); + } + + return McObject(name, address, ext, length, data_type, components); + } + + Bin create_bin() { + std::uint16_t size; + std::uint16_t residual_capacity; + std::vector entries{}; + + size = from_binary(); + residual_capacity = from_binary(); + std::size_t entry_size = from_binary(); + for (auto i = 0U; i < entry_size; i++) { + entries.push_back(create_mc_object()); + } + + return Bin(size, residual_capacity, entries); + } + + template + inline T from_binary() { + auto tmp = *reinterpret_cast(&m_buf[m_offset]); + m_offset += sizeof(T); + return tmp; + } + + // template<> + // inline std::string from_binary_str() { + inline std::string from_binary_str() { + auto length = from_binary(); + std::string result; + auto start = m_buf.cbegin() + m_offset; + + std::copy(start, start + length, std::back_inserter(result)); + m_offset += length; + return result; + } + + private: + + std::string m_buf; + std::size_t m_offset = 0; +}; + +class DaqListState { + public: + + enum class state_t : std::uint8_t { + IDLE = 0, + COLLECTING = 1, + FINISHED = 2, + _IGNORE = 3, // Duplicate frame. + _ERROR = 4, // Out-of-order/missing sequence/ODT number. + }; + + DaqListState( + std::uint16_t daq_list_num, std::uint16_t num_odts, std::uint16_t total_entries, bool enable_timestamps, + std::uint16_t initial_offset, const flatten_odts_t& flatten_odts, const Getter& getter, MeasurementParameters params + ) : + m_daq_list_num(daq_list_num), + m_num_odts(num_odts), + m_total_entries(total_entries), + m_enable_timestamps(enable_timestamps), + m_initial_offset(initial_offset), + m_next_odt(0), + m_current_idx(0), + m_timestamp0(0.0), + m_timestamp1(0.0), + m_state(state_t::IDLE), + m_buffer{}, + m_flatten_odts(flatten_odts), + m_getter(getter), + m_params(params) { + m_buffer.resize(m_total_entries); + } + + state_t check_state(uint16_t odt_num) { + if ((m_state == state_t::IDLE) && (odt_num == 0x00)) { + // "synch pulse". + if (m_num_odts == 0x01) { + resetSM(); + return state_t::FINISHED; + } else { + m_state = state_t::COLLECTING; + m_next_odt = 1; + } + } else if (m_state == state_t::COLLECTING) { + if (odt_num == m_next_odt) { + m_next_odt++; + if (m_next_odt == m_num_odts) { + resetSM(); + return state_t::FINISHED; + } + } else { + resetSM(); + return state_t::_ERROR; + } + } + return m_state; + } + + bool feed(uint16_t odt_num, double timestamp, const std::string& payload) { + auto state = check_state(odt_num); + auto finished = false; + + if (state == state_t::COLLECTING) { + m_timestamp0 = timestamp; + parse_Odt(odt_num, payload); + } else if (state == state_t::FINISHED) { + m_timestamp0 = timestamp; + parse_Odt(odt_num, payload); + finished = true; + } + return finished; + } + + void add_result(std::vector& result_buffer) { + result_buffer.emplace_back(m_daq_list_num, m_timestamp0, m_timestamp1, m_buffer); + } + + void add_result(measurement_tuple_t& result_buffer) { + result_buffer = { m_daq_list_num, m_timestamp0, m_timestamp1, m_buffer }; + } + + protected: + + void resetSM() { + m_state = state_t::IDLE; + m_next_odt = 0; + m_timestamp0 = 0.0; + } + + void parse_Odt(uint16_t odt_num, const std::string& payload) { + auto offset = m_initial_offset; // consider ID field size. + auto payload_data = reinterpret_cast(payload.data()); + auto payload_size = std::size(payload); + + if (odt_num == 0) { + m_current_idx = 0; + if (m_params.m_timestamps_supported && + (m_params.m_ts_fixed || (m_params.m_selectable_timestamps && m_enable_timestamps == true))) { + m_timestamp1 = static_cast(m_getter.get_timestamp(payload_data)) * m_params.m_ts_scale_factor; + offset += m_params.m_ts_size; + } else { + m_timestamp1 = 0.0; + } + } + + for (const auto& param : m_flatten_odts[odt_num]) { + const auto& [name, address, ext, size, type_index] = param; + + if (offset >= payload_size) { + throw std::runtime_error( + "Offset is out of range! " + std::to_string(offset) + " >= " + std::to_string(payload_size) + ); + } + + m_buffer[m_current_idx++] = m_getter.reader(type_index, payload_data, offset); + offset += size; + } + } + + private: + + std::uint16_t m_daq_list_num = 0; + std::uint16_t m_num_odts = 0; + std::uint16_t m_total_entries = 0; + bool m_enable_timestamps = false; + std::uint16_t m_initial_offset; + std::uint16_t m_next_odt = 0; + std::uint16_t m_current_idx = 0; + double m_timestamp0 = 0.0; + double m_timestamp1 = 0.0; + state_t m_state = state_t::IDLE; + std::vector m_buffer; + flatten_odts_t m_flatten_odts; + Getter m_getter; + MeasurementParameters m_params; +}; + +auto requires_swap(std::uint8_t byte_order) -> bool { + // INTEL(LITTLE)=0, MOTOROLA(BIG)=1 + std::endian target_byte_order = (byte_order == 1) ? std::endian::big : std::endian::little; + return (target_byte_order != std::endian::native) ? true : false; +} + +class DAQProcessor { + public: + + explicit DAQProcessor(const MeasurementParameters& params) : m_params(params) { + create_state_vars(params); + } + + DAQProcessor() = delete; + virtual ~DAQProcessor() = default; + + std::optional feed(double timestamp, const std::string& payload) noexcept { + const auto data = reinterpret_cast(payload.data()); + auto [daq_num, odt_num] = m_getter.get_id(data); + + if (m_state[daq_num].feed(odt_num, timestamp, payload)) { + // DAQ list completed. + measurement_tuple_t result; + + m_state[daq_num].add_result(result); // get_result()??? + return result; + } + return std::nullopt; + } + + private: + + void create_state_vars(const MeasurementParameters& params) noexcept { + m_getter = Getter(requires_swap(params.m_byte_order), params.m_id_field_size, params.m_ts_size); + for (std::uint16_t idx = 0; idx < params.m_daq_lists.size(); ++idx) { + m_state.emplace_back(DaqListState( + idx, params.m_daq_lists[idx].get_odt_count(), params.m_daq_lists[idx].get_total_entries(), + params.m_daq_lists[idx].get_enable_timestamps(), params.m_id_field_size, params.m_daq_lists[idx].get_flatten_odts(), + m_getter, params + )); + } + m_getter.set_first_pids(m_params.m_daq_lists, m_params.m_first_pids); + } + + MeasurementParameters m_params; + Getter m_getter; + std::map m_first_pids; + std::vector m_state; +}; + +class DAQPolicyBase { + public: + + virtual ~DAQPolicyBase() { + } + + virtual void set_parameters(const MeasurementParameters& params) noexcept { + initialize(); + } + + virtual void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) = 0; + + virtual void initialize() = 0; + + virtual void finalize() = 0; +}; + +class DaqRecorderPolicy : public DAQPolicyBase { + public: + + ~DaqRecorderPolicy() { + finalize(); + } + + DaqRecorderPolicy() = default; + + void set_parameters(const MeasurementParameters& params) noexcept override { + m_params = params; + DAQPolicyBase::set_parameters(params); + } + + void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) override { + if (frame_cat != static_cast(FrameCategory::DAQ)) { + // Only record DAQ frames for now. + return; + } + m_writer->add_frame(frame_cat, counter, timestamp, static_cast(payload.size()), payload.c_str()); + } + + void create_writer(const std::string& file_name, std::uint32_t prealloc, std::uint32_t chunk_size, std::string_view metadata) { + m_writer = std::make_unique(file_name, prealloc, chunk_size, metadata); + } + + void initialize() override { + // TODO: Save meta-data. + } + + void finalize() override { + m_writer->finalize(); + } + + private: + + std::unique_ptr m_writer; + MeasurementParameters m_params; +}; + +class DaqOnlinePolicy : public DAQPolicyBase { + public: + + ~DaqOnlinePolicy() { + } + + DaqOnlinePolicy() = default; + + void set_parameters(const MeasurementParameters& params) noexcept { + m_unfolder = std::make_unique(params); + DAQPolicyBase::set_parameters(params); + } + + virtual void on_daq_list( + std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + ) = 0; + + void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) { + if (frame_cat != static_cast(FrameCategory::DAQ)) { + return; + } + auto result = m_unfolder->feed(timestamp, payload); + if (result) { + const auto& [daq_list, ts0, ts1, meas] = *result; + on_daq_list(daq_list, ts0, ts1, meas); + } + } + + virtual void initialize() { + } + + virtual void finalize() { + } + + private: + + std::unique_ptr m_unfolder; +}; + +class XcpLogFileUnfolder { + public: + + explicit XcpLogFileUnfolder(const std::string& file_name) : m_reader(file_name) { + auto metadata = m_reader.get_metadata(); + if (metadata != "") { + auto des = Deserializer(metadata); + m_params = des.run(); + m_unfolder = std::make_unique(m_params); + } else { + // cannot proceed!!! + } + } + + XcpLogFileUnfolder() = delete; + virtual ~XcpLogFileUnfolder() = default; + + void run() { + const auto converter = [](const blob_t* in_str, std::size_t length) -> std::string { + std::string result; + result.resize(length); + + for (std::size_t idx = 0; idx < length; ++idx) { + result[idx] = static_cast(in_str[idx]); + } + + return result; + }; + + while (true) { + const auto& block = m_reader.next_block(); + if (!block) { + return; + } + + for (const auto& [frame_cat, counter, timestamp, length, payload] : block.value()) { + auto str_data = converter(payload.data(), std::size(payload)); + if (frame_cat != static_cast(FrameCategory::DAQ)) { + continue; + } + auto result = m_unfolder->feed(timestamp, str_data); + if (result) { + const auto& [daq_list, ts0, ts1, meas] = *result; + on_daq_list(daq_list, ts0, ts1, meas); + } + } + } + } + + virtual void on_daq_list( + std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + ) = 0; + + MeasurementParameters get_parameters() const { + return m_params; + } + + auto get_daq_lists() const { + return m_params.m_daq_lists; + } + + auto get_header() const { + return m_reader.get_header(); + } + + private: + + XcpLogFileReader m_reader; + std::unique_ptr m_unfolder; + MeasurementParameters m_params; +}; + +#endif // RECORDER_UNFOLDER_HPP diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 6e6c937..bf3b30e 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -1,176 +1,176 @@ - -#include -#include -#include -#include - -#include - -#include "rekorder.hpp" - -namespace py = pybind11; -using namespace pybind11::literals; - -class PyDaqOnlinePolicy : public DaqOnlinePolicy { - public: - - using DaqOnlinePolicy::DaqOnlinePolicy; - - void on_daq_list( - std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement - ) override { - PYBIND11_OVERRIDE_PURE(void, DaqOnlinePolicy, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); - } - - void initialize() override { - PYBIND11_OVERRIDE(void, DaqOnlinePolicy, initialize); - } - - void finalize() override { - PYBIND11_OVERRIDE(void, DaqOnlinePolicy, finalize); - } -}; - -class PyDaqRecorderPolicy : public DaqRecorderPolicy { - public: - - using DaqRecorderPolicy::DaqRecorderPolicy; - - void initialize() override { - PYBIND11_OVERRIDE(void, DaqRecorderPolicy, initialize); - } - - void finalize() override { - PYBIND11_OVERRIDE(void, DaqRecorderPolicy, finalize); - } -}; - -class PyXcpLogFileUnfolder : public XcpLogFileUnfolder { - public: - - using XcpLogFileUnfolder::XcpLogFileUnfolder; - - void on_daq_list( - std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement - ) override { - PYBIND11_OVERRIDE_PURE(void, XcpLogFileUnfolder, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); - } -}; - -PYBIND11_MODULE(rekorder, m) { - m.doc() = "XCP raw frame recorder."; - m.def("data_types", get_data_types); - -#if 0 - version; - options; - num_containers; - record_count; - size_compressed; - size_uncompressed; -#endif - - py::class_(m, "FileHeaderType") - .def(py::init()) - .def("__repr__", [](const FileHeaderType& self) { - std::stringstream ss; - ss << "FileHeaderType(" << std::endl; - ss << " hdr_size=" << self.hdr_size << "," << std::endl; - ss << " version=" << self.version << "," << std::endl; - ss << " options=" << self.options << "," << std::endl; - ss << " num_containers=" << self.num_containers << "," << std::endl; - ss << " record_count=" << self.record_count << "," << std::endl; - ss << " size_compressed=" << self.size_compressed << "," << std::endl; - ss << " size_uncompressed=" << self.size_uncompressed << "," << std::endl; - ss << " compression_ratio=" - << (double)((std::uint64_t)(((double)self.size_uncompressed / (double)self.size_compressed * 100.0) + 0.5)) / 100.0 - << std::endl; - ss << ")" << std::endl; - return ss.str(); - }); - - py::class_(m, "Deserializer").def(py::init()).def("run", &Deserializer::run); - - py::class_(m, "_PyXcpLogFileReader") - .def(py::init()) - .def("next_block", &XcpLogFileReader::next_block) - .def("reset", &XcpLogFileReader::reset) - .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple) - .def("get_metadata", [](const XcpLogFileReader& self) { return py::bytes(self.get_metadata()); }); - - py::class_(m, "_PyXcpLogFileWriter") - .def( - py::init(), py::arg("filename"), - py::arg("prealloc"), py::arg("chunk_size"), py::arg("metadata") = "" - ) - .def("finalize", &XcpLogFileWriter::finalize) - .def("add_frame", &XcpLogFileWriter::add_frame); - - py::class_(m, "MeasurementParameters") - .def(py::init< - std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector&, - const std::vector&>()) - .def("dumps", [](const MeasurementParameters& self) { return py::bytes(self.dumps()); }) - .def( - "__repr__", - [](const MeasurementParameters& self) { - std::stringstream ss; - ss << "MeasurementParameters("; - ss << "byte_order=\"" << byte_order_to_string(self.m_byte_order) << "\", "; - ss << "id_field_size=" << static_cast(self.m_id_field_size) << ", "; - ss << "timestamps_supported=" << bool_to_string(self.m_timestamps_supported) << ", "; - ss << "ts_fixed=" << bool_to_string(self.m_ts_fixed) << ", "; - ss << "prescaler_supported=" << bool_to_string(self.m_prescaler_supported) << ", "; - ss << "selectable_timestamps=" << bool_to_string(self.m_selectable_timestamps) << ", "; - ss << "ts_scale_factor=" << self.m_ts_scale_factor << ", "; - ss << "ts_size=" << static_cast(self.m_ts_size) << ", "; - ss << "min_daq=" << static_cast(self.m_min_daq) << ", "; - ss << "daq_lists=[\n"; - for (const auto& dl : self.m_daq_lists) { - ss << dl.to_string() << ",\n"; - } - ss << "],\n"; - ss << "first_pids=["; - for (auto fp : self.m_first_pids) { - ss << fp << ", "; - } - ss << "]"; - return ss.str(); - } - ) - .def_property_readonly("byte_order", &MeasurementParameters::get_byte_order) - .def_property_readonly("id_field_size", &MeasurementParameters::get_id_field_size) - .def_property_readonly("timestamps_supported", &MeasurementParameters::get_timestamps_supported) - .def_property_readonly("ts_fixed", &MeasurementParameters::get_ts_fixed) - .def_property_readonly("prescaler_supported", &MeasurementParameters::get_prescaler_supported) - .def_property_readonly("selectable_timestamps", &MeasurementParameters::get_selectable_timestamps) - .def_property_readonly("ts_scale_factor", &MeasurementParameters::get_ts_scale_factor) - .def_property_readonly("ts_size", &MeasurementParameters::get_ts_size) - .def_property_readonly("min_daq", &MeasurementParameters::get_min_daq) - .def_property_readonly("daq_lists", &MeasurementParameters::get_daq_lists) - .def_property_readonly("first_pids", &MeasurementParameters::get_first_pids); - - py::class_(m, "DaqRecorderPolicy", py::dynamic_attr()) - .def(py::init<>()) - .def("create_writer", &DaqRecorderPolicy::create_writer) - .def("feed", &DaqRecorderPolicy::feed) - .def("set_parameters", &DaqRecorderPolicy::set_parameters) - .def("initialize", &DaqRecorderPolicy::initialize) - .def("finalize", &DaqRecorderPolicy::finalize); - - py::class_(m, "DaqOnlinePolicy", py::dynamic_attr()) - .def(py::init<>()) - .def("on_daq_list", &DaqOnlinePolicy::on_daq_list) - .def("feed", &DaqOnlinePolicy::feed) - .def("finalize", &DaqOnlinePolicy::finalize) - .def("set_parameters", &DaqOnlinePolicy::set_parameters) - .def("initialize", &DaqOnlinePolicy::initialize); - - py::class_(m, "XcpLogFileUnfolder", py::dynamic_attr()) - .def(py::init()) - .def("run", &XcpLogFileUnfolder::run) - .def("on_daq_list", &XcpLogFileUnfolder::on_daq_list) - .def_property_readonly("parameters", &XcpLogFileUnfolder::get_parameters) - .def_property_readonly("daq_lists", &XcpLogFileUnfolder::get_daq_lists) - .def("get_header", &XcpLogFileUnfolder::get_header); -} + +#include +#include +#include +#include + +#include + +#include "rekorder.hpp" + +namespace py = pybind11; +using namespace pybind11::literals; + +class PyDaqOnlinePolicy : public DaqOnlinePolicy { + public: + + using DaqOnlinePolicy::DaqOnlinePolicy; + + void on_daq_list( + std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + ) override { + PYBIND11_OVERRIDE_PURE(void, DaqOnlinePolicy, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); + } + + void initialize() override { + PYBIND11_OVERRIDE(void, DaqOnlinePolicy, initialize); + } + + void finalize() override { + PYBIND11_OVERRIDE(void, DaqOnlinePolicy, finalize); + } +}; + +class PyDaqRecorderPolicy : public DaqRecorderPolicy { + public: + + using DaqRecorderPolicy::DaqRecorderPolicy; + + void initialize() override { + PYBIND11_OVERRIDE(void, DaqRecorderPolicy, initialize); + } + + void finalize() override { + PYBIND11_OVERRIDE(void, DaqRecorderPolicy, finalize); + } +}; + +class PyXcpLogFileUnfolder : public XcpLogFileUnfolder { + public: + + using XcpLogFileUnfolder::XcpLogFileUnfolder; + + void on_daq_list( + std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + ) override { + PYBIND11_OVERRIDE_PURE(void, XcpLogFileUnfolder, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); + } +}; + +PYBIND11_MODULE(rekorder, m) { + m.doc() = "XCP raw frame recorder."; + m.def("data_types", get_data_types); + +#if 0 + version; + options; + num_containers; + record_count; + size_compressed; + size_uncompressed; +#endif + + py::class_(m, "FileHeaderType") + .def(py::init()) + .def("__repr__", [](const FileHeaderType& self) { + std::stringstream ss; + ss << "FileHeaderType(" << std::endl; + ss << " hdr_size=" << self.hdr_size << "," << std::endl; + ss << " version=" << self.version << "," << std::endl; + ss << " options=" << self.options << "," << std::endl; + ss << " num_containers=" << self.num_containers << "," << std::endl; + ss << " record_count=" << self.record_count << "," << std::endl; + ss << " size_compressed=" << self.size_compressed << "," << std::endl; + ss << " size_uncompressed=" << self.size_uncompressed << "," << std::endl; + ss << " compression_ratio=" + << (double)((std::uint64_t)(((double)self.size_uncompressed / (double)self.size_compressed * 100.0) + 0.5)) / 100.0 + << std::endl; + ss << ")" << std::endl; + return ss.str(); + }); + + py::class_(m, "Deserializer").def(py::init()).def("run", &Deserializer::run); + + py::class_(m, "_PyXcpLogFileReader") + .def(py::init()) + .def("next_block", &XcpLogFileReader::next_block) + .def("reset", &XcpLogFileReader::reset) + .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple) + .def("get_metadata", [](const XcpLogFileReader& self) { return py::bytes(self.get_metadata()); }); + + py::class_(m, "_PyXcpLogFileWriter") + .def( + py::init(), py::arg("filename"), + py::arg("prealloc"), py::arg("chunk_size"), py::arg("metadata") = "" + ) + .def("finalize", &XcpLogFileWriter::finalize) + .def("add_frame", &XcpLogFileWriter::add_frame); + + py::class_(m, "MeasurementParameters") + .def(py::init< + std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector&, + const std::vector&>()) + .def("dumps", [](const MeasurementParameters& self) { return py::bytes(self.dumps()); }) + .def( + "__repr__", + [](const MeasurementParameters& self) { + std::stringstream ss; + ss << "MeasurementParameters("; + ss << "byte_order=\"" << byte_order_to_string(self.m_byte_order) << "\", "; + ss << "id_field_size=" << static_cast(self.m_id_field_size) << ", "; + ss << "timestamps_supported=" << bool_to_string(self.m_timestamps_supported) << ", "; + ss << "ts_fixed=" << bool_to_string(self.m_ts_fixed) << ", "; + ss << "prescaler_supported=" << bool_to_string(self.m_prescaler_supported) << ", "; + ss << "selectable_timestamps=" << bool_to_string(self.m_selectable_timestamps) << ", "; + ss << "ts_scale_factor=" << self.m_ts_scale_factor << ", "; + ss << "ts_size=" << static_cast(self.m_ts_size) << ", "; + ss << "min_daq=" << static_cast(self.m_min_daq) << ", "; + ss << "daq_lists=[\n"; + for (const auto& dl : self.m_daq_lists) { + ss << dl.to_string() << ",\n"; + } + ss << "],\n"; + ss << "first_pids=["; + for (auto fp : self.m_first_pids) { + ss << fp << ", "; + } + ss << "]"; + return ss.str(); + } + ) + .def_property_readonly("byte_order", &MeasurementParameters::get_byte_order) + .def_property_readonly("id_field_size", &MeasurementParameters::get_id_field_size) + .def_property_readonly("timestamps_supported", &MeasurementParameters::get_timestamps_supported) + .def_property_readonly("ts_fixed", &MeasurementParameters::get_ts_fixed) + .def_property_readonly("prescaler_supported", &MeasurementParameters::get_prescaler_supported) + .def_property_readonly("selectable_timestamps", &MeasurementParameters::get_selectable_timestamps) + .def_property_readonly("ts_scale_factor", &MeasurementParameters::get_ts_scale_factor) + .def_property_readonly("ts_size", &MeasurementParameters::get_ts_size) + .def_property_readonly("min_daq", &MeasurementParameters::get_min_daq) + .def_property_readonly("daq_lists", &MeasurementParameters::get_daq_lists) + .def_property_readonly("first_pids", &MeasurementParameters::get_first_pids); + + py::class_(m, "DaqRecorderPolicy", py::dynamic_attr()) + .def(py::init<>()) + .def("create_writer", &DaqRecorderPolicy::create_writer) + .def("feed", &DaqRecorderPolicy::feed) + .def("set_parameters", &DaqRecorderPolicy::set_parameters) + .def("initialize", &DaqRecorderPolicy::initialize) + .def("finalize", &DaqRecorderPolicy::finalize); + + py::class_(m, "DaqOnlinePolicy", py::dynamic_attr()) + .def(py::init<>()) + .def("on_daq_list", &DaqOnlinePolicy::on_daq_list) + .def("feed", &DaqOnlinePolicy::feed) + .def("finalize", &DaqOnlinePolicy::finalize) + .def("set_parameters", &DaqOnlinePolicy::set_parameters) + .def("initialize", &DaqOnlinePolicy::initialize); + + py::class_(m, "XcpLogFileUnfolder", py::dynamic_attr()) + .def(py::init()) + .def("run", &XcpLogFileUnfolder::run) + .def("on_daq_list", &XcpLogFileUnfolder::on_daq_list) + .def_property_readonly("parameters", &XcpLogFileUnfolder::get_parameters) + .def_property_readonly("daq_lists", &XcpLogFileUnfolder::get_daq_lists) + .def("get_header", &XcpLogFileUnfolder::get_header); +} diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 32b07d6..db3e8ba 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -1,285 +1,285 @@ - -#ifndef RECORDER_WRITER_HPP -#define RECORDER_WRITER_HPP - -constexpr std::uint64_t MASK32 = (1ULL << 32) - 1; - -class XcpLogFileWriter { - public: - - explicit XcpLogFileWriter( - const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1, std::string_view metadata = "" - ) { - if (!file_name.ends_with(detail::FILE_EXTENSION)) { - m_file_name = file_name + detail::FILE_EXTENSION; - } else { - m_file_name = file_name; - } - -#if defined(_WIN32) - m_fd = CreateFileA( - m_file_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) nullptr, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr - ); -#else - m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666); -#endif - m_hard_limit = megabytes(prealloc); - resize(m_hard_limit); - m_mmap = new mio::mmap_sink(m_fd); - m_chunk_size = 512 * 1024; // megabytes(chunk_size); - m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)]; - m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; - m_metadata = metadata; - - if (!metadata.empty()) { - m_offset += std::size(metadata); - write_metadata(); - } - start_thread(); - } - - ~XcpLogFileWriter() { - finalize(); -#ifdef __APPLE__ - if (collector_thread.joinable()) { - collector_thread.join(); - } -#endif - } - - void finalize() { - std::error_code ec; - if (!m_finalized) { - m_finalized = true; - stop_thread(); - if (m_container_record_count) { - compress_frames(); - } - - std::uint16_t options = m_metadata.empty() ? 0 : XMRAW_HAS_METADATA; - - write_header( - detail::VERSION, options, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed - ); - m_mmap->unmap(); - ec = mio::detail::last_error(); - if (ec.value() != 0) { - std::cout << error_string("mio::unmap", ec); - } - - resize(m_offset); -#if defined(_WIN32) - if (!CloseHandle(m_fd)) { - std::cout << error_string("CloseHandle", get_last_error()); - } -#else - if (close(m_fd) == -1) { - std::cout << error_string("close", get_last_error()); - } -#endif - delete m_mmap; - delete[] m_intermediate_storage; - } - } - - void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const *data) { - auto payload = new char[length]; - - _fcopy(payload, data, length); - my_queue.put(std::make_tuple(category, counter, timestamp, length, payload)); - } - - protected: - - void resize(std::uint64_t size, bool remap = false) { - std::error_code ec; - - if (remap) { - m_mmap->unmap(); - ec = mio::detail::last_error(); - if (ec.value() != 0) { - std::cout << error_string("mio::unmap", ec); - } - } - -#if defined(_WIN32) - LONG low_part = (MASK32 & size); - LONG high_part = size >> 32; - - if (SetFilePointer(m_fd, low_part, &high_part, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { - auto err = get_last_error(); - - if (err.value() != NO_ERROR) { - std::cout << error_string("SetFilePointer", err); - } - } - if (SetEndOfFile(m_fd) == 0) { - std::cout << error_string("SetEndOfFile", get_last_error()); - } -#else - if (ftruncate(m_fd, size) == -1) { - std::cout << error_string("ftruncate", get_last_error()); - } -#endif - if (remap) { - m_mmap->map(m_fd, 0, size, ec); - if (ec.value() != 0) { - std::cout << error_string("mio::map", ec); - } - } - } - - blob_t *ptr(std::uint64_t pos = 0) const { - return (blob_t *)(m_mmap->data() + pos); - } - - template - void store_im(T const *data, std::uint32_t length) { - _fcopy( - reinterpret_cast(m_intermediate_storage + m_intermediate_storage_offset), reinterpret_cast(data), - length - ); - m_intermediate_storage_offset += length; - } - - void compress_frames() { - auto container = ContainerHeaderType{}; - // printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); - const int cp_size = ::LZ4_compress_HC( - reinterpret_cast(m_intermediate_storage), - reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), m_intermediate_storage_offset, - LZ4_COMPRESSBOUND(m_intermediate_storage_offset), LZ4HC_CLEVEL_MAX - ); - - if (cp_size < 0) { - throw std::runtime_error("LZ4 compression failed."); - } - - if (m_offset > (m_hard_limit >> 1)) { - std::cout << "[INFO] " << current_timestamp() << ": Doubling measurement file size." << std::endl; - m_hard_limit <<= 1; - resize(m_hard_limit, true); - write_header( - detail::VERSION, m_metadata.empty() ? 0 : XMRAW_HAS_METADATA, m_num_containers, m_record_count, - m_total_size_compressed, m_total_size_uncompressed - ); - } - container.record_count = m_container_record_count; - container.size_compressed = cp_size; - container.size_uncompressed = m_container_size_uncompressed; - - _fcopy(reinterpret_cast(ptr(m_offset)), reinterpret_cast(&container), detail::CONTAINER_SIZE); - - m_offset += (detail::CONTAINER_SIZE + cp_size); - m_total_size_uncompressed += m_container_size_uncompressed; - m_total_size_compressed += cp_size; - m_record_count += m_container_record_count; - m_container_size_uncompressed = 0; - m_container_size_compressed = 0; - m_container_record_count = 0; - m_intermediate_storage_offset = 0; - m_num_containers += 1; - } - - void write_bytes(std::uint64_t pos, std::uint64_t count, char const *buf) const { - auto addr = reinterpret_cast(ptr(pos)); - - _fcopy(addr, buf, count); - } - - void write_header( - std::uint16_t version, std::uint16_t options, std::uint64_t num_containers, std::uint64_t record_count, - std::uint64_t size_compressed, std::uint64_t size_uncompressed - ) { - auto header = FileHeaderType{}; - write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); - header.hdr_size = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; - header.version = version; - header.options = options; - header.num_containers = num_containers; - header.record_count = record_count; - header.size_compressed = size_compressed; - header.size_uncompressed = size_uncompressed; - write_bytes(0x00000000UL + detail::MAGIC_SIZE, detail::FILE_HEADER_SIZE, reinterpret_cast(&header)); - } - - void write_metadata() { - if (!m_metadata.empty()) { - write_bytes(detail::MAGIC_SIZE + detail::FILE_HEADER_SIZE, m_metadata.size(), m_metadata.c_str()); - } - } - - bool start_thread() { - if (collector_thread.joinable()) { - return false; - } - stop_collector_thread_flag = false; -#ifdef __APPLE__ - collector_thread = std::thread([this]() { -#else - collector_thread = std::jthread([this]() { -#endif - while (!stop_collector_thread_flag) { - auto item = my_queue.get(); - const auto content = item.get(); - if (stop_collector_thread_flag == true) { - break; - } - const auto [category, counter, timestamp, length, payload] = *content; - const frame_header_t frame{ category, counter, timestamp, length }; - store_im(&frame, sizeof(frame)); - store_im(payload, length); - delete[] payload; - m_container_record_count += 1; - m_container_size_uncompressed += (sizeof(frame) + length); - if (m_container_size_uncompressed > m_chunk_size) { - compress_frames(); - } - } - }); - - return true; - } - - bool stop_thread() { - if (!collector_thread.joinable()) { - return false; - } - stop_collector_thread_flag = true; - my_queue.put(FrameTupleWriter{}); // Put something into the queue, otherwise the thread will hang forever. - collector_thread.join(); - return true; - } - - private: - - std::string m_file_name; - std::uint64_t m_offset{ 0 }; - std::uint32_t m_chunk_size{ 0 }; - std::string m_metadata; - std::uint64_t m_num_containers{ 0 }; - std::uint64_t m_record_count{ 0UL }; - std::uint32_t m_container_record_count{ 0UL }; - std::uint64_t m_total_size_uncompressed{ 0UL }; - std::uint64_t m_total_size_compressed{ 0UL }; - std::uint32_t m_container_size_uncompressed{ 0UL }; - std::uint32_t m_container_size_compressed{ 0UL }; - __ALIGN blob_t *m_intermediate_storage{ nullptr }; - std::uint32_t m_intermediate_storage_offset{ 0 }; - std::uint64_t m_hard_limit{ 0 }; - mio::file_handle_type m_fd{ INVALID_HANDLE_VALUE }; - mio::mmap_sink *m_mmap{ nullptr }; - bool m_finalized{ false }; -#ifdef __APPLE__ - std::thread collector_thread{}; -#else - std::jthread collector_thread{}; -#endif - std::mutex mtx; - TsQueue my_queue; - BlockMemory mem{}; - std::atomic_bool stop_collector_thread_flag{ false }; -}; - -#endif // RECORDER_WRITER_HPP + +#ifndef RECORDER_WRITER_HPP +#define RECORDER_WRITER_HPP + +constexpr std::uint64_t MASK32 = (1ULL << 32) - 1; + +class XcpLogFileWriter { + public: + + explicit XcpLogFileWriter( + const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1, std::string_view metadata = "" + ) { + if (!file_name.ends_with(detail::FILE_EXTENSION)) { + m_file_name = file_name + detail::FILE_EXTENSION; + } else { + m_file_name = file_name; + } + +#if defined(_WIN32) + m_fd = CreateFileA( + m_file_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr + ); +#else + m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666); +#endif + m_hard_limit = megabytes(prealloc); + resize(m_hard_limit); + m_mmap = new mio::mmap_sink(m_fd); + m_chunk_size = 512 * 1024; // megabytes(chunk_size); + m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)]; + m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; + m_metadata = metadata; + + if (!metadata.empty()) { + m_offset += std::size(metadata); + write_metadata(); + } + start_thread(); + } + + ~XcpLogFileWriter() { + finalize(); +#ifdef __APPLE__ + if (collector_thread.joinable()) { + collector_thread.join(); + } +#endif + } + + void finalize() { + std::error_code ec; + if (!m_finalized) { + m_finalized = true; + stop_thread(); + if (m_container_record_count) { + compress_frames(); + } + + std::uint16_t options = m_metadata.empty() ? 0 : XMRAW_HAS_METADATA; + + write_header( + detail::VERSION, options, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed + ); + m_mmap->unmap(); + ec = mio::detail::last_error(); + if (ec.value() != 0) { + std::cout << error_string("mio::unmap", ec); + } + + resize(m_offset); +#if defined(_WIN32) + if (!CloseHandle(m_fd)) { + std::cout << error_string("CloseHandle", get_last_error()); + } +#else + if (close(m_fd) == -1) { + std::cout << error_string("close", get_last_error()); + } +#endif + delete m_mmap; + delete[] m_intermediate_storage; + } + } + + void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const *data) { + auto payload = new char[length]; + + _fcopy(payload, data, length); + my_queue.put(std::make_tuple(category, counter, timestamp, length, payload)); + } + + protected: + + void resize(std::uint64_t size, bool remap = false) { + std::error_code ec; + + if (remap) { + m_mmap->unmap(); + ec = mio::detail::last_error(); + if (ec.value() != 0) { + std::cout << error_string("mio::unmap", ec); + } + } + +#if defined(_WIN32) + LONG low_part = (MASK32 & size); + LONG high_part = size >> 32; + + if (SetFilePointer(m_fd, low_part, &high_part, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + auto err = get_last_error(); + + if (err.value() != NO_ERROR) { + std::cout << error_string("SetFilePointer", err); + } + } + if (SetEndOfFile(m_fd) == 0) { + std::cout << error_string("SetEndOfFile", get_last_error()); + } +#else + if (ftruncate(m_fd, size) == -1) { + std::cout << error_string("ftruncate", get_last_error()); + } +#endif + if (remap) { + m_mmap->map(m_fd, 0, size, ec); + if (ec.value() != 0) { + std::cout << error_string("mio::map", ec); + } + } + } + + blob_t *ptr(std::uint64_t pos = 0) const { + return (blob_t *)(m_mmap->data() + pos); + } + + template + void store_im(T const *data, std::uint32_t length) { + _fcopy( + reinterpret_cast(m_intermediate_storage + m_intermediate_storage_offset), reinterpret_cast(data), + length + ); + m_intermediate_storage_offset += length; + } + + void compress_frames() { + auto container = ContainerHeaderType{}; + // printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset); + const int cp_size = ::LZ4_compress_HC( + reinterpret_cast(m_intermediate_storage), + reinterpret_cast(ptr(m_offset + detail::CONTAINER_SIZE)), m_intermediate_storage_offset, + LZ4_COMPRESSBOUND(m_intermediate_storage_offset), LZ4HC_CLEVEL_MAX + ); + + if (cp_size < 0) { + throw std::runtime_error("LZ4 compression failed."); + } + + if (m_offset > (m_hard_limit >> 1)) { + std::cout << "[INFO] " << current_timestamp() << ": Doubling measurement file size." << std::endl; + m_hard_limit <<= 1; + resize(m_hard_limit, true); + write_header( + detail::VERSION, m_metadata.empty() ? 0 : XMRAW_HAS_METADATA, m_num_containers, m_record_count, + m_total_size_compressed, m_total_size_uncompressed + ); + } + container.record_count = m_container_record_count; + container.size_compressed = cp_size; + container.size_uncompressed = m_container_size_uncompressed; + + _fcopy(reinterpret_cast(ptr(m_offset)), reinterpret_cast(&container), detail::CONTAINER_SIZE); + + m_offset += (detail::CONTAINER_SIZE + cp_size); + m_total_size_uncompressed += m_container_size_uncompressed; + m_total_size_compressed += cp_size; + m_record_count += m_container_record_count; + m_container_size_uncompressed = 0; + m_container_size_compressed = 0; + m_container_record_count = 0; + m_intermediate_storage_offset = 0; + m_num_containers += 1; + } + + void write_bytes(std::uint64_t pos, std::uint64_t count, char const *buf) const { + auto addr = reinterpret_cast(ptr(pos)); + + _fcopy(addr, buf, count); + } + + void write_header( + std::uint16_t version, std::uint16_t options, std::uint64_t num_containers, std::uint64_t record_count, + std::uint64_t size_compressed, std::uint64_t size_uncompressed + ) { + auto header = FileHeaderType{}; + write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str()); + header.hdr_size = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE; + header.version = version; + header.options = options; + header.num_containers = num_containers; + header.record_count = record_count; + header.size_compressed = size_compressed; + header.size_uncompressed = size_uncompressed; + write_bytes(0x00000000UL + detail::MAGIC_SIZE, detail::FILE_HEADER_SIZE, reinterpret_cast(&header)); + } + + void write_metadata() { + if (!m_metadata.empty()) { + write_bytes(detail::MAGIC_SIZE + detail::FILE_HEADER_SIZE, m_metadata.size(), m_metadata.c_str()); + } + } + + bool start_thread() { + if (collector_thread.joinable()) { + return false; + } + stop_collector_thread_flag = false; +#ifdef __APPLE__ + collector_thread = std::thread([this]() { +#else + collector_thread = std::jthread([this]() { +#endif + while (!stop_collector_thread_flag) { + auto item = my_queue.get(); + const auto content = item.get(); + if (stop_collector_thread_flag == true) { + break; + } + const auto [category, counter, timestamp, length, payload] = *content; + const frame_header_t frame{ category, counter, timestamp, length }; + store_im(&frame, sizeof(frame)); + store_im(payload, length); + delete[] payload; + m_container_record_count += 1; + m_container_size_uncompressed += (sizeof(frame) + length); + if (m_container_size_uncompressed > m_chunk_size) { + compress_frames(); + } + } + }); + + return true; + } + + bool stop_thread() { + if (!collector_thread.joinable()) { + return false; + } + stop_collector_thread_flag = true; + my_queue.put(FrameTupleWriter{}); // Put something into the queue, otherwise the thread will hang forever. + collector_thread.join(); + return true; + } + + private: + + std::string m_file_name; + std::uint64_t m_offset{ 0 }; + std::uint32_t m_chunk_size{ 0 }; + std::string m_metadata; + std::uint64_t m_num_containers{ 0 }; + std::uint64_t m_record_count{ 0UL }; + std::uint32_t m_container_record_count{ 0UL }; + std::uint64_t m_total_size_uncompressed{ 0UL }; + std::uint64_t m_total_size_compressed{ 0UL }; + std::uint32_t m_container_size_uncompressed{ 0UL }; + std::uint32_t m_container_size_compressed{ 0UL }; + __ALIGN blob_t *m_intermediate_storage{ nullptr }; + std::uint32_t m_intermediate_storage_offset{ 0 }; + std::uint64_t m_hard_limit{ 0 }; + mio::file_handle_type m_fd{ INVALID_HANDLE_VALUE }; + mio::mmap_sink *m_mmap{ nullptr }; + bool m_finalized{ false }; +#ifdef __APPLE__ + std::thread collector_thread{}; +#else + std::jthread collector_thread{}; +#endif + std::mutex mtx; + TsQueue my_queue; + BlockMemory mem{}; + std::atomic_bool stop_collector_thread_flag{ false }; +}; + +#endif // RECORDER_WRITER_HPP From 50eeef5b65562f19df0a74bf2dad2574dec4fc6e Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 25 Jun 2024 13:13:14 +0300 Subject: [PATCH 66/99] today() --- CMakeLists.txt | 25 ++++++++++++++++--------- build_ext.py | 3 ++- pyproject.toml | 1 + pyxcp/cpp_ext/helper.hpp | 2 +- pyxcp/scripts/xcp_profile.py | 3 +++ 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 357d431..aad08ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,16 +14,23 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/dist") SET(GCC_N_CLANG_BASE_OPTIONS "-std=c++23 -Wall -Wextra -Wpedantic -Warray-bounds -mtune=native -fexceptions") -# target_link_options(${PROJECT_NAME} PUBLIC -flto=auto) -SET(MSVC_BASE_OPTIONS "/W3 /permissive- /EHsc /bigobj /std:c++latest") - - -# if (CMAKE_BUILD_TYPE STREQUAL "DEBUG") -# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Od /fsanitize=address /Zi") -# else () -# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox ") # /GL -# endif () +SET(MSVC_BASE_OPTIONS "/W3 /permissive- /EHsc /bigobj /Zc:__cplusplus /std:c++latest") + + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + if (MSVC) + SET(MSVC_BASE_OPTIONS "${MSVC_BASE_OPTIONS} /Od /fsanitize=address /Zi") + else() + SET(MSVC_BASE_OPTIONS "${MSVC_BASE_OPTIONS} -Og -g3 -ggdb --fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fsanitize=bounds") # -fsanitize=hwaddress + endif() +else () + if (MSVC) + SET(MSVC_BASE_OPTIONS "${MSVC_BASE_OPTIONS} /Ox") + else() + SET(MSVC_BASE_OPTIONS "${MSVC_BASE_OPTIONS} -O3 --fomit-frame-pointer") + endif() + endif () if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") diff --git a/build_ext.py b/build_ext.py index d53a192..34f00a9 100644 --- a/build_ext.py +++ b/build_ext.py @@ -28,6 +28,7 @@ def build_extension(debug: bool = False) -> None: debug = int(os.environ.get("DEBUG", 0)) or debug cfg = "Debug" if debug else "Release" + print(f" BUILD-TYPE: {cfg!r}") # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code @@ -77,4 +78,4 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": includes = subprocess.getoutput("pybind11-config --cmakedir") # nosec os.environ["pybind11_DIR"] = includes - build_extension() + build_extension(False) diff --git a/pyproject.toml b/pyproject.toml index 7cb054d..3862d3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,6 +94,7 @@ pyxcp-probe-can-drivers = "pyxcp.scripts.pyxcp_probe_can_drivers:main" xcp-id-scanner = "pyxcp.scripts.xcp_id_scanner:main" xcp-fetch-a2l = "pyxcp.scripts.xcp_fetch_a2l:main" xcp-info = "pyxcp.scripts.xcp_info:main" +xcp-profile = "pyxcp.scripts.xcp_profile:main" [tool.pytest] addopts = "--verbose --tb=short --junitxml=result.xml -o junit_family=xunit2" diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 6f17312..03df74d 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -5,7 +5,7 @@ #include #include - #if __cplusplus >= 202302L + #if (__cplusplus >= 202302L) || (__STDC_VERSION__ >= 202302L) #include #if defined(__STDCPP_BFLOAT16_T__) diff --git a/pyxcp/scripts/xcp_profile.py b/pyxcp/scripts/xcp_profile.py index 03226f6..5ec8640 100644 --- a/pyxcp/scripts/xcp_profile.py +++ b/pyxcp/scripts/xcp_profile.py @@ -2,9 +2,12 @@ """Create / convert pyxcp profiles (configurations). """ +import sys + from pyxcp.cmdline import ArgumentParser +sys.argv.append("profile") ap = ArgumentParser(description="Create / convert pyxcp profiles (configurations).") with ap.run() as x: From 7ada8870fd9956fb5231871081ed6d9943713fdc Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 30 Jun 2024 11:45:29 +0300 Subject: [PATCH 67/99] constexpr Memblock --- pyxcp/cpp_ext/blockmem.hpp | 6 +++--- pyxcp/examples/run_daq.py | 27 ++++++++++++++------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/pyxcp/cpp_ext/blockmem.hpp b/pyxcp/cpp_ext/blockmem.hpp index ac65671..8204c12 100644 --- a/pyxcp/cpp_ext/blockmem.hpp +++ b/pyxcp/cpp_ext/blockmem.hpp @@ -17,7 +17,7 @@ class BlockMemory { using mem_block_t = std::array; - explicit BlockMemory() noexcept : m_memory{ nullptr }, m_allocation_count{ 0 } { + constexpr explicit BlockMemory() noexcept : m_memory{ nullptr }, m_allocation_count{ 0 } { m_memory = new T[_IS * _NB]; } @@ -29,7 +29,7 @@ class BlockMemory { BlockMemory(const BlockMemory&) = delete; - T* acquire() noexcept { + constexpr T* acquire() noexcept { const std::scoped_lock lock(m_mtx); if (m_allocation_count >= _NB) { @@ -40,7 +40,7 @@ class BlockMemory { return ptr; } - void release() noexcept { + constexpr void release() noexcept { const std::scoped_lock lock(m_mtx); if (m_allocation_count == 0) { return; diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 56fd255..56324ef 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -10,8 +10,8 @@ ap = ArgumentParser(description="DAQ test") -# XCP_LITE = True -XCP_LITE = False +XCP_LITE = True +# XCP_LITE = False # Completly random configurations, only for illustrative purposes. # @@ -26,10 +26,10 @@ False, False, [ - ("byteCounter", 0x203EA, 0, "U8"), - ("wordCounter", 0x203EC, 0, "U16"), - ("dwordCounter", 0x20410, 0, "U32"), - ("sbyteCounter", 0x203EB, 0, "I8"), + ("byteCounter", 0x1D48080, 0, "U8"), # + ("wordCounter", 0x1D48082, 0, "U16"), + ("dwordCounter", 0x1D48084, 0, "U32"), + ("sbyteCounter", 0x1D48088, 0, "I8"), ], ), DaqList( @@ -38,11 +38,11 @@ False, False, [ - ("swordCounter", 0x20414, 0, "I16"), - ("sdwordCounter", 0x20418, 0, "I32"), - ("channel1", 0x203F8, 0, "F64"), - ("channel2", 0x20400, 0, "F64"), - ("channel3", 0x20408, 0, "F64"), + ("swordCounter", 0x1D4808A, 0, "I16"), + ("sdwordCounter", 0x1D4808C, 0, "I32"), + ("channel1", 0x1D48068, 0, "F64"), + ("channel2", 0x1D48070, 0, "F64"), + ("channel3", 0x1D48078, 0, "F64"), ], ), ] @@ -121,6 +121,7 @@ ), ] +""" DAQ_LISTS = [ DaqList( "mkrzero_floats", @@ -139,7 +140,7 @@ ], ), ] - +""" # daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. daq_parser = DaqRecorder(DAQ_LISTS, "run_daq", 2) @@ -155,7 +156,7 @@ daq_parser.setup() # Run internal and XCP setup procedures. print("start DAQ lists.") daq_parser.start() # Start DAQ lists. - time.sleep(1 * 60.0 * 60.0) # Arbitrary termination condition.ls *. + time.sleep(12 * 60.0 * 60.0) # Arbitrary termination condition.ls *. # time.sleep(6 * 60.0 * 60.0) # Arbitrary termination condition.ls *. daq_parser.stop() # Stop DAQ lists. print("finalize DAQ lists.\n") From e88189387e61de107d2c050ed92b19d4c773080c Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 30 Jun 2024 12:02:02 +0300 Subject: [PATCH 68/99] today() --- .pre-commit-config.yaml | 4 ++++ poetry.lock | 15 +++++++++++-- pyproject.toml | 1 + pyxcp/cpp_ext/daqlist.hpp | 28 ++++++++++++++--------- pyxcp/cpp_ext/extension_wrapper.cpp | 2 +- pyxcp/cpp_ext/helper.hpp | 10 +++++++++ pyxcp/cpp_ext/mcobject.hpp | 18 +++++++++++++++ pyxcp/daq_stim/__init__.py | 2 +- pyxcp/recorder/__init__.py | 1 + pyxcp/recorder/unfolder.hpp | 27 +++++++++++++++++++++- pyxcp/recorder/wrap.cpp | 35 ++++++++++++++++++++--------- 11 files changed, 116 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fff4ed0..156b1de 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -124,3 +124,7 @@ repos: rev: v2.6.0 hooks: - id: prettier + #- repo: https://github.com/necaris/pre-commit-pyright + #rev: '1.1.53' + #hooks: + #- id: pyright diff --git a/poetry.lock b/poetry.lock index 1949f92..130f12e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1122,7 +1122,7 @@ files = [ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, + {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, ] [[package]] @@ -2173,6 +2173,17 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "tomlkit" +version = "0.12.5" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, +] + [[package]] name = "tornado" version = "6.4.1" @@ -2426,4 +2437,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "55351fe922a2782147c19d96431addbc8911e925f19707c9dba68785fd99a55d" +content-hash = "0a5222b7a2b78258d8cc489cf52717186e6bc11eb8d0fb6bd5077142aeed0000" diff --git a/pyproject.toml b/pyproject.toml index 3862d3c..6634f8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,6 +84,7 @@ line-profiler-pycharm = "^1.1.0" toml = "^0.10.2" bandit = "^1.7.8" +tomlkit = "^0.12.5" [tool.poetry.group.dev.dependencies] ruff = "^0.1.0" diff --git a/pyxcp/cpp_ext/daqlist.hpp b/pyxcp/cpp_ext/daqlist.hpp index 9832e8e..b836105 100644 --- a/pyxcp/cpp_ext/daqlist.hpp +++ b/pyxcp/cpp_ext/daqlist.hpp @@ -53,6 +53,10 @@ class DaqList { return m_header_names; } + const std::vector>& get_headers() const noexcept { + return m_headers; + } + std::uint16_t get_odt_count() const { return m_odt_count; } @@ -85,6 +89,7 @@ class DaqList { component.get_name(), component.get_address(), component.get_ext(), component.get_length(), component.get_type_index() ); + m_headers.emplace_back(component.get_name(), TYPE_MAP_REV.at(component.get_type_index())); total_entries++; total_length += component.get_length(); } @@ -175,17 +180,18 @@ class DaqList { private: - std::string m_name; - std::uint16_t m_event_num; - bool m_stim; - bool m_enable_timestamps; - std::vector m_measurements; - std::vector m_measurements_opt; - std::vector m_header_names; - std::uint16_t m_odt_count; - std::uint16_t m_total_entries; - std::uint16_t m_total_length; - flatten_odts_t m_flatten_odts; + std::string m_name; + std::uint16_t m_event_num; + bool m_stim; + bool m_enable_timestamps; + std::vector m_measurements; + std::vector m_measurements_opt; + std::vector m_header_names; + std::vector> m_headers; + std::uint16_t m_odt_count; + std::uint16_t m_total_entries; + std::uint16_t m_total_length; + flatten_odts_t m_flatten_odts; }; #endif // __DAQ_LIST_HPP diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index c906169..cf1662b 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -57,7 +57,7 @@ PYBIND11_MODULE(cpp_ext, m) { .def_property("enable_timestamps", &DaqList::get_enable_timestamps, nullptr) .def_property("measurements", &DaqList::get_measurements, nullptr) .def_property("measurements_opt", &DaqList::get_measurements_opt, &DaqList::set_measurements_opt) - .def_property("header_names", &DaqList::get_header_names, nullptr) + .def_property("headers", &DaqList::get_headers, nullptr) .def_property("odt_count", &DaqList::get_odt_count, nullptr) .def_property("total_entries", &DaqList::get_total_entries, nullptr) .def_property("total_length", &DaqList::get_total_length, nullptr); diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 03df74d..de37671 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -3,6 +3,7 @@ #define __HELPER_HPP #include + #include #include #if (__cplusplus >= 202302L) || (__STDC_VERSION__ >= 202302L) @@ -91,4 +92,13 @@ inline auto byte_order_to_string(int value) { return ""; } +template +static std::map reverse_map(const std::map &m) { + std::map result; + for (const auto &[k, v] : m) { + result[v] = k; + } + return result; +} + #endif // __HELPER_HPP diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index 7aaa1c7..ef95c4e 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -30,6 +30,24 @@ const std::map> TYPE #endif }; +const std::map TYPE_MAP_REV = { + { 0, "U8" }, + { 1, "I8" }, + { 2, "U16" }, + { 3, "I16" }, + { 4, "U32" }, + { 5, "I32" }, + { 6, "U64" }, + { 7, "I64" }, + { 8, "F32" }, + { 9, "F64" }, + #if HAS_FLOAT16 + { 10, "F16" }, + #endif + #if HAS_BFLOAT16 + { 11, "BF16" }, + #endif +}; inline std::vector get_data_types() { std::vector result; diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index e701550..88748b0 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -201,7 +201,7 @@ def initialize(self): continue out_file = open(f"{daq_list.name}.csv", "w") self.files[num] = out_file - hdr = ",".join(["timestamp0", "timestamp1"] + daq_list.header_names) + hdr = ",".join(["timestamp0", "timestamp1"] + [h[0] for h in daq_list.headers]) out_file.write(f"{hdr}\n") def on_daq_list(self, daq_list: int, ts0: float, ts1: float, payload: list): diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 61af50e..44bd5cc 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -19,6 +19,7 @@ DaqRecorderPolicy, Deserializer, MeasurementParameters, + ValueHolder, XcpLogFileUnfolder, _PyXcpLogFileReader, _PyXcpLogFileWriter, diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 7e5573f..31e98f0 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -2,6 +2,7 @@ #ifndef RECORDER_UNFOLDER_HPP #define RECORDER_UNFOLDER_HPP +#include #include #include #include @@ -1056,7 +1057,6 @@ class DaqRecorderPolicy : public DAQPolicyBase { } void initialize() override { - // TODO: Save meta-data. } void finalize() override { @@ -1108,6 +1108,22 @@ class DaqOnlinePolicy : public DAQPolicyBase { std::unique_ptr m_unfolder; }; + +struct ValueHolder { + + ValueHolder() = delete; + ValueHolder(const ValueHolder&) = default; + ValueHolder(const std::any& value) : m_value(value) {} + ValueHolder(std::any&& value) : m_value(std::move(value)) {} + + std::any get_value() const noexcept { + return m_value; + } + +private: + std::any m_value; +}; + class XcpLogFileUnfolder { public: @@ -1125,7 +1141,14 @@ class XcpLogFileUnfolder { XcpLogFileUnfolder() = delete; virtual ~XcpLogFileUnfolder() = default; + virtual void initialize() { + } + + virtual void finalize() { + } + void run() { + initialize(); const auto converter = [](const blob_t* in_str, std::size_t length) -> std::string { std::string result; result.resize(length); @@ -1140,6 +1163,7 @@ class XcpLogFileUnfolder { while (true) { const auto& block = m_reader.next_block(); if (!block) { + finalize(); return; } @@ -1155,6 +1179,7 @@ class XcpLogFileUnfolder { } } } + return; } virtual void on_daq_list( diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index bf3b30e..e45fe8c 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -11,6 +11,8 @@ namespace py = pybind11; using namespace pybind11::literals; +PYBIND11_MAKE_OPAQUE(ValueHolder); + class PyDaqOnlinePolicy : public DaqOnlinePolicy { public: @@ -55,21 +57,20 @@ class PyXcpLogFileUnfolder : public XcpLogFileUnfolder { ) override { PYBIND11_OVERRIDE_PURE(void, XcpLogFileUnfolder, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); } + + void initialize() override { + PYBIND11_OVERRIDE(void, XcpLogFileUnfolder, initialize); + } + + void finalize() override { + PYBIND11_OVERRIDE(void, XcpLogFileUnfolder, finalize); + } }; PYBIND11_MODULE(rekorder, m) { m.doc() = "XCP raw frame recorder."; m.def("data_types", get_data_types); -#if 0 - version; - options; - num_containers; - record_count; - size_compressed; - size_uncompressed; -#endif - py::class_(m, "FileHeaderType") .def(py::init()) .def("__repr__", [](const FileHeaderType& self) { @@ -89,7 +90,10 @@ PYBIND11_MODULE(rekorder, m) { return ss.str(); }); - py::class_(m, "Deserializer").def(py::init()).def("run", &Deserializer::run); + py::class_(m, "Deserializer") + .def(py::init()) + .def("run", &Deserializer::run) + ; py::class_(m, "_PyXcpLogFileReader") .def(py::init()) @@ -172,5 +176,14 @@ PYBIND11_MODULE(rekorder, m) { .def("on_daq_list", &XcpLogFileUnfolder::on_daq_list) .def_property_readonly("parameters", &XcpLogFileUnfolder::get_parameters) .def_property_readonly("daq_lists", &XcpLogFileUnfolder::get_daq_lists) - .def("get_header", &XcpLogFileUnfolder::get_header); + .def("get_header", &XcpLogFileUnfolder::get_header) + .def("initialize", &XcpLogFileUnfolder::initialize) + .def("finalize", &XcpLogFileUnfolder::finalize) + ; + + py::class_(m, "ValueHolder") + //.def(py::init()) + .def(py::init()) + .def_property_readonly("value", &ValueHolder::get_value) + ; } From f1a1280e200af19e135537299d127f22e6555708 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 7 Jul 2024 08:37:44 +0300 Subject: [PATCH 69/99] today() --- poetry.lock | 280 +++++++++++++++++++------------------ pyproject.toml | 2 +- pyxcp/examples/ex_arrow.py | 135 ++++++++++++++++++ 3 files changed, 283 insertions(+), 134 deletions(-) create mode 100644 pyxcp/examples/ex_arrow.py diff --git a/poetry.lock b/poetry.lock index 130f12e..0ac5d4c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -168,13 +168,13 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.6.2" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, - {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] @@ -592,19 +592,19 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.15.4" +version = "3.12.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, + {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, + {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] +typing = ["typing-extensions (>=4.7.1)"] [[package]] name = "flake8" @@ -729,13 +729,13 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.2.1" +version = "8.0.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.2.1-py3-none-any.whl", hash = "sha256:ffef94b0b66046dd8ea2d619b701fe978d9264d38f3998bc4c27ec3b146a87c8"}, - {file = "importlib_metadata-7.2.1.tar.gz", hash = "sha256:509ecb2ab77071db5137c655e24ceb3eee66e7bbc6574165d0d114d9fc4bbe68"}, + {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"}, + {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"}, ] [package.dependencies] @@ -1127,38 +1127,38 @@ files = [ [[package]] name = "mypy" -version = "1.10.0" +version = "1.10.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, - {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, - {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, - {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, - {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, - {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, - {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, - {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, - {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, - {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, - {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, - {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, - {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, - {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, - {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, - {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, - {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, - {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, - {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, + {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, + {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, + {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, + {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, + {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, + {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, + {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, + {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, + {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, + {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, + {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, + {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, + {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, + {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, + {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, + {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, + {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, ] [package.dependencies] @@ -1355,109 +1355,122 @@ files = [ [[package]] name = "pydantic" -version = "2.7.4" +version = "2.8.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, - {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, + {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, + {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.4" -typing-extensions = ">=4.6.1" +pydantic-core = "2.20.1" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] [package.extras] email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.4" +version = "2.20.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, - {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, - {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, - {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, - {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, - {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, - {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, - {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, - {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, - {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, - {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, - {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, - {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, - {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, + {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, + {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, + {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, + {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, + {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, + {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, + {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, + {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"}, + {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"}, + {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, + {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, + {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, + {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, ] [package.dependencies] @@ -1853,19 +1866,20 @@ files = [ [[package]] name = "safety" -version = "3.2.3" +version = "3.2.4" description = "Checks installed dependencies for known vulnerabilities and licenses." optional = false python-versions = ">=3.7" files = [ - {file = "safety-3.2.3-py3-none-any.whl", hash = "sha256:cda1e91749f610337a18b7f21f78267c127e44ebbbbcbbd419c83284279a5024"}, - {file = "safety-3.2.3.tar.gz", hash = "sha256:414154934f1727daf8a6473493944fecb380540c3f00875dc1ae377382f7d83f"}, + {file = "safety-3.2.4-py3-none-any.whl", hash = "sha256:242ff7ae448d7fb2ea455c90f44e3f2ca45be9c8559b2fe9dfc89617164a0f17"}, + {file = "safety-3.2.4.tar.gz", hash = "sha256:bac0202016d736a2118057964a0e3983fa20ff2563fd103cac3f3ac1ed3fea11"}, ] [package.dependencies] Authlib = ">=1.2.0" Click = ">=8.0.2" dparse = ">=0.6.4b0" +filelock = ">=3.12.2,<3.13.0" jinja2 = ">=3.1.0" marshmallow = ">=3.15.0" packaging = ">=21.0" @@ -1904,18 +1918,18 @@ typing-extensions = ">=4.7.1" [[package]] name = "setuptools" -version = "70.1.0" +version = "70.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.1.0-py3-none-any.whl", hash = "sha256:d9b8b771455a97c8a9f3ab3448ebe0b29b5e105f1228bba41028be116985a267"}, - {file = "setuptools-70.1.0.tar.gz", hash = "sha256:01a1e793faa5bd89abc851fa15d0a0db26f160890c7102cd8dce643e886b47f5"}, + {file = "setuptools-70.2.0-py3-none-any.whl", hash = "sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05"}, + {file = "setuptools-70.2.0.tar.gz", hash = "sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shellingham" diff --git a/pyproject.toml b/pyproject.toml index 6634f8f..cd29730 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["poetry-core>=1.0.0", "setuptools>=68.0.0" , "pybind11[global]>=2.11.1"] +requires = ["poetry-core>=1.0.0", "setuptools>=68.0.0" , "pybind11[global]>=2.12.0"] build-backend = "poetry.core.masonry.api" diff --git a/pyxcp/examples/ex_arrow.py b/pyxcp/examples/ex_arrow.py new file mode 100644 index 0000000..f03f840 --- /dev/null +++ b/pyxcp/examples/ex_arrow.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- + +import argparse +import logging +from array import array +from dataclasses import dataclass, field +from pathlib import Path + +# from pprint import pprint +from typing import Any + +import pyarrow as pa + +# import pyarrow.compute as pc +import pyarrow.parquet as pq + +from pyxcp.recorder import XcpLogFileUnfolder + + +MAP_TO_ARROW = { + "U8": pa.uint8(), + "I8": pa.int8(), + "U16": pa.uint16(), + "I16": pa.int16(), + "U32": pa.uint32(), + "I32": pa.int32(), + "U64": pa.uint64(), + "I64": pa.int64(), + "F32": pa.float32(), + "F64": pa.float64(), + "F16": pa.float16(), + # "BF16" +} + +MAP_TO_ARRAY = { + "U8": "B", + "I8": "b", + "U16": "H", + "I16": "h", + "U32": "L", + "I32": "l", + "U64": "Q", + "I64": "q", + "F32": "f", + "F64": "d", + "F16": "f", + # "BF16" +} + +logger = logging.getLogger("PyXCP") +# logger.setLevel(logging.INFO) + + +# sys.argv.append(r"C:\Users\Chris\PycharmProjects\pyxcp\pyxcp\examples\run_daq.xmraw") + +parser = argparse.ArgumentParser(description="Use .xmraw files in an Apache Arrow application.") +parser.add_argument("xmraw_file", help=".xmraw file") +args = parser.parse_args() + + +@dataclass +class Storage: + name: str + arrow_type: Any + arr: array + + +@dataclass +class StorageContainer: + name: str + arr: list[Storage] = field(default_factory=[]) + ts0: array[float] = field(default_factory=lambda: array("d")) + ts1: array[float] = field(default_factory=lambda: array("d")) + + +class Unfolder(XcpLogFileUnfolder): + + def initialize(self): + # print("initialize()") + self.arrow_tables = [] + for dl in self.daq_lists: + # print("=" * 80) + # print(dl.name) + result = [] + for name, type_str in dl.headers: + array_txpe = MAP_TO_ARRAY[type_str] + arrow_type = MAP_TO_ARROW[type_str] + sd = Storage(name, arrow_type, array(array_txpe)) + print(f"\t{name!r} {array_txpe} {arrow_type}", sd) + result.append(sd) + sc = StorageContainer(dl.name, result) + # print(sc) + self.arrow_tables.append(sc) + + def finalize(self) -> Any: + # print("finalize()") + result = [] + for arr in self.arrow_tables: + # timestamps = arr.ts0 + names = [] + data = [] + + # names = ["timestamp"] + # data = [timestamps] + for sd in arr.arr: + # print(sd.name, sd.arrow_type, len(sd.arr)) + adt = pa.array(sd.arr, type=sd.arrow_type) + names.append(sd.name) + data.append(adt) + table = pa.Table.from_arrays(data, names=names) + # table = table.append_column("timestamp", [timestamps]) + fname = f"{arr.name}.parquet" + print("Writing table", fname) + pq.write_table(table, fname) + print("done.", table.shape) + result.append(table) + # xcv = ValueHolder(result) + # print(xcv) + return result + + def on_daq_list(self, daq_list_num: int, timestamp0: float, timestamp1: float, measurements: list): + sc = self.arrow_tables[daq_list_num] + sc.ts0.append(timestamp0) + sc.ts1.append(timestamp1) + for idx, elem in enumerate(measurements): + sto = sc.arr[idx] + sto.arr.append(elem) + + +logger.info(f"Processing {args.xmraw_file!r}") +logger.info(f"Processing {Path(args.xmraw_file)!r}") + +lfr = Unfolder(args.xmraw_file) +res = lfr.run() +print(res) From e17c1dc5083b3b7278735f099586ed8f052943e3 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 8 Jul 2024 09:56:00 +0300 Subject: [PATCH 70/99] timestamps --- pyxcp/config/__init__.py | 6 +++ pyxcp/cpp_ext/__init__.py | 2 +- pyxcp/cpp_ext/extension_wrapper.cpp | 10 +++++ pyxcp/cpp_ext/helper.hpp | 39 ++++++++++++++++++ pyxcp/examples/run_daq.py | 6 +-- pyxcp/recorder/unfolder.hpp | 15 ++++--- pyxcp/recorder/wrap.cpp | 13 ++---- pyxcp/transport/base.py | 62 +++++++++++++++-------------- 8 files changed, 105 insertions(+), 48 deletions(-) diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 0f95e0a..8d7d731 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -793,6 +793,12 @@ class Transport(SingletonConfigurable): ["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=True, help="Choose one of the supported XCP transport layers." ).tag(config=True) create_daq_timestamps = Bool(False, help="Record time of frame reception or set timestamp to 0.").tag(config=True) + timestamp_mode = Enum( + ["ABSOLUTE", "RELATIVE"], + default_value="RELATIVE", + help="""Either absolute UTC timestamps since epoch (1-1-1970) +or program start. Both values are in nano seconds.""", + ).tag(config=True) timeout = Float( 2.0, help="""raise `XcpTimeoutError` after `timeout` seconds diff --git a/pyxcp/cpp_ext/__init__.py b/pyxcp/cpp_ext/__init__.py index 22473f2..cc30cfb 100644 --- a/pyxcp/cpp_ext/__init__.py +++ b/pyxcp/cpp_ext/__init__.py @@ -1 +1 @@ -from .cpp_ext import Bin, DaqList, McObject # noqa: F401 +from .cpp_ext import Bin, DaqList, McObject, Timestamp, TimestampType # noqa: F401 diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index cf1662b..72ab1b4 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -61,4 +61,14 @@ PYBIND11_MODULE(cpp_ext, m) { .def_property("odt_count", &DaqList::get_odt_count, nullptr) .def_property("total_entries", &DaqList::get_total_entries, nullptr) .def_property("total_length", &DaqList::get_total_length, nullptr); + + py::enum_(m, "TimestampType") + .value("ABSOLUTE_TS", TimestampType::ABSOLUTE_TS) + .value("RELATIVE_TS", TimestampType::RELATIVE_TS); + + py::class_(m, "Timestamp") + .def(py::init(), "ts_type"_a) + .def_property_readonly("absolute", &Timestamp::absolute) + .def_property_readonly("relative", &Timestamp::relative) + .def_property_readonly("value", &Timestamp::get_value); } diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index de37671..5c518c2 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -2,6 +2,7 @@ #if !defined(__HELPER_HPP) #define __HELPER_HPP + #include #include #include #include @@ -101,4 +102,42 @@ static std::map reverse_map(const std::map &m) { return result; } +enum class TimestampType : std::uint8_t { + ABSOLUTE_TS, + RELATIVE_TS +}; + +class Timestamp { + public: + + explicit Timestamp(TimestampType type) : m_type(type) { + m_initial = absolute(); + } + + Timestamp(const Timestamp &) = delete; + Timestamp(Timestamp &&) = delete; + + std::uint64_t get_value() const noexcept { + if (m_type == TimestampType::ABSOLUTE_TS) { + return absolute(); + } else if (m_type == TimestampType::RELATIVE_TS) { + return relative(); + } + } + + std::uint64_t absolute() const noexcept { + const std::chrono::time_point now = std::chrono::system_clock::now(); + return std::chrono::duration_cast(now.time_since_epoch()).count(); + } + + std::uint64_t relative() const noexcept { + return absolute() - m_initial; + } + + private: + + TimestampType m_type; + std::uint64_t m_initial; +}; + #endif // __HELPER_HPP diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 56324ef..bfd6c6a 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -10,8 +10,8 @@ ap = ArgumentParser(description="DAQ test") -XCP_LITE = True -# XCP_LITE = False +# XCP_LITE = True +XCP_LITE = False # Completly random configurations, only for illustrative purposes. # @@ -156,7 +156,7 @@ daq_parser.setup() # Run internal and XCP setup procedures. print("start DAQ lists.") daq_parser.start() # Start DAQ lists. - time.sleep(12 * 60.0 * 60.0) # Arbitrary termination condition.ls *. + time.sleep(1 * 60.0 * 60.0) # Arbitrary termination condition.ls *. # time.sleep(6 * 60.0 * 60.0) # Arbitrary termination condition.ls *. daq_parser.stop() # Stop DAQ lists. print("finalize DAQ lists.\n") diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 31e98f0..b0bd237 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -1108,19 +1108,22 @@ class DaqOnlinePolicy : public DAQPolicyBase { std::unique_ptr m_unfolder; }; - struct ValueHolder { - - ValueHolder() = delete; + ValueHolder() = delete; ValueHolder(const ValueHolder&) = default; - ValueHolder(const std::any& value) : m_value(value) {} - ValueHolder(std::any&& value) : m_value(std::move(value)) {} + + ValueHolder(const std::any& value) : m_value(value) { + } + + ValueHolder(std::any&& value) : m_value(std::move(value)) { + } std::any get_value() const noexcept { return m_value; } -private: + private: + std::any m_value; }; diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index e45fe8c..219a331 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -90,10 +90,7 @@ PYBIND11_MODULE(rekorder, m) { return ss.str(); }); - py::class_(m, "Deserializer") - .def(py::init()) - .def("run", &Deserializer::run) - ; + py::class_(m, "Deserializer").def(py::init()).def("run", &Deserializer::run); py::class_(m, "_PyXcpLogFileReader") .def(py::init()) @@ -178,12 +175,10 @@ PYBIND11_MODULE(rekorder, m) { .def_property_readonly("daq_lists", &XcpLogFileUnfolder::get_daq_lists) .def("get_header", &XcpLogFileUnfolder::get_header) .def("initialize", &XcpLogFileUnfolder::initialize) - .def("finalize", &XcpLogFileUnfolder::finalize) - ; + .def("finalize", &XcpLogFileUnfolder::finalize); py::class_(m, "ValueHolder") //.def(py::init()) - .def(py::init()) - .def_property_readonly("value", &ValueHolder::get_value) - ; + .def(py::init()) + .def_property_readonly("value", &ValueHolder::get_value); } diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index a8074d1..3bd5cd1 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -4,10 +4,11 @@ import typing from collections import deque from datetime import datetime -from time import perf_counter, sleep, time +from time import sleep import pyxcp.types as types +from ..cpp_ext import Timestamp, TimestampType from ..recorder import XcpLogFileWriter from ..timing import Timing from ..utils import SHORT_SLEEP, flatten, hexDump @@ -118,20 +119,6 @@ class EmptyFrameError(Exception): """Raised when an empty frame is received.""" -def get(q, timeout, restart_event): - """Get an item from a deque considering a timeout condition.""" - start = time() - while not q: - if restart_event.is_set(): - start = time() - restart_event.clear() - if time() - start > timeout: - raise EmptyFrameError - sleep(SHORT_SLEEP) - item = q.popleft() - return item - - class BaseTransport(metaclass=abc.ABCMeta): """Base class for transport-layers (Can, Eth, Sxi). @@ -158,6 +145,10 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.counterSend = 0 self.counterReceived = -1 self.create_daq_timestamps = config.create_daq_timestamps + + timestamp_mode = TimestampType.ABSOLUTE_TS if config.timestamp_mode == "ABSOLUTE" else TimestampType.RELATIVE_TS + self.timestamp = Timestamp(timestamp_mode) + self.alignment = config.alignment self.timeout = config.timeout self.timer_restart_event = threading.Event() @@ -171,12 +162,12 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.first_daq_timestamp = None - self.timestamp_origin = time() + self.timestamp_origin = self.timestamp.value self.datetime_origin = datetime.fromtimestamp(self.timestamp_origin) - self.pre_send_timestamp = time() - self.post_send_timestamp = time() - self.recv_timestamp = time() + self.pre_send_timestamp = self.timestamp.value + self.post_send_timestamp = self.timestamp.value + self.recv_timestamp = self.timestamp.value def __del__(self): self.finishListener() @@ -198,6 +189,19 @@ def close(self): def connect(self): pass + def get(self, q: deque, timeout: int, restart_event: threading.Event): + """Get an item from a deque considering a timeout condition.""" + start = self.timestamp.value + while not q: + if restart_event.is_set(): + start = self.timestamp.value + restart_event.clear() + if self.timestamp.value - start > timeout: + raise EmptyFrameError + sleep(SHORT_SLEEP) + item = q.popleft() + return item + def startListener(self): if self.listener.is_alive(): self.finishListener() @@ -215,10 +219,10 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): frame = self._prepare_request(cmd, *data) self.timing.start() with self.policy_lock: - self.policy.feed(types.FrameCategory.CMD, self.counterSend, perf_counter(), frame) + self.policy.feed(types.FrameCategory.CMD, self.counterSend, self.timestamp.value, frame) self.send(frame) try: - xcpPDU = get( + xcpPDU = self.get( self.resQueue, timeout=self.timeout, restart_event=self.timer_restart_event, @@ -227,7 +231,7 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): if not ignore_timeout: MSG = f"Response timed out (timeout={self.timeout}s)" with self.policy_lock: - self.policy.feed(types.FrameCategory.METADATA, self.counterSend, perf_counter(), bytes(MSG, "ascii")) + self.policy.feed(types.FrameCategory.METADATA, self.counterSend, self.timestamp.value, bytes(MSG, "ascii")) raise types.XcpTimeoutError(MSG) from None else: self.timing.stop() @@ -236,7 +240,7 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): pid = types.Response.parse(xcpPDU).type if pid == "ERR" and cmd.name != "SYNCH": with self.policy_lock: - self.policy.feed(types.FrameCategory.ERROR, self.counterReceived, perf_counter(), xcpPDU[1:]) + self.policy.feed(types.FrameCategory.ERROR, self.counterReceived, self.timestamp.value, xcpPDU[1:]) err = types.XcpError.parse(xcpPDU[1:]) raise types.XcpResponseError(err) @@ -271,7 +275,7 @@ def block_request(self, cmd, *data): self.policy.feed( types.FrameCategory.CMD if int(cmd) >= 0xC0 else types.FrameCategory.STIM, self.counterSend, - perf_counter(), + self.timestamp.value, frame, ) self.send(frame) @@ -320,13 +324,13 @@ def block_receive(self, length_required: int) -> bytes: :class:`pyxcp.types.XcpTimeoutError` """ block_response = b"" - start = time() + start = self.timestamp.value while len(block_response) < length_required: if len(self.resQueue): partial_response = self.resQueue.popleft() block_response += partial_response[1:] else: - if time() - start > self.timeout: + if self.timestamp.value - start > self.timeout: raise types.XcpTimeoutError("Response timed out [block_receive].") from None sleep(SHORT_SLEEP) return block_response @@ -367,15 +371,15 @@ def processResponse(self, response, length, counter, recv_timestamp=None): if pid >= 0xFE: self.resQueue.append(response) with self.policy_lock: - self.policy.feed(types.FrameCategory.RESPONSE, self.counterReceived, perf_counter(), response) + self.policy.feed(types.FrameCategory.RESPONSE, self.counterReceived, self.timestamp.value, response) self.recv_timestamp = recv_timestamp elif pid == 0xFD: self.process_event_packet(response) with self.policy_lock: - self.policy.feed(types.FrameCategory.EVENT, self.counterReceived, perf_counter(), response) + self.policy.feed(types.FrameCategory.EVENT, self.counterReceived, self.timestamp.value, response) elif pid == 0xFC: with self.policy_lock: - self.policy.feed(types.FrameCategory.SERV, self.counterReceived, perf_counter(), response) + self.policy.feed(types.FrameCategory.SERV, self.counterReceived, self.timestamp.value, response) else: if self._debug: self.logger.debug(f"<- L{length} C{counter} ODT_Data[0:8] {hexDump(response[:8])}") From 45fb30fa9ecba81c120af2f6f03525e28167fa12 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 8 Jul 2024 10:37:28 +0300 Subject: [PATCH 71/99] timestamps --- pyxcp/cpp_ext/extension_wrapper.cpp | 3 ++- pyxcp/cpp_ext/helper.hpp | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index 72ab1b4..e1bd7ac 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -70,5 +70,6 @@ PYBIND11_MODULE(cpp_ext, m) { .def(py::init(), "ts_type"_a) .def_property_readonly("absolute", &Timestamp::absolute) .def_property_readonly("relative", &Timestamp::relative) - .def_property_readonly("value", &Timestamp::get_value); + .def_property_readonly("value", &Timestamp::get_value) + .def_property_readonly("initial_value", &Timestamp::get_initial_value); } diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 5c518c2..94d5a07 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -6,6 +6,7 @@ #include #include #include + #include #if (__cplusplus >= 202302L) || (__STDC_VERSION__ >= 202302L) #include @@ -110,6 +111,9 @@ enum class TimestampType : std::uint8_t { class Timestamp { public: + using clock_variant = + std::variant; + explicit Timestamp(TimestampType type) : m_type(type) { m_initial = absolute(); } @@ -125,6 +129,10 @@ class Timestamp { } } + std::uint64_t get_initial_value() const noexcept { + return m_initial; + } + std::uint64_t absolute() const noexcept { const std::chrono::time_point now = std::chrono::system_clock::now(); return std::chrono::duration_cast(now.time_since_epoch()).count(); @@ -136,8 +144,9 @@ class Timestamp { private: - TimestampType m_type; - std::uint64_t m_initial; + TimestampType m_type; + std::uint64_t m_initial; + std::unique_ptr m_clk; }; #endif // __HELPER_HPP From 5bf21aea16fae58beb46a7c222ef3ad959ee0c5f Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 8 Jul 2024 16:23:39 +0300 Subject: [PATCH 72/99] today --- pyxcp/config/__init__.py | 7 ++-- pyxcp/cpp_ext/__init__.py | 9 ++++- pyxcp/cpp_ext/extension_wrapper.cpp | 8 ++++- pyxcp/cpp_ext/helper.hpp | 48 ++++++++++++++++++++++++--- pyxcp/scripts/xcp_info.py | 12 +++---- pyxcp/transport/base.py | 51 ++++++++++++++++------------- pyxcp/transport/can.py | 5 ++- pyxcp/transport/eth.py | 10 +++--- pyxcp/transport/sxi.py | 9 +++-- pyxcp/transport/usb_transport.py | 8 ++--- 10 files changed, 114 insertions(+), 53 deletions(-) diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 8d7d731..4b2d8c1 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -796,8 +796,11 @@ class Transport(SingletonConfigurable): timestamp_mode = Enum( ["ABSOLUTE", "RELATIVE"], default_value="RELATIVE", - help="""Either absolute UTC timestamps since epoch (1-1-1970) -or program start. Both values are in nano seconds.""", + help="""Either absolute timestamps since some epoch +or program start. Both values are in nano-seconds.""", + ).tag(config=True) + clock_type = Enum( + ["GPS", "SYSTEM", "TAI", "UTC"], default_value="UTC", help="""Change from UTC only if you know what are you doing.""" ).tag(config=True) timeout = Float( 2.0, diff --git a/pyxcp/cpp_ext/__init__.py b/pyxcp/cpp_ext/__init__.py index cc30cfb..91a16e2 100644 --- a/pyxcp/cpp_ext/__init__.py +++ b/pyxcp/cpp_ext/__init__.py @@ -1 +1,8 @@ -from .cpp_ext import Bin, DaqList, McObject, Timestamp, TimestampType # noqa: F401 +from .cpp_ext import ( # noqa: F401 + Bin, + ClockType, + DaqList, + McObject, + Timestamp, + TimestampType, +) diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index e1bd7ac..faaf3bb 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -66,8 +66,14 @@ PYBIND11_MODULE(cpp_ext, m) { .value("ABSOLUTE_TS", TimestampType::ABSOLUTE_TS) .value("RELATIVE_TS", TimestampType::RELATIVE_TS); + py::enum_(m, "ClockType") + .value("SYSTEM_CLK", ClockType::SYSTEM_CLK) + .value("GPS_CLK", ClockType::GPS_CLK) + .value("TAI_CLK", ClockType::TAI_CLK) + .value("UTC_CLK", ClockType::UTC_CLK); + py::class_(m, "Timestamp") - .def(py::init(), "ts_type"_a) + .def(py::init(), "ts_type"_a, "clock_type"_a) .def_property_readonly("absolute", &Timestamp::absolute) .def_property_readonly("relative", &Timestamp::relative) .def_property_readonly("value", &Timestamp::get_value) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 94d5a07..1b4fe32 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -108,14 +108,37 @@ enum class TimestampType : std::uint8_t { RELATIVE_TS }; +enum class ClockType : std::uint8_t { + SYSTEM_CLK, + GPS_CLK, + TAI_CLK, + UTC_CLK, +}; + class Timestamp { public: using clock_variant = std::variant; - explicit Timestamp(TimestampType type) : m_type(type) { + explicit Timestamp(TimestampType ts_type, ClockType clk_type) : m_type(ts_type) { + switch (clk_type) { + case ClockType::SYSTEM_CLK: + m_clk = std::make_unique(std::chrono::system_clock()); + break; + case ClockType::GPS_CLK: + m_clk = std::make_unique(std::chrono::gps_clock()); + break; + case ClockType::TAI_CLK: + m_clk = std::make_unique(std::chrono::tai_clock()); + break; + case ClockType::UTC_CLK: + m_clk = std::make_unique(std::chrono::utc_clock()); + break; + } + std::cout << static_cast(ts_type) << " : " << static_cast(clk_type) << std::endl; m_initial = absolute(); + std::cout << "initial: " << m_initial << std::endl; } Timestamp(const Timestamp &) = delete; @@ -134,8 +157,25 @@ class Timestamp { } std::uint64_t absolute() const noexcept { - const std::chrono::time_point now = std::chrono::system_clock::now(); - return std::chrono::duration_cast(now.time_since_epoch()).count(); + std::uint64_t current; + + std::visit( + [¤t](auto &&arg) { + using T = std::decay_t; + + if constexpr (std::is_same_v) { + current = std::chrono::duration_cast(arg.now().time_since_epoch()).count(); + } else if constexpr (std::is_same_v) { + current = std::chrono::duration_cast(arg.now().time_since_epoch()).count(); + } else if constexpr (std::is_same_v) { + current = std::chrono::duration_cast(arg.now().time_since_epoch()).count(); + } else if constexpr (std::is_same_v) { + current = std::chrono::duration_cast(arg.now().time_since_epoch()).count(); + } + }, + *m_clk + ); + return current; } std::uint64_t relative() const noexcept { @@ -145,8 +185,8 @@ class Timestamp { private: TimestampType m_type; - std::uint64_t m_initial; std::unique_ptr m_clk; + std::uint64_t m_initial; }; #endif // __HELPER_HPP diff --git a/pyxcp/scripts/xcp_info.py b/pyxcp/scripts/xcp_info.py index 52e9fed..e6d9a0e 100644 --- a/pyxcp/scripts/xcp_info.py +++ b/pyxcp/scripts/xcp_info.py @@ -19,12 +19,12 @@ print("=================") pprint(x.slaveProperties) - result = x.id_scanner() - print("\n") - print("Implemented IDs:") - print("================") - for key, value in result.items(): - print(f"{key}: {value}", end="\n\n") + # result = x.id_scanner() + # print("\n") + # print("Implemented IDs:") + # print("================") + # for key, value in result.items(): + # print(f"{key}: {value}", end="\n\n") cps = x.getCurrentProtectionStatus() print("\nProtection Status") print("=================") diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 3bd5cd1..b5b4828 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -3,12 +3,13 @@ import threading import typing from collections import deque -from datetime import datetime + +# from datetime import datetime from time import sleep import pyxcp.types as types -from ..cpp_ext import Timestamp, TimestampType +from ..cpp_ext import ClockType, Timestamp, TimestampType from ..recorder import XcpLogFileWriter from ..timing import Timing from ..utils import SHORT_SLEEP, flatten, hexDump @@ -35,7 +36,7 @@ def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] def filtered_out(self) -> typing.Set[types.FrameCategory]: return self._frame_types_to_filter_out - def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None: ... # noqa: E704 + def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: ... # noqa: E704 def finalize(self, *args) -> None: """ @@ -77,7 +78,7 @@ def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] types.FrameCategory.STIM: self.stimQueue, } - def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None: + def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: # print(f"{frame_type.name:8} {counter:6} {timestamp:7.7f} {hexDump(payload)}") if frame_type not in self.filtered_out: self.QUEUE_MAP.get(frame_type).append((counter, timestamp, payload)) @@ -96,7 +97,7 @@ def __init__( super().__init__(filter_out) self.recorder = XcpLogFileWriter(file_name, prealloc=prealloc, chunk_size=chunk_size) - def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None: + def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: if frame_type not in self.filtered_out: self.recorder.add_frame(frame_type, counter, timestamp, payload) @@ -110,9 +111,9 @@ class StdoutPolicy(FrameAcquisitionPolicy): def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None): super().__init__(filter_out) - def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: float, payload: bytes) -> None: + def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: if frame_type not in self.filtered_out: - print(f"{frame_type.name:8} {counter:6} {timestamp:7.7f} {hexDump(payload)}") + print(f"{frame_type.name:8} {counter:6} {timestamp:8u} {hexDump(payload)}") class EmptyFrameError(Exception): @@ -145,9 +146,16 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.counterSend = 0 self.counterReceived = -1 self.create_daq_timestamps = config.create_daq_timestamps - + if config.clock_type == "UTC": + clock_type = ClockType.UTC_CLK + elif config.clock_type == "TAI": + clock_type = ClockType.TAI_CLK + elif config.clock_type == "SYSTEM": + clock_type = ClockType.SYSTEM_CLK + elif config.clock_type == "GPS": + clock_type = ClockType.GPS_CLK timestamp_mode = TimestampType.ABSOLUTE_TS if config.timestamp_mode == "ABSOLUTE" else TimestampType.RELATIVE_TS - self.timestamp = Timestamp(timestamp_mode) + self.timestamp = Timestamp(timestamp_mode, clock_type) self.alignment = config.alignment self.timeout = config.timeout @@ -162,8 +170,8 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.first_daq_timestamp = None - self.timestamp_origin = self.timestamp.value - self.datetime_origin = datetime.fromtimestamp(self.timestamp_origin) + # self.timestamp_origin = self.timestamp.value + # self.datetime_origin = datetime.fromtimestamp(self.timestamp_origin) self.pre_send_timestamp = self.timestamp.value self.post_send_timestamp = self.timestamp.value @@ -189,17 +197,19 @@ def close(self): def connect(self): pass - def get(self, q: deque, timeout: int, restart_event: threading.Event): + def get(self): """Get an item from a deque considering a timeout condition.""" start = self.timestamp.value - while not q: - if restart_event.is_set(): + while not self.resQueue: + if self.timer_restart_event.is_set(): start = self.timestamp.value - restart_event.clear() - if self.timestamp.value - start > timeout: + self.timer_restart_event.restart_event.clear() + if self.timestamp.value - start > self.timeout: + # print("*E*") raise EmptyFrameError sleep(SHORT_SLEEP) - item = q.popleft() + item = self.resQueue.popleft() + # print("Q", item) return item def startListener(self): @@ -222,11 +232,7 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): self.policy.feed(types.FrameCategory.CMD, self.counterSend, self.timestamp.value, frame) self.send(frame) try: - xcpPDU = self.get( - self.resQueue, - timeout=self.timeout, - restart_event=self.timer_restart_event, - ) + xcpPDU = self.get() except EmptyFrameError: if not ignore_timeout: MSG = f"Response timed out (timeout={self.timeout}s)" @@ -243,7 +249,6 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): self.policy.feed(types.FrameCategory.ERROR, self.counterReceived, self.timestamp.value, xcpPDU[1:]) err = types.XcpError.parse(xcpPDU[1:]) raise types.XcpResponseError(err) - return xcpPDU[1:] def request(self, cmd, *data): diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index dc4719a..a9c0673 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -5,7 +5,6 @@ import functools import operator from bisect import bisect_left -from time import time from typing import Any, Dict, Optional from can import CanError, CanInitializationError, Message, detect_available_configs @@ -389,9 +388,9 @@ def connect(self): def send(self, frame: bytes) -> None: # send the request - self.pre_send_timestamp = time() + self.pre_send_timestamp = self.timestamp.value self.can_interface.transmit(payload=padFrame(frame, self.max_dlc_required, self.padding_value)) - self.post_send_timestamp = time() + self.post_send_timestamp = self.timestamp.value def closeConnection(self): if hasattr(self, "can_interface"): diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 766e0a8..ccce0a7 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -4,7 +4,7 @@ import struct import threading from collections import deque -from time import sleep, time +from time import sleep from pyxcp.transport.base import BaseTransport from pyxcp.utils import SHORT_SLEEP @@ -142,7 +142,7 @@ def _packet_listen(self): sel = select(0.02) for _, events in sel: if events & EVENT_READ: - recv_timestamp = time() + recv_timestamp = self.timestamp.value if use_tcp: response = sock_recv(RECV_SIZE) @@ -154,6 +154,7 @@ def _packet_listen(self): _packets.append((response, recv_timestamp)) else: response, _ = sock_recv(Eth.MAX_DATAGRAM_SIZE) + print("U:", response) if not response: self.sock.close() self.status = 0 @@ -190,6 +191,7 @@ def listen(self): for _ in range(count): bts, timestamp = popleft() + print("P:", bts, timestamp) data += bts current_size = len(data) @@ -219,9 +221,9 @@ def listen(self): break def send(self, frame): - self.pre_send_timestamp = time() + self.pre_send_timestamp = self.timestamp.value self.sock.send(frame) - self.post_send_timestamp = time() + self.post_send_timestamp = self.timestamp.value def closeConnection(self): if not self.invalidSocket: diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index 473fb82..1fef3cc 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -3,7 +3,6 @@ import struct from collections import deque from dataclasses import dataclass -from time import time import serial @@ -109,9 +108,9 @@ def listen(self): if not self.comm_port.inWaiting(): continue - recv_timestamp = time() + recv_timestamp = self.timestamp.value header_values = self.unpacker(self.HEADER.unpack(self.comm_port.read(self.HEADER_SIZE))) - length, counter, _git = header_values.length, header_values.counter, header_values.filler + length, counter, _ = header_values.length, header_values.counter, header_values.filler response = self.comm_port.read(length) self.timing.stop() @@ -121,9 +120,9 @@ def listen(self): self.processResponse(response, length, counter, recv_timestamp) def send(self, frame): - self.pre_send_timestamp = time() + self.pre_send_timestamp = self.timestamp.value self.comm_port.write(frame) - self.post_send_timestamp = time() + self.post_send_timestamp = self.timestamp.value def closeConnection(self): if hasattr(self, "comm_port") and self.comm_port.isOpen(): diff --git a/pyxcp/transport/usb_transport.py b/pyxcp/transport/usb_transport.py index 9072511..947a656 100644 --- a/pyxcp/transport/usb_transport.py +++ b/pyxcp/transport/usb_transport.py @@ -3,7 +3,7 @@ import threading from array import array from collections import deque -from time import perf_counter, sleep, time +from time import perf_counter, sleep import usb.backend.libusb0 as libusb0 import usb.backend.libusb1 as libusb1 @@ -132,7 +132,7 @@ def _packet_listen(self): if close_event_set(): return try: - recv_timestamp = time() + recv_timestamp = self.timestamp.value read_count = read(buffer, 100) # 100ms timeout if read_count != RECV_SIZE: _packets.append((buffer_view[:read_count].tobytes(), recv_timestamp)) @@ -209,7 +209,7 @@ def listen(self): break def send(self, frame): - self.pre_send_timestamp = time() + self.pre_send_timestamp = self.timestamp.value try: self.out_ep.write(frame) except (USBError, USBTimeoutError): @@ -218,7 +218,7 @@ def send(self, frame): # Ignore this here since a Timeout error will be raised anyway if # the device does not respond pass - self.post_send_timestamp = time() + self.post_send_timestamp = self.timestamp.value def closeConnection(self): if self.device is not None: From d26a12983152f8ccb090341e9bde55ff801cf1de Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 9 Jul 2024 21:07:08 +0300 Subject: [PATCH 73/99] today() --- poetry.lock | 2 +- pyproject.toml | 1 + pyxcp/config/__init__.py | 3 +-- pyxcp/cpp_ext/extension_wrapper.cpp | 3 ++- pyxcp/cpp_ext/helper.hpp | 10 +++++--- pyxcp/daq_stim/__init__.py | 10 ++++---- pyxcp/examples/run_daq.py | 2 +- pyxcp/master/master.py | 5 ++++ pyxcp/recorder/rekorder.hpp | 10 ++++---- pyxcp/recorder/unfolder.hpp | 34 ++++++++++++++------------- pyxcp/recorder/wrap.cpp | 6 +++-- pyxcp/recorder/writer.hpp | 2 +- pyxcp/scripts/xcp_info.py | 13 +++++----- pyxcp/transport/base.py | 22 +++++++++++++---- pyxcp/transport/base_transport.hpp | 0 pyxcp/transport/can.py | 6 +++-- pyxcp/transport/eth.py | 3 --- pyxcp/transport/transport_wrapper.cpp | 0 pyxcp/types.py | 18 ++++++++++++++ pyxcp/utils.py | 25 ++++++++++++++++++++ 20 files changed, 122 insertions(+), 53 deletions(-) create mode 100644 pyxcp/transport/base_transport.hpp create mode 100644 pyxcp/transport/transport_wrapper.cpp diff --git a/poetry.lock b/poetry.lock index 0ac5d4c..c8646d9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2451,4 +2451,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "0a5222b7a2b78258d8cc489cf52717186e6bc11eb8d0fb6bd5077142aeed0000" +content-hash = "2afd9ffcdd5ad1a6dbc0ef02a986f769bf99859fc19fb4e96e2e1e73f1773a29" diff --git a/pyproject.toml b/pyproject.toml index cd29730..4050721 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,6 +85,7 @@ line-profiler-pycharm = "^1.1.0" toml = "^0.10.2" bandit = "^1.7.8" tomlkit = "^0.12.5" +pytz = "^2024.1" [tool.poetry.group.dev.dependencies] ruff = "^0.1.0" diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 4b2d8c1..dd23b2d 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -796,8 +796,7 @@ class Transport(SingletonConfigurable): timestamp_mode = Enum( ["ABSOLUTE", "RELATIVE"], default_value="RELATIVE", - help="""Either absolute timestamps since some epoch -or program start. Both values are in nano-seconds.""", + help="""Either absolute timestamps since some epoch or program start, both values are in nano-seconds.""", ).tag(config=True) clock_type = Enum( ["GPS", "SYSTEM", "TAI", "UTC"], default_value="UTC", help="""Change from UTC only if you know what are you doing.""" diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index faaf3bb..c368ce0 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -77,5 +77,6 @@ PYBIND11_MODULE(cpp_ext, m) { .def_property_readonly("absolute", &Timestamp::absolute) .def_property_readonly("relative", &Timestamp::relative) .def_property_readonly("value", &Timestamp::get_value) - .def_property_readonly("initial_value", &Timestamp::get_initial_value); + .def_property_readonly("initial_value", &Timestamp::get_initial_value) + .def_property_readonly("current_time_zone_name", &Timestamp::get_current_time_zone_name); } diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 1b4fe32..3a4f122 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -136,9 +136,8 @@ class Timestamp { m_clk = std::make_unique(std::chrono::utc_clock()); break; } - std::cout << static_cast(ts_type) << " : " << static_cast(clk_type) << std::endl; - m_initial = absolute(); - std::cout << "initial: " << m_initial << std::endl; + m_initial = absolute(); + m_current_time_zone_name = std::chrono::current_zone()->name(); } Timestamp(const Timestamp &) = delete; @@ -156,6 +155,10 @@ class Timestamp { return m_initial; } + std::string get_current_time_zone_name() const noexcept { + return m_current_time_zone_name; + } + std::uint64_t absolute() const noexcept { std::uint64_t current; @@ -187,6 +190,7 @@ class Timestamp { TimestampType m_type; std::unique_ptr m_clk; std::uint64_t m_initial; + std::string m_current_time_zone_name; }; #endif // __HELPER_HPP diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 88748b0..a1a5c10 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -11,6 +11,7 @@ from pyxcp.recorder import DaqOnlinePolicy as _DaqOnlinePolicy from pyxcp.recorder import DaqRecorderPolicy as _DaqRecorderPolicy from pyxcp.recorder import MeasurementParameters +from pyxcp.utils import CurrentDatetime DAQ_ID_FIELD_SIZE = { @@ -33,9 +34,8 @@ def __init__(self, daq_lists: List[DaqList]): self.daq_lists = daq_lists self.log = get_application().log - def setup(self, write_multiple: bool = True): + def setup(self, start_datetime: CurrentDatetime | None = None, write_multiple: bool = True): self.daq_info = self.xcp_master.getDaqInfo() - # pprint(self.daq_info) try: processor = self.daq_info.get("processor") properties = processor.get("properties") @@ -49,9 +49,9 @@ def setup(self, write_multiple: bool = True): mode = resolution.get("timestampMode") self.ts_fixed = mode.get("fixed") self.ts_size = DAQ_TIMESTAMP_SIZE[mode.get("size")] - ts_unit_exp = types.DAQ_TIMESTAMP_UNIT_TO_EXP[mode.get("unit")] + ts_factor = types.DAQ_TIMESTAMP_UNIT_TO_NS[mode.get("unit")] ts_ticks = resolution.get("timestampTicks") - self.ts_scale_factor = (10**ts_unit_exp) * ts_ticks + self.ts_scale_factor = ts_factor * ts_ticks else: self.ts_size = 0 self.ts_fixed = False @@ -131,6 +131,8 @@ def setup(self, write_multiple: bool = True): ) res = self.xcp_master.startStopDaqList(0x02, i) self._first_pids.append(res.firstPid) + if start_datetime: + pass self.measurement_params = MeasurementParameters( byte_order, header_len, diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index bfd6c6a..40abbf2 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -53,7 +53,7 @@ "pwm_stuff", 2, False, - False, + True, [ ("channel1", 0x1BD004, 0, "F32"), ("period", 0x001C0028, 0, "F32"), diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 6036a75..1267a20 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -1936,6 +1936,11 @@ def generate(): raise RuntimeError(f"Error while scanning for ID {id_value}: {response!r}") return result + @property + def start_datetime(self) -> int: + """""" + return self.transport.start_datetime + def try_command(self, cmd: Callable, *args, **kws) -> Tuple[types.TryCommandResult, Any]: """Call master functions and handle XCP errors more gracefuly. diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 8eebab8..e0b74de 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -107,16 +107,16 @@ using payload_t = py::array_t; struct frame_header_t { std::uint8_t category{ 0 }; - std::uint16_t counter{ 0 }; - double timestamp{ 0.0 }; - std::uint16_t length{ 0 }; + std::uint16_t counter{ 0U }; + std::uint64_t timestamp{ 0ULL }; + std::uint16_t length{ 0U }; }; #pragma pack(pop) -using FrameTuple = std::tuple; +using FrameTuple = std::tuple; using FrameVector = std::vector; -using FrameTupleWriter = std::tuple; +using FrameTupleWriter = std::tuple; enum class FrameCategory : std::uint8_t { META, diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index b0bd237..c638bc7 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -16,8 +16,8 @@ #include "writer.hpp" using measurement_value_t = std::variant; -using measurement_tuple_t = std::tuple>; -using measurement_callback_t = std::function)>; +using measurement_tuple_t = std::tuple>; +using measurement_callback_t = std::function)>; template auto get_value(blob_t const * buf, std::uint64_t offset) -> Ty { @@ -849,8 +849,8 @@ class DaqListState { m_initial_offset(initial_offset), m_next_odt(0), m_current_idx(0), - m_timestamp0(0.0), - m_timestamp1(0.0), + m_timestamp0(0ULL), + m_timestamp1(0ULL), m_state(state_t::IDLE), m_buffer{}, m_flatten_odts(flatten_odts), @@ -884,7 +884,7 @@ class DaqListState { return m_state; } - bool feed(uint16_t odt_num, double timestamp, const std::string& payload) { + bool feed(uint16_t odt_num, std::uint64_t timestamp, const std::string& payload) { auto state = check_state(odt_num); auto finished = false; @@ -912,7 +912,7 @@ class DaqListState { void resetSM() { m_state = state_t::IDLE; m_next_odt = 0; - m_timestamp0 = 0.0; + m_timestamp0 = 0ULL; } void parse_Odt(uint16_t odt_num, const std::string& payload) { @@ -924,10 +924,10 @@ class DaqListState { m_current_idx = 0; if (m_params.m_timestamps_supported && (m_params.m_ts_fixed || (m_params.m_selectable_timestamps && m_enable_timestamps == true))) { - m_timestamp1 = static_cast(m_getter.get_timestamp(payload_data)) * m_params.m_ts_scale_factor; + m_timestamp1 = static_cast(m_getter.get_timestamp(payload_data) * m_params.m_ts_scale_factor); offset += m_params.m_ts_size; } else { - m_timestamp1 = 0.0; + m_timestamp1 = 0ULL; } } @@ -954,8 +954,8 @@ class DaqListState { std::uint16_t m_initial_offset; std::uint16_t m_next_odt = 0; std::uint16_t m_current_idx = 0; - double m_timestamp0 = 0.0; - double m_timestamp1 = 0.0; + std::uint64_t m_timestamp0 = 0ULL; + std::uint64_t m_timestamp1 = 0ULL; state_t m_state = state_t::IDLE; std::vector m_buffer; flatten_odts_t m_flatten_odts; @@ -979,7 +979,7 @@ class DAQProcessor { DAQProcessor() = delete; virtual ~DAQProcessor() = default; - std::optional feed(double timestamp, const std::string& payload) noexcept { + std::optional feed(std::uint64_t timestamp, const std::string& payload) noexcept { const auto data = reinterpret_cast(payload.data()); auto [daq_num, odt_num] = m_getter.get_id(data); @@ -1023,7 +1023,7 @@ class DAQPolicyBase { initialize(); } - virtual void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) = 0; + virtual void feed(std::uint8_t frame_cat, std::uint16_t counter, std::uint64_t timestamp, const std::string& payload) = 0; virtual void initialize() = 0; @@ -1044,7 +1044,7 @@ class DaqRecorderPolicy : public DAQPolicyBase { DAQPolicyBase::set_parameters(params); } - void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) override { + void feed(std::uint8_t frame_cat, std::uint16_t counter, std::uint64_t timestamp, const std::string& payload) override { if (frame_cat != static_cast(FrameCategory::DAQ)) { // Only record DAQ frames for now. return; @@ -1083,10 +1083,11 @@ class DaqOnlinePolicy : public DAQPolicyBase { } virtual void on_daq_list( - std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + std::uint16_t daq_list_num, std::uint64_t timestamp0, std::uint64_t timestamp1, + const std::vector& measurement ) = 0; - void feed(std::uint8_t frame_cat, std::uint16_t counter, double timestamp, const std::string& payload) { + void feed(std::uint8_t frame_cat, std::uint16_t counter, std::uint64_t timestamp, const std::string& payload) { if (frame_cat != static_cast(FrameCategory::DAQ)) { return; } @@ -1186,7 +1187,8 @@ class XcpLogFileUnfolder { } virtual void on_daq_list( - std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + std::uint16_t daq_list_num, std::uint64_t timestamp0, std::uint64_t timestamp1, + const std::vector& measurement ) = 0; MeasurementParameters get_parameters() const { diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 219a331..9a6e824 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -19,7 +19,8 @@ class PyDaqOnlinePolicy : public DaqOnlinePolicy { using DaqOnlinePolicy::DaqOnlinePolicy; void on_daq_list( - std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + std::uint16_t daq_list_num, std::uint64_t timestamp0, std::uint64_t timestamp1, + const std::vector& measurement ) override { PYBIND11_OVERRIDE_PURE(void, DaqOnlinePolicy, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); } @@ -53,7 +54,8 @@ class PyXcpLogFileUnfolder : public XcpLogFileUnfolder { using XcpLogFileUnfolder::XcpLogFileUnfolder; void on_daq_list( - std::uint16_t daq_list_num, double timestamp0, double timestamp1, const std::vector& measurement + std::uint16_t daq_list_num, std::uint64_t timestamp0, std::uint64_t timestamp1, + const std::vector& measurement ) override { PYBIND11_OVERRIDE_PURE(void, XcpLogFileUnfolder, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index db3e8ba..37b7426 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -83,7 +83,7 @@ class XcpLogFileWriter { } } - void add_frame(uint8_t category, uint16_t counter, double timestamp, uint16_t length, char const *data) { + void add_frame(uint8_t category, uint16_t counter, std::uint64_t timestamp, uint16_t length, char const *data) { auto payload = new char[length]; _fcopy(payload, data, length); diff --git a/pyxcp/scripts/xcp_info.py b/pyxcp/scripts/xcp_info.py index e6d9a0e..8db4281 100644 --- a/pyxcp/scripts/xcp_info.py +++ b/pyxcp/scripts/xcp_info.py @@ -19,12 +19,12 @@ print("=================") pprint(x.slaveProperties) - # result = x.id_scanner() - # print("\n") - # print("Implemented IDs:") - # print("================") - # for key, value in result.items(): - # print(f"{key}: {value}", end="\n\n") + result = x.id_scanner() + print("\n") + print("Implemented IDs:") + print("================") + for key, value in result.items(): + print(f"{key}: {value}", end="\n\n") cps = x.getCurrentProtectionStatus() print("\nProtection Status") print("=================") @@ -101,5 +101,4 @@ if daq_id == 0: print("N/A") x.disconnect() - print("\nDone.") diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index b5b4828..8edeb3e 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -12,7 +12,13 @@ from ..cpp_ext import ClockType, Timestamp, TimestampType from ..recorder import XcpLogFileWriter from ..timing import Timing -from ..utils import SHORT_SLEEP, flatten, hexDump +from ..utils import ( + SHORT_SLEEP, + CurrentDatetime, + flatten, + hexDump, + seconds_to_nanoseconds, +) class FrameAcquisitionPolicy: @@ -143,8 +149,8 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.logger = config.log self._debug = self.logger.level == 10 - self.counterSend = 0 - self.counterReceived = -1 + self.counterSend: int = 0 + self.counterReceived: int = -1 self.create_daq_timestamps = config.create_daq_timestamps if config.clock_type == "UTC": clock_type = ClockType.UTC_CLK @@ -157,8 +163,10 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): timestamp_mode = TimestampType.ABSOLUTE_TS if config.timestamp_mode == "ABSOLUTE" else TimestampType.RELATIVE_TS self.timestamp = Timestamp(timestamp_mode, clock_type) + # Reference point for UTC timestamps. + self._start_datetime = CurrentDatetime(self.timestamp.initial_value, self.timestamp.current_time_zone_name) self.alignment = config.alignment - self.timeout = config.timeout + self.timeout = seconds_to_nanoseconds(config.timeout) self.timer_restart_event = threading.Event() self.timing = Timing() self.resQueue = deque() @@ -205,13 +213,17 @@ def get(self): start = self.timestamp.value self.timer_restart_event.restart_event.clear() if self.timestamp.value - start > self.timeout: - # print("*E*") raise EmptyFrameError sleep(SHORT_SLEEP) item = self.resQueue.popleft() # print("Q", item) return item + @property + def start_datetime(self) -> int: + """""" + return self._start_datetime + def startListener(self): if self.listener.is_alive(): self.finishListener() diff --git a/pyxcp/transport/base_transport.hpp b/pyxcp/transport/base_transport.hpp new file mode 100644 index 0000000..e69de29 diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index a9c0673..0aaee8e 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -14,6 +14,8 @@ from pyxcp.config import CAN_INTERFACE_MAP from pyxcp.transport.base import BaseTransport +from ..utils import seconds_to_nanoseconds + console = Console() @@ -279,7 +281,7 @@ def read(self) -> Optional[Frame]: id_=identifier, dlc=frame.dlc, data=frame.data, - timestamp=frame.timestamp, + timestamp=seconds_to_nanoseconds(frame.timestamp), ) def getTimestampResolution(self) -> int: @@ -358,7 +360,7 @@ def get_interface_parameters(self) -> Dict[str, Any]: result[name] = value return result - def dataReceived(self, payload: bytes, recv_timestamp: float = None): + def dataReceived(self, payload: bytes, recv_timestamp: int = None): self.processResponse( payload, len(payload), diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index ccce0a7..5b07f91 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -154,7 +154,6 @@ def _packet_listen(self): _packets.append((response, recv_timestamp)) else: response, _ = sock_recv(Eth.MAX_DATAGRAM_SIZE) - print("U:", response) if not response: self.sock.close() self.status = 0 @@ -191,8 +190,6 @@ def listen(self): for _ in range(count): bts, timestamp = popleft() - print("P:", bts, timestamp) - data += bts current_size = len(data) current_position = 0 diff --git a/pyxcp/transport/transport_wrapper.cpp b/pyxcp/transport/transport_wrapper.cpp new file mode 100644 index 0000000..e69de29 diff --git a/pyxcp/types.py b/pyxcp/types.py index a9e8338..f4489c4 100644 --- a/pyxcp/types.py +++ b/pyxcp/types.py @@ -916,6 +916,7 @@ class Event(enum.IntEnum): DbgLlbtResponse = Struct(Padding(1), "length" / Int16u, "data" / Int8ul[this.length]) +# Convert to seconds. DAQ_TIMESTAMP_UNIT_TO_EXP = { "DAQ_TIMESTAMP_UNIT_1PS": -12, "DAQ_TIMESTAMP_UNIT_10PS": -11, @@ -932,6 +933,23 @@ class Event(enum.IntEnum): "DAQ_TIMESTAMP_UNIT_1S": 0, } +# Convert to nano-seconds. +DAQ_TIMESTAMP_UNIT_TO_NS = { + "DAQ_TIMESTAMP_UNIT_1PS": 0.001, + "DAQ_TIMESTAMP_UNIT_10PS": 0.01, + "DAQ_TIMESTAMP_UNIT_100PS": 0.1, + "DAQ_TIMESTAMP_UNIT_1NS": 1, + "DAQ_TIMESTAMP_UNIT_10NS": 10, + "DAQ_TIMESTAMP_UNIT_100NS": 100, + "DAQ_TIMESTAMP_UNIT_1US": 1000, + "DAQ_TIMESTAMP_UNIT_10US": 10 * 1000, + "DAQ_TIMESTAMP_UNIT_100US": 100 * 1000, + "DAQ_TIMESTAMP_UNIT_1MS": 1000 * 1000, + "DAQ_TIMESTAMP_UNIT_10MS": 10 * 1000 * 1000, + "DAQ_TIMESTAMP_UNIT_100MS": 100 * 1000 * 1000, + "DAQ_TIMESTAMP_UNIT_1S": 1000 * 1000 * 1000, +} + EVENT_CHANNEL_TIME_UNIT_TO_EXP = { "EVENT_CHANNEL_TIME_UNIT_1PS": -12, "EVENT_CHANNEL_TIME_UNIT_10PS": -11, diff --git a/pyxcp/utils.py b/pyxcp/utils.py index 7eb3949..cd284ee 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +import datetime import functools import operator import sys @@ -6,6 +7,7 @@ from time import perf_counter import chardet +import pytz def hexDump(arr): @@ -28,6 +30,10 @@ def hexDump(arr): return "[{}]".format(" ".join([f"{x:02x}" for x in arr])) +def seconds_to_nanoseconds(value: float) -> int: + return int(value * 1000 * 1000 * 1000) + + def slicer(iterable, sliceLength, converter=None): if converter is None: converter = type(iterable) @@ -70,3 +76,22 @@ def delay(amount: float): start = perf_counter() while perf_counter() < start + amount: pass + + +class CurrentDatetime: + + def __init__(self, timestamp_ns: int, tz_name: str): + self.timestamp_ns = timestamp_ns + self.timezone = pytz.timezone(tz_name) + self._dt = datetime.datetime.fromtimestamp(timestamp_ns / (1000.0 * 1000.0 * 1000.0)) + self.utc_offset = int(self.timezone.utcoffset(self._dt).total_seconds() / 60) + self.dst_offset = int(self.timezone.dst(self._dt).total_seconds() / 60) + + def __str__(self): + return f"""CurrentDatetime( + datetime="{self._dt!s}", + timezone="{self.timezone}", + timestamp_ns={self.timestamp_ns}, + utc_offset={self.utc_offset}, + dst_offset={self.dst_offset} +)""" From 24bb2da8973c08965cff504f527639028588fa5b Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 10 Jul 2024 11:22:56 +0300 Subject: [PATCH 74/99] today() --- pyxcp/cpp_ext/helper.hpp | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 3a4f122..21849e8 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -2,7 +2,13 @@ #if !defined(__HELPER_HPP) #define __HELPER_HPP +#if defined(_WIN32) || defined(_WIN64) #include +#else + #include + #include +#endif + #include #include #include @@ -118,10 +124,11 @@ enum class ClockType : std::uint8_t { class Timestamp { public: - using clock_variant = - std::variant; + //using clock_variant = std::variant; explicit Timestamp(TimestampType ts_type, ClockType clk_type) : m_type(ts_type) { + + #if defined(_WIN32) || defined(_WIN64) switch (clk_type) { case ClockType::SYSTEM_CLK: m_clk = std::make_unique(std::chrono::system_clock()); @@ -136,10 +143,16 @@ class Timestamp { m_clk = std::make_unique(std::chrono::utc_clock()); break; } - m_initial = absolute(); m_current_time_zone_name = std::chrono::current_zone()->name(); + #else + tzset(); + + #endif // _WIN32 || _WIN64 + m_initial = absolute(); } + + Timestamp(const Timestamp &) = delete; Timestamp(Timestamp &&) = delete; @@ -162,6 +175,7 @@ class Timestamp { std::uint64_t absolute() const noexcept { std::uint64_t current; +#if defined(_WIN32) || defined(_WIN64) std::visit( [¤t](auto &&arg) { using T = std::decay_t; @@ -178,6 +192,12 @@ class Timestamp { }, *m_clk ); +#else + // On MacOS `clock_gettime_nsec_np` could be used. + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + current = static_cast(ts.tv_sec) * 1'000'000'000 + ts.tv_nsec; +#endif // _WIN32 || _WIN64 return current; } @@ -188,7 +208,11 @@ class Timestamp { private: TimestampType m_type; +#if defined(_WIN32) || defined(_WIN64) std::unique_ptr m_clk; + #else + + #endif // _WIN32 || _WIN64 std::uint64_t m_initial; std::string m_current_time_zone_name; }; From 0e73a88d30a49a9712093451b12ae2f5188b6fee Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 10 Jul 2024 18:13:23 +0300 Subject: [PATCH 75/99] today() --- pyxcp/config/__init__.py | 3 - pyxcp/cpp_ext/__init__.py | 2 +- pyxcp/cpp_ext/extension_wrapper.cpp | 24 ++++-- pyxcp/cpp_ext/helper.hpp | 127 ++++++++++++++-------------- pyxcp/transport/base.py | 18 ++-- pyxcp/utils.py | 19 +++-- 6 files changed, 98 insertions(+), 95 deletions(-) diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index dd23b2d..d789fa1 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -798,9 +798,6 @@ class Transport(SingletonConfigurable): default_value="RELATIVE", help="""Either absolute timestamps since some epoch or program start, both values are in nano-seconds.""", ).tag(config=True) - clock_type = Enum( - ["GPS", "SYSTEM", "TAI", "UTC"], default_value="UTC", help="""Change from UTC only if you know what are you doing.""" - ).tag(config=True) timeout = Float( 2.0, help="""raise `XcpTimeoutError` after `timeout` seconds diff --git a/pyxcp/cpp_ext/__init__.py b/pyxcp/cpp_ext/__init__.py index 91a16e2..4a07cae 100644 --- a/pyxcp/cpp_ext/__init__.py +++ b/pyxcp/cpp_ext/__init__.py @@ -1,8 +1,8 @@ from .cpp_ext import ( # noqa: F401 Bin, - ClockType, DaqList, McObject, Timestamp, + TimestampInfo, TimestampType, ) diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index c368ce0..25aeb22 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -14,6 +14,12 @@ namespace py = pybind11; using namespace pybind11::literals; +class PyTimestampInfo : public TimestampInfo { + public: + + using TimestampInfo::TimestampInfo; +}; + PYBIND11_MODULE(cpp_ext, m) { m.doc() = "C++ extensions for pyXCP."; @@ -66,17 +72,17 @@ PYBIND11_MODULE(cpp_ext, m) { .value("ABSOLUTE_TS", TimestampType::ABSOLUTE_TS) .value("RELATIVE_TS", TimestampType::RELATIVE_TS); - py::enum_(m, "ClockType") - .value("SYSTEM_CLK", ClockType::SYSTEM_CLK) - .value("GPS_CLK", ClockType::GPS_CLK) - .value("TAI_CLK", ClockType::TAI_CLK) - .value("UTC_CLK", ClockType::UTC_CLK); - py::class_(m, "Timestamp") - .def(py::init(), "ts_type"_a, "clock_type"_a) + .def(py::init(), "ts_type"_a) .def_property_readonly("absolute", &Timestamp::absolute) .def_property_readonly("relative", &Timestamp::relative) .def_property_readonly("value", &Timestamp::get_value) - .def_property_readonly("initial_value", &Timestamp::get_initial_value) - .def_property_readonly("current_time_zone_name", &Timestamp::get_current_time_zone_name); + .def_property_readonly("initial_value", &Timestamp::get_initial_value); + + py::class_(m, "TimestampInfo", py::dynamic_attr()) + .def(py::init()) + .def_property_readonly("timestamp_ns", &TimestampInfo::get_timestamp_ns) + .def_property("utc_offset", &TimestampInfo::get_utc_offset, &TimestampInfo::set_utc_offset) + .def_property("dst_offset", &TimestampInfo::get_dst_offset, &TimestampInfo::set_dst_offset) + .def_property("timezone", &TimestampInfo::get_timezone, &TimestampInfo::set_timezone); } diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 21849e8..cf449a9 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -2,12 +2,12 @@ #if !defined(__HELPER_HPP) #define __HELPER_HPP -#if defined(_WIN32) || defined(_WIN64) - #include -#else - #include - #include -#endif + #if defined(_WIN32) || defined(_WIN64) + #include + #else + #include + #include + #endif #include #include @@ -114,44 +114,67 @@ enum class TimestampType : std::uint8_t { RELATIVE_TS }; -enum class ClockType : std::uint8_t { - SYSTEM_CLK, - GPS_CLK, - TAI_CLK, - UTC_CLK, -}; - -class Timestamp { +class TimestampInfo { public: - //using clock_variant = std::variant; - - explicit Timestamp(TimestampType ts_type, ClockType clk_type) : m_type(ts_type) { - + explicit TimestampInfo(std::uint64_t timestamp_ns) : m_timestamp_ns(timestamp_ns) { #if defined(_WIN32) || defined(_WIN64) - switch (clk_type) { - case ClockType::SYSTEM_CLK: - m_clk = std::make_unique(std::chrono::system_clock()); - break; - case ClockType::GPS_CLK: - m_clk = std::make_unique(std::chrono::gps_clock()); - break; - case ClockType::TAI_CLK: - m_clk = std::make_unique(std::chrono::tai_clock()); - break; - case ClockType::UTC_CLK: - m_clk = std::make_unique(std::chrono::utc_clock()); - break; - } - m_current_time_zone_name = std::chrono::current_zone()->name(); + m_timezone = std::chrono::current_zone()->name(); #else tzset(); + time_t rawtime = time(timestamp_ns / 1'000'000'000); + struct tm *timeinfo; + timeinfo = localtime(&rawtime); + std::copy(std::begin(timeinfo->tm_zone), std::end(timeinfo->tm_zone), std::back_inserter(m_timezone)); + #endif // _WIN32 || _WIN64 - m_initial = absolute(); } + std::string get_timezone() const noexcept { + return m_timezone; + } + + void set_timezone(const std::string &value) noexcept { + m_timezone = value; + } + + std::uint64_t get_timestamp_ns() const noexcept { + return m_timestamp_ns; + } + + void set_utc_offset(std::int16_t value) noexcept { + m_utc_offset = value; + } + + std::int16_t get_utc_offset() const noexcept { + return m_utc_offset; + } + + void set_dst_offset(std::int16_t value) noexcept { + m_dst_offset = value; + } + + std::int16_t get_dst_offset() const noexcept { + return m_dst_offset; + } + + virtual void dummy() const noexcept {}; + private: + + std::uint64_t m_timestamp_ns; + std::string m_timezone{}; + std::int16_t m_utc_offset{ 0 }; + std::int16_t m_dst_offset{ 0 }; +}; + +class Timestamp { + public: + + explicit Timestamp(TimestampType ts_type) : m_type(ts_type) { + m_initial = absolute(); + } Timestamp(const Timestamp &) = delete; Timestamp(Timestamp &&) = delete; @@ -168,36 +191,17 @@ class Timestamp { return m_initial; } - std::string get_current_time_zone_name() const noexcept { - return m_current_time_zone_name; - } - std::uint64_t absolute() const noexcept { std::uint64_t current; -#if defined(_WIN32) || defined(_WIN64) - std::visit( - [¤t](auto &&arg) { - using T = std::decay_t; - - if constexpr (std::is_same_v) { - current = std::chrono::duration_cast(arg.now().time_since_epoch()).count(); - } else if constexpr (std::is_same_v) { - current = std::chrono::duration_cast(arg.now().time_since_epoch()).count(); - } else if constexpr (std::is_same_v) { - current = std::chrono::duration_cast(arg.now().time_since_epoch()).count(); - } else if constexpr (std::is_same_v) { - current = std::chrono::duration_cast(arg.now().time_since_epoch()).count(); - } - }, - *m_clk - ); -#else + #if defined(_WIN32) || defined(_WIN64) + current = std::chrono::duration_cast(m_clk.now().time_since_epoch()).count(); + #else // On MacOS `clock_gettime_nsec_np` could be used. timespec ts; clock_gettime(CLOCK_REALTIME, &ts); current = static_cast(ts.tv_sec) * 1'000'000'000 + ts.tv_nsec; -#endif // _WIN32 || _WIN64 + #endif // _WIN32 || _WIN64 return current; } @@ -207,14 +211,13 @@ class Timestamp { private: - TimestampType m_type; -#if defined(_WIN32) || defined(_WIN64) - std::unique_ptr m_clk; + TimestampType m_type; + #if defined(_WIN32) || defined(_WIN64) + std::chrono::utc_clock m_clk; #else #endif // _WIN32 || _WIN64 - std::uint64_t m_initial; - std::string m_current_time_zone_name; + std::uint64_t m_initial; }; #endif // __HELPER_HPP diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 8edeb3e..7926c64 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -9,7 +9,7 @@ import pyxcp.types as types -from ..cpp_ext import ClockType, Timestamp, TimestampType +from ..cpp_ext import Timestamp, TimestampType from ..recorder import XcpLogFileWriter from ..timing import Timing from ..utils import ( @@ -152,19 +152,13 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.counterSend: int = 0 self.counterReceived: int = -1 self.create_daq_timestamps = config.create_daq_timestamps - if config.clock_type == "UTC": - clock_type = ClockType.UTC_CLK - elif config.clock_type == "TAI": - clock_type = ClockType.TAI_CLK - elif config.clock_type == "SYSTEM": - clock_type = ClockType.SYSTEM_CLK - elif config.clock_type == "GPS": - clock_type = ClockType.GPS_CLK timestamp_mode = TimestampType.ABSOLUTE_TS if config.timestamp_mode == "ABSOLUTE" else TimestampType.RELATIVE_TS - self.timestamp = Timestamp(timestamp_mode, clock_type) + self.timestamp = Timestamp(timestamp_mode) - # Reference point for UTC timestamps. - self._start_datetime = CurrentDatetime(self.timestamp.initial_value, self.timestamp.current_time_zone_name) + # Reference point for timestamping (may relative). + self._start_datetime = CurrentDatetime(self.timestamp.initial_value) + + print(self._start_datetime) self.alignment = config.alignment self.timeout = seconds_to_nanoseconds(config.timeout) self.timer_restart_event = threading.Event() diff --git a/pyxcp/utils.py b/pyxcp/utils.py index cd284ee..8d1d8c5 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -9,6 +9,8 @@ import chardet import pytz +from pyxcp.cpp_ext import TimestampInfo + def hexDump(arr): if isinstance(arr, (bytes, bytearray)): @@ -31,7 +33,7 @@ def hexDump(arr): def seconds_to_nanoseconds(value: float) -> int: - return int(value * 1000 * 1000 * 1000) + return int(value * 1_000_000_000) def slicer(iterable, sliceLength, converter=None): @@ -78,14 +80,15 @@ def delay(amount: float): pass -class CurrentDatetime: +class CurrentDatetime(TimestampInfo): - def __init__(self, timestamp_ns: int, tz_name: str): - self.timestamp_ns = timestamp_ns - self.timezone = pytz.timezone(tz_name) - self._dt = datetime.datetime.fromtimestamp(timestamp_ns / (1000.0 * 1000.0 * 1000.0)) - self.utc_offset = int(self.timezone.utcoffset(self._dt).total_seconds() / 60) - self.dst_offset = int(self.timezone.dst(self._dt).total_seconds() / 60) + def __init__(self, timestamp_ns: int): + TimestampInfo.__init__(self, timestamp_ns) + # self.timestamp_ns = timestamp_ns + timezone = pytz.timezone(self.timezone) + self._dt = datetime.datetime.fromtimestamp(timestamp_ns / 1_000_000_000.0) + self.utc_offset = int(timezone.utcoffset(self._dt).total_seconds() / 60) + self.dst_offset = int(timezone.dst(self._dt).total_seconds() / 60) def __str__(self): return f"""CurrentDatetime( From 9c6a1d263ddf03b097dcd0da7e1371d787938e95 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 10 Jul 2024 16:05:42 +0000 Subject: [PATCH 76/99] today() --- pyxcp/cpp_ext/helper.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index cf449a9..935b6f3 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -122,11 +122,12 @@ class TimestampInfo { m_timezone = std::chrono::current_zone()->name(); #else tzset(); - - time_t rawtime = time(timestamp_ns / 1'000'000'000); + time_t rawtime = static_cast(timestamp_ns / 1'000'000'000); + //time_t rawtime = time(&value); struct tm *timeinfo; timeinfo = localtime(&rawtime); - std::copy(std::begin(timeinfo->tm_zone), std::end(timeinfo->tm_zone), std::back_inserter(m_timezone)); + //std::copy(std::begin(timeinfo->tm_zone), std::end(timeinfo->tm_zone), std::back_inserter(m_timezone)); + m_timezone = timeinfo->tm_zone; #endif // _WIN32 || _WIN64 } From 8a2ec9dcec3ca2719849dd4e1c88aebbf1699209 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 10 Jul 2024 19:54:27 +0300 Subject: [PATCH 77/99] today() --- pyxcp/cpp_ext/helper.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 935b6f3..f61e53f 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -127,7 +127,8 @@ class TimestampInfo { struct tm *timeinfo; timeinfo = localtime(&rawtime); //std::copy(std::begin(timeinfo->tm_zone), std::end(timeinfo->tm_zone), std::back_inserter(m_timezone)); - m_timezone = timeinfo->tm_zone; + //m_timezone = timeinfo->tm_zone; + m_timezone = tzname[0]; #endif // _WIN32 || _WIN64 } From 85338df761b20b63bc433fabd998fcd826211353 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Fri, 12 Jul 2024 06:37:40 +0300 Subject: [PATCH 78/99] today() --- pyproject.toml | 3 ++- pyxcp/cpp_ext/helper.hpp | 17 +++++++++-------- pyxcp/daq_stim/__init__.py | 6 ++++++ pyxcp/examples/run_daq.py | 4 +++- pyxcp/recorder/unfolder.hpp | 36 +++++++++++++++++++++++++++++++++--- pyxcp/recorder/wrap.cpp | 5 +++-- pyxcp/transport/base.py | 3 --- pyxcp/transport/eth.py | 2 +- pyxcp/utils.py | 1 - 9 files changed, 57 insertions(+), 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4050721..5e7ffb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,8 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12" + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.12" ] build = "build_ext.py" include = [ diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index f61e53f..603607e 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -117,17 +117,18 @@ enum class TimestampType : std::uint8_t { class TimestampInfo { public: + TimestampInfo() : m_timestamp_ns(0), m_timezone{}, m_utc_offset(0), m_dst_offset(0) { + } + + TimestampInfo(std::uint64_t timestamp_ns, const std::string &timezone, std::int16_t utc_offset, std::int16_t dst_offset) : + m_timestamp_ns(timestamp_ns), m_timezone(timezone), m_utc_offset(utc_offset), m_dst_offset(dst_offset) { + } + explicit TimestampInfo(std::uint64_t timestamp_ns) : m_timestamp_ns(timestamp_ns) { #if defined(_WIN32) || defined(_WIN64) m_timezone = std::chrono::current_zone()->name(); #else tzset(); - time_t rawtime = static_cast(timestamp_ns / 1'000'000'000); - //time_t rawtime = time(&value); - struct tm *timeinfo; - timeinfo = localtime(&rawtime); - //std::copy(std::begin(timeinfo->tm_zone), std::end(timeinfo->tm_zone), std::back_inserter(m_timezone)); - //m_timezone = timeinfo->tm_zone; m_timezone = tzname[0]; #endif // _WIN32 || _WIN64 @@ -178,8 +179,8 @@ class Timestamp { m_initial = absolute(); } - Timestamp(const Timestamp &) = delete; - Timestamp(Timestamp &&) = delete; + Timestamp(const Timestamp &) = default; + Timestamp(Timestamp &&) = default; std::uint64_t get_value() const noexcept { if (m_type == TimestampType::ABSOLUTE_TS) { diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index a1a5c10..a4ffef0 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # from pprint import pprint +from time import time_ns from typing import List from pyxcp import types @@ -36,6 +37,10 @@ def __init__(self, daq_lists: List[DaqList]): def setup(self, start_datetime: CurrentDatetime | None = None, write_multiple: bool = True): self.daq_info = self.xcp_master.getDaqInfo() + if start_datetime is None: + start_datetime = CurrentDatetime(time_ns()) + self.start_datetime = start_datetime + print(self.start_datetime) try: processor = self.daq_info.get("processor") properties = processor.get("properties") @@ -143,6 +148,7 @@ def setup(self, start_datetime: CurrentDatetime | None = None, write_multiple: b self.ts_scale_factor, self.ts_size, self.min_daq, + self.start_datetime, self.daq_lists, self._first_pids, ) diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 40abbf2..65d4585 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -156,7 +156,9 @@ daq_parser.setup() # Run internal and XCP setup procedures. print("start DAQ lists.") daq_parser.start() # Start DAQ lists. - time.sleep(1 * 60.0 * 60.0) # Arbitrary termination condition.ls *. + + time.sleep(15.0 * 60.0) + # time.sleep(1 * 60.0 * 60.0) # Arbitrary termination condition.ls *. # time.sleep(6 * 60.0 * 60.0) # Arbitrary termination condition.ls *. daq_parser.stop() # Stop DAQ lists. print("finalize DAQ lists.\n") diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index c638bc7..a4155c0 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -536,7 +536,7 @@ struct MeasurementParameters { explicit MeasurementParameters( std::uint8_t byte_order, std::uint8_t id_field_size, bool timestamps_supported, bool ts_fixed, bool prescaler_supported, bool selectable_timestamps, double ts_scale_factor, std::uint8_t ts_size, std::uint16_t min_daq, - const std::vector& daq_lists, const std::vector& first_pids + const TimestampInfo& timestamp_info, const std::vector& daq_lists, const std::vector& first_pids ) : m_byte_order(byte_order), m_id_field_size(id_field_size), @@ -547,6 +547,7 @@ struct MeasurementParameters { m_ts_scale_factor(ts_scale_factor), m_ts_size(ts_size), m_min_daq(min_daq), + m_timestamp_info(timestamp_info), m_daq_lists(daq_lists), m_first_pids(first_pids) { } @@ -563,7 +564,12 @@ struct MeasurementParameters { ss << to_binary(m_ts_scale_factor); ss << to_binary(m_ts_size); ss << to_binary(m_min_daq); - + //// + ss << to_binary(m_timestamp_info.get_timestamp_ns()); + //ss << to_binary(m_timestamp_info.get_timezone()); + ss << to_binary(m_timestamp_info.get_utc_offset()); + ss << to_binary(m_timestamp_info.get_dst_offset()); + //// std::size_t dl_count = m_daq_lists.size(); ss << to_binary(dl_count); for (const auto& daq_list : m_daq_lists) { @@ -615,6 +621,10 @@ struct MeasurementParameters { return m_min_daq; } + auto get_timestamp_info() const noexcept { + return m_timestamp_info; + } + auto get_daq_lists() const noexcept { return m_daq_lists; } @@ -632,6 +642,7 @@ struct MeasurementParameters { double m_ts_scale_factor; std::uint8_t m_ts_size; std::uint16_t m_min_daq; + TimestampInfo m_timestamp_info; std::vector m_daq_lists; std::vector m_first_pids; }; @@ -655,7 +666,12 @@ class Deserializer { std::size_t dl_count; std::vector daq_lists; std::size_t fp_count; + std::uint64_t timestamp_ns; + std::int16_t utc_offset; + std::int16_t dst_offset; + std::string timezone; std::vector first_pids; + // TimestampInfo timestamp_info{ 0 }; byte_order = from_binary(); id_field_size = from_binary(); @@ -668,6 +684,20 @@ class Deserializer { min_daq = from_binary(); dl_count = from_binary(); + //// + timestamp_ns = from_binary(); + std::cout << "TS: " << timestamp_ns << std::endl; + //timezone = from_binary_str(); + //std::cout << "TZ: " << timezone << std::endl; + utc_offset = from_binary(); + std::cout << "UTC:" << utc_offset << std::endl; + dst_offset = from_binary(); + std::cout << "DST:" << dst_offset << std::endl; + + //TimestampInfo timestamp_info{ timestamp_ns, timezone, utc_offset, dst_offset }; + TimestampInfo timestamp_info(0); + //// + for (std::size_t i = 0; i < dl_count; i++) { daq_lists.push_back(create_daq_list()); } @@ -679,7 +709,7 @@ class Deserializer { return MeasurementParameters( byte_order, id_field_size, timestamps_supported, ts_fixed, prescaler_supported, selectable_timestamps, ts_scale_factor, - ts_size, min_daq, daq_lists, first_pids + ts_size, min_daq, timestamp_info, daq_lists, first_pids ); } diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 9a6e824..7bd40dc 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -111,8 +111,8 @@ PYBIND11_MODULE(rekorder, m) { py::class_(m, "MeasurementParameters") .def(py::init< - std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const std::vector&, - const std::vector&>()) + std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const TimestampInfo&, + const std::vector&, const std::vector&>()) .def("dumps", [](const MeasurementParameters& self) { return py::bytes(self.dumps()); }) .def( "__repr__", @@ -150,6 +150,7 @@ PYBIND11_MODULE(rekorder, m) { .def_property_readonly("ts_scale_factor", &MeasurementParameters::get_ts_scale_factor) .def_property_readonly("ts_size", &MeasurementParameters::get_ts_size) .def_property_readonly("min_daq", &MeasurementParameters::get_min_daq) + .def_property_readonly("timestamp_info", &MeasurementParameters::get_timestamp_info) .def_property_readonly("daq_lists", &MeasurementParameters::get_daq_lists) .def_property_readonly("first_pids", &MeasurementParameters::get_first_pids); diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 7926c64..eb2e9f4 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -154,11 +154,8 @@ def __init__(self, config, policy: FrameAcquisitionPolicy = None): self.create_daq_timestamps = config.create_daq_timestamps timestamp_mode = TimestampType.ABSOLUTE_TS if config.timestamp_mode == "ABSOLUTE" else TimestampType.RELATIVE_TS self.timestamp = Timestamp(timestamp_mode) - # Reference point for timestamping (may relative). self._start_datetime = CurrentDatetime(self.timestamp.initial_value) - - print(self._start_datetime) self.alignment = config.alignment self.timeout = seconds_to_nanoseconds(config.timeout) self.timer_restart_event = threading.Event() diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 5b07f91..9d3d64e 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -27,7 +27,7 @@ def socket_to_str(sock: socket.socket) -> str: } family = AF.get(sock.family, "OTHER") typ = TYPE.get(sock.type, "UNKNOWN") - res = f"Connected to: {peer[0]}:{peer[1]} local address: {local[0]}:{local[1]} [{family}][{typ}]" + res = f"XCPonEth - Connected to: {peer[0]}:{peer[1]} local address: {local[0]}:{local[1]} [{family}][{typ}]" return res diff --git a/pyxcp/utils.py b/pyxcp/utils.py index 8d1d8c5..9e7579d 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -84,7 +84,6 @@ class CurrentDatetime(TimestampInfo): def __init__(self, timestamp_ns: int): TimestampInfo.__init__(self, timestamp_ns) - # self.timestamp_ns = timestamp_ns timezone = pytz.timezone(self.timezone) self._dt = datetime.datetime.fromtimestamp(timestamp_ns / 1_000_000_000.0) self.utc_offset = int(timezone.utcoffset(self._dt).total_seconds() / 60) From e900952f1abf18f7dbd87ff7574fff3f1ed531a5 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Fri, 12 Jul 2024 10:07:37 +0300 Subject: [PATCH 79/99] constructors --- pyxcp/cpp_ext/extension_wrapper.cpp | 2 ++ pyxcp/cpp_ext/helper.hpp | 5 +++++ pyxcp/utils.py | 2 ++ 3 files changed, 9 insertions(+) diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index 25aeb22..3e96cae 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -81,6 +81,8 @@ PYBIND11_MODULE(cpp_ext, m) { py::class_(m, "TimestampInfo", py::dynamic_attr()) .def(py::init()) + .def(py::init()) + .def_property_readonly("timestamp_ns", &TimestampInfo::get_timestamp_ns) .def_property("utc_offset", &TimestampInfo::get_utc_offset, &TimestampInfo::set_utc_offset) .def_property("dst_offset", &TimestampInfo::get_dst_offset, &TimestampInfo::set_dst_offset) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 603607e..6c588db 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -117,6 +117,11 @@ enum class TimestampType : std::uint8_t { class TimestampInfo { public: + TimestampInfo(const TimestampInfo&) = default; + TimestampInfo(TimestampInfo&&) = default; + TimestampInfo& operator=(const TimestampInfo&) = default; + TimestampInfo& operator=(TimestampInfo&&) = default; + TimestampInfo() : m_timestamp_ns(0), m_timezone{}, m_utc_offset(0), m_dst_offset(0) { } diff --git a/pyxcp/utils.py b/pyxcp/utils.py index 9e7579d..21f82c4 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -97,3 +97,5 @@ def __str__(self): utc_offset={self.utc_offset}, dst_offset={self.dst_offset} )""" + + __repr__ = __str__() From 6b97e3f868fab3f79b3c336c8425fb626e8374f2 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Fri, 12 Jul 2024 20:03:14 +0300 Subject: [PATCH 80/99] today() --- build_ext.py | 2 +- pyxcp/config/__init__.py | 4 ++-- pyxcp/cpp_ext/helper.hpp | 11 +++++++++++ pyxcp/examples/run_daq.py | 24 ++++++++++++------------ pyxcp/examples/xcp_recording.py | 8 ++++++++ pyxcp/recorder/unfolder.hpp | 22 ++++++++++------------ pyxcp/recorder/wrap.cpp | 4 +++- pyxcp/utils.py | 4 ++-- 8 files changed, 49 insertions(+), 30 deletions(-) diff --git a/build_ext.py b/build_ext.py index 34f00a9..838213a 100644 --- a/build_ext.py +++ b/build_ext.py @@ -78,4 +78,4 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": includes = subprocess.getoutput("pybind11-config --cmakedir") # nosec os.environ["pybind11_DIR"] = includes - build_extension(False) + build_extension(True) diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index d789fa1..2ab56c8 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -795,7 +795,7 @@ class Transport(SingletonConfigurable): create_daq_timestamps = Bool(False, help="Record time of frame reception or set timestamp to 0.").tag(config=True) timestamp_mode = Enum( ["ABSOLUTE", "RELATIVE"], - default_value="RELATIVE", + default_value="ABSOLUTE", help="""Either absolute timestamps since some epoch or program start, both values are in nano-seconds.""", ).tag(config=True) timeout = Float( @@ -990,7 +990,7 @@ def read_configuration_file(self, file_name: str, emit_warning: bool = True): aliases = Dict( # type:ignore[assignment] dict( - c="PyXCP.config_file", + c="PyXCP.config_file", # Application log_level="PyXCP.log_level", l="PyXCP.log_level", ) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 6c588db..84b5b8a 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -167,6 +167,17 @@ class TimestampInfo { return m_dst_offset; } + std::string to_string() const noexcept { + std::stringstream ss; + ss << "TimestamInfo(\n"; + ss << "\ttimestamp_ns=" << m_timestamp_ns << ",\n"; + ss << "\ttimezone=\"" << m_timezone << "\",\n"; + ss << "\tutc_offset=" << m_utc_offset << ",\n"; + ss << "\tdst_offset=" << m_dst_offset << "\n"; + ss << ");"; + return ss.str(); + } + virtual void dummy() const noexcept {}; private: diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 65d4585..9680421 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -10,8 +10,8 @@ ap = ArgumentParser(description="DAQ test") -# XCP_LITE = True -XCP_LITE = False +XCP_LITE = True +# XCP_LITE = False # Completly random configurations, only for illustrative purposes. # @@ -26,10 +26,10 @@ False, False, [ - ("byteCounter", 0x1D48080, 0, "U8"), # - ("wordCounter", 0x1D48082, 0, "U16"), - ("dwordCounter", 0x1D48084, 0, "U32"), - ("sbyteCounter", 0x1D48088, 0, "I8"), + ("byteCounter", 0x114A0, 0, "U8"), # + ("wordCounter", 0x114A2, 0, "U16"), + ("dwordCounter", 0x114A4, 0, "U32"), + ("sbyteCounter", 0x114A8, 0, "I8"), ], ), DaqList( @@ -38,11 +38,11 @@ False, False, [ - ("swordCounter", 0x1D4808A, 0, "I16"), - ("sdwordCounter", 0x1D4808C, 0, "I32"), - ("channel1", 0x1D48068, 0, "F64"), - ("channel2", 0x1D48070, 0, "F64"), - ("channel3", 0x1D48078, 0, "F64"), + ("swordCounter", 0x114AA, 0, "I16"), + ("sdwordCounter", 0x114AC, 0, "I32"), + ("channel1", 0x10088, 0, "F64"), + ("channel2", 0x10090, 0, "F64"), + ("channel3", 0x10098, 0, "F64"), ], ), ] @@ -142,7 +142,6 @@ ] """ # daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. - daq_parser = DaqRecorder(DAQ_LISTS, "run_daq", 2) with ap.run(policy=daq_parser) as x: @@ -160,6 +159,7 @@ time.sleep(15.0 * 60.0) # time.sleep(1 * 60.0 * 60.0) # Arbitrary termination condition.ls *. # time.sleep(6 * 60.0 * 60.0) # Arbitrary termination condition.ls *. + print("Stop DAQ....") daq_parser.stop() # Stop DAQ lists. print("finalize DAQ lists.\n") x.disconnect() diff --git a/pyxcp/examples/xcp_recording.py b/pyxcp/examples/xcp_recording.py index 076c9e3..2dea0f5 100644 --- a/pyxcp/examples/xcp_recording.py +++ b/pyxcp/examples/xcp_recording.py @@ -16,8 +16,14 @@ class Unfolder(XcpLogFileUnfolder): + prev = 0 + wraps = 0 + def on_daq_list(self, daq_list_num, timestamp0, timestamp1, measurement): + if timestamp1 < self.prev: + self.wraps += 1 print(daq_list_num, timestamp0, timestamp1, measurement) + self.prev = timestamp1 lfr = Unfolder(args.xmraw_file) @@ -30,6 +36,8 @@ def on_daq_list(self, daq_list_num, timestamp0, timestamp1, measurement): print("=" * 80) print("=" * 80) print(lfr.daq_lists) +print(lfr.parameters.timestamp_info) +print("Wrap-arounds:", lfr.wraps) sys.exit() reader = XcpLogFileReader(args.xmraw_file) diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index a4155c0..bd1345d 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -564,14 +564,15 @@ struct MeasurementParameters { ss << to_binary(m_ts_scale_factor); ss << to_binary(m_ts_size); ss << to_binary(m_min_daq); + std::size_t dl_count = m_daq_lists.size(); + ss << to_binary(dl_count); //// ss << to_binary(m_timestamp_info.get_timestamp_ns()); - //ss << to_binary(m_timestamp_info.get_timezone()); + ss << to_binary(m_timestamp_info.get_timezone()); ss << to_binary(m_timestamp_info.get_utc_offset()); ss << to_binary(m_timestamp_info.get_dst_offset()); //// - std::size_t dl_count = m_daq_lists.size(); - ss << to_binary(dl_count); + for (const auto& daq_list : m_daq_lists) { ss << daq_list.dumps(); } @@ -686,17 +687,15 @@ class Deserializer { //// timestamp_ns = from_binary(); - std::cout << "TS: " << timestamp_ns << std::endl; - //timezone = from_binary_str(); + //std::cout << "TS: " << timestamp_ns << std::endl; + timezone = from_binary_str(); //std::cout << "TZ: " << timezone << std::endl; utc_offset = from_binary(); - std::cout << "UTC:" << utc_offset << std::endl; + //std::cout << "UTC:" << utc_offset << std::endl; dst_offset = from_binary(); - std::cout << "DST:" << dst_offset << std::endl; + //std::cout << "DST:" << dst_offset << std::endl; - //TimestampInfo timestamp_info{ timestamp_ns, timezone, utc_offset, dst_offset }; - TimestampInfo timestamp_info(0); - //// + TimestampInfo timestamp_info{ timestamp_ns, timezone, utc_offset, dst_offset }; for (std::size_t i = 0; i < dl_count; i++) { daq_lists.push_back(create_daq_list()); @@ -839,8 +838,6 @@ class Deserializer { return tmp; } - // template<> - // inline std::string from_binary_str() { inline std::string from_binary_str() { auto length = from_binary(); std::string result; @@ -1090,6 +1087,7 @@ class DaqRecorderPolicy : public DAQPolicyBase { } void finalize() override { + std::cout << "DaqRecorderPolicy::finalize()\n"; m_writer->finalize(); } diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 7bd40dc..5ff6f4a 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -128,6 +128,7 @@ PYBIND11_MODULE(rekorder, m) { ss << "ts_scale_factor=" << self.m_ts_scale_factor << ", "; ss << "ts_size=" << static_cast(self.m_ts_size) << ", "; ss << "min_daq=" << static_cast(self.m_min_daq) << ", "; + ss << "timestamp=" << self.get_timestamp_info().to_string() << ", "; ss << "daq_lists=[\n"; for (const auto& dl : self.m_daq_lists) { ss << dl.to_string() << ",\n"; @@ -152,7 +153,8 @@ PYBIND11_MODULE(rekorder, m) { .def_property_readonly("min_daq", &MeasurementParameters::get_min_daq) .def_property_readonly("timestamp_info", &MeasurementParameters::get_timestamp_info) .def_property_readonly("daq_lists", &MeasurementParameters::get_daq_lists) - .def_property_readonly("first_pids", &MeasurementParameters::get_first_pids); + .def_property_readonly("first_pids", &MeasurementParameters::get_first_pids) + .def_property_readonly("timestamp_info", &MeasurementParameters::get_timestamp_info); py::class_(m, "DaqRecorderPolicy", py::dynamic_attr()) .def(py::init<>()) diff --git a/pyxcp/utils.py b/pyxcp/utils.py index 21f82c4..e6c5ce7 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -97,5 +97,5 @@ def __str__(self): utc_offset={self.utc_offset}, dst_offset={self.dst_offset} )""" - - __repr__ = __str__() + + __repr__ = __str__ From 14a409a3fb767c09bf58745cd3c65b20b43a3e31 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sat, 13 Jul 2024 10:00:04 +0300 Subject: [PATCH 81/99] today() --- CMakeLists.txt | 2 +- pyxcp/examples/ex_mdf.py | 134 +++++++++++++++++++++++++++++++++++++++ pyxcp/recorder/wrap.cpp | 2 +- pyxcp/utils.py | 8 +-- 4 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 pyxcp/examples/ex_mdf.py diff --git a/CMakeLists.txt b/CMakeLists.txt index aad08ce..63a28ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ SET(MSVC_BASE_OPTIONS "/W3 /permissive- /EHsc /bigobj /Zc:__cplusplus /std:c++la if (CMAKE_BUILD_TYPE STREQUAL "Debug") if (MSVC) - SET(MSVC_BASE_OPTIONS "${MSVC_BASE_OPTIONS} /Od /fsanitize=address /Zi") + SET(MSVC_BASE_OPTIONS "${MSVC_BASE_OPTIONS} /Od /fsanitize=address /Zi") else() SET(MSVC_BASE_OPTIONS "${MSVC_BASE_OPTIONS} -Og -g3 -ggdb --fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fsanitize=bounds") # -fsanitize=hwaddress endif() diff --git a/pyxcp/examples/ex_mdf.py b/pyxcp/examples/ex_mdf.py new file mode 100644 index 0000000..ece0057 --- /dev/null +++ b/pyxcp/examples/ex_mdf.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- + +import argparse +import logging +from array import array +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +import numpy as np +from asammdf import MDF, Signal +from asammdf.blocks.v4_blocks import HeaderBlock # ChannelGroup +from asammdf.blocks.v4_constants import FLAG_HD_TIME_OFFSET_VALID # FLAG_HD_LOCAL_TIME, + +from pyxcp.recorder import XcpLogFileUnfolder + + +MAP_TO_NP = { + "U8": np.uint8, + "I8": np.int8, + "U16": np.uint16, + "I16": np.int16, + "U32": np.uint32, + "I32": np.int32, + "U64": np.uint64, + "I64": np.int64, + "F32": np.float32, + "F64": np.float64, + "F16": np.float16, + # "BF16" +} + +MAP_TO_ARRAY = { + "U8": "B", + "I8": "b", + "U16": "H", + "I16": "h", + "U32": "L", + "I32": "l", + "U64": "Q", + "I64": "q", + "F32": "f", + "F64": "d", + "F16": "f", + # "BF16" +} + +logger = logging.getLogger("PyXCP") +# logger.setLevel(logging.INFO) + + +# sys.argv.append(r"C:\Users\Chris\PycharmProjects\pyxcp\pyxcp\examples\run_daq.xmraw") + +parser = argparse.ArgumentParser(description="Use .xmraw files in an Apache Arrow application.") +parser.add_argument("xmraw_file", help=".xmraw file") +args = parser.parse_args() + + +@dataclass +class Storage: + name: str + arrow_type: Any + arr: array + + +@dataclass +class StorageContainer: + name: str + arr: list[Storage] = field(default_factory=[]) + ts0: array[float] = field(default_factory=lambda: array("d")) + ts1: array[float] = field(default_factory=lambda: array("d")) + + +class Unfolder(XcpLogFileUnfolder): + + def __init__(self, recording_file_name: str): + super().__init__(recording_file_name) + self.mdf_file_name = Path(recording_file_name).with_suffix(".mf4") + + def initialize(self): + self.tables = [] + for dl in self.daq_lists: + result = [] + for name, type_str in dl.headers: + array_txpe = MAP_TO_ARRAY[type_str] + arrow_type = MAP_TO_NP[type_str] + sd = Storage(name, arrow_type, array(array_txpe)) + result.append(sd) + sc = StorageContainer(dl.name, result) + self.tables.append(sc) + print("Extracting DAQ lists...") + + def finalize(self) -> Any: + print("Creating MDF result...") + timestamp_info = self.parameters.timestamp_info + hdr = HeaderBlock( + abs_time=timestamp_info.timestamp_ns, + tz_offset=timestamp_info.utc_offset, + daylight_save_time=timestamp_info.dst_offset, + time_flags=FLAG_HD_TIME_OFFSET_VALID, + ) + hdr.comment = f"""Timezone: {timestamp_info.timezone}""" # Test-Comment. + mdf4 = MDF(version="4.10") + mdf4.header = hdr + # result = [] + for idx, arr in enumerate(self.tables): + signals = [] + timestamps = arr.ts0 + for sd in arr.arr: + # adt = np.array(sd.arr, dtype=sd.arrow_type) + signal = Signal(samples=sd.arr, name=sd.name, timestamps=timestamps) + # print(signal) + signals.append(signal) + print(f"Appending data-group {arr.name!r}") + mdf4.append(signals, acq_name=arr.name, comment="Created by pyxcp recorder") + print(f"Writing '{self.mdf_file_name!s}'") + mdf4.save(self.mdf_file_name, compression=2, overwrite=True) + print("Done.") + return mdf4 + + def on_daq_list(self, daq_list_num: int, timestamp0: float, timestamp1: float, measurements: list): + sc = self.tables[daq_list_num] + sc.ts0.append(timestamp0) + sc.ts1.append(timestamp1) + for idx, elem in enumerate(measurements): + sto = sc.arr[idx] + sto.arr.append(elem) + + +logger.info(f"Processing {args.xmraw_file!r}") +logger.info(f"Processing {Path(args.xmraw_file)!r}") + +lfr = Unfolder(args.xmraw_file) +res = lfr.run() diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 5ff6f4a..875f9e1 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -128,7 +128,7 @@ PYBIND11_MODULE(rekorder, m) { ss << "ts_scale_factor=" << self.m_ts_scale_factor << ", "; ss << "ts_size=" << static_cast(self.m_ts_size) << ", "; ss << "min_daq=" << static_cast(self.m_min_daq) << ", "; - ss << "timestamp=" << self.get_timestamp_info().to_string() << ", "; + ss << "timestamp_info=" << self.get_timestamp_info().to_string() << ", "; ss << "daq_lists=[\n"; for (const auto& dl : self.m_daq_lists) { ss << dl.to_string() << ",\n"; diff --git a/pyxcp/utils.py b/pyxcp/utils.py index e6c5ce7..e7a5a87 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -85,9 +85,9 @@ class CurrentDatetime(TimestampInfo): def __init__(self, timestamp_ns: int): TimestampInfo.__init__(self, timestamp_ns) timezone = pytz.timezone(self.timezone) - self._dt = datetime.datetime.fromtimestamp(timestamp_ns / 1_000_000_000.0) - self.utc_offset = int(timezone.utcoffset(self._dt).total_seconds() / 60) - self.dst_offset = int(timezone.dst(self._dt).total_seconds() / 60) + dt = datetime.datetime.fromtimestamp(timestamp_ns / 1_000_000_000.0) + self.utc_offset = int(timezone.utcoffset(dt).total_seconds() / 60) + self.dst_offset = int(timezone.dst(dt).total_seconds() / 60) def __str__(self): return f"""CurrentDatetime( @@ -97,5 +97,3 @@ def __str__(self): utc_offset={self.utc_offset}, dst_offset={self.dst_offset} )""" - - __repr__ = __str__ From a88e8b59b8558aa3933cc0c52d8bd1b6b473e855 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sat, 13 Jul 2024 12:10:27 +0300 Subject: [PATCH 82/99] today() --- pyxcp/examples/ex_arrow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyxcp/examples/ex_arrow.py b/pyxcp/examples/ex_arrow.py index f03f840..6abc512 100644 --- a/pyxcp/examples/ex_arrow.py +++ b/pyxcp/examples/ex_arrow.py @@ -96,12 +96,12 @@ def finalize(self) -> Any: # print("finalize()") result = [] for arr in self.arrow_tables: - # timestamps = arr.ts0 + timestamps = arr.ts0 names = [] data = [] - # names = ["timestamp"] - # data = [timestamps] + names = ["timestamp"] + data = [timestamps] for sd in arr.arr: # print(sd.name, sd.arrow_type, len(sd.arr)) adt = pa.array(sd.arr, type=sd.arrow_type) From 9084f8d7e1a622a44d30634a76da20bccf8bc716 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 14 Jul 2024 04:28:58 +0300 Subject: [PATCH 83/99] today() --- pyxcp/examples/ex_arrow.py | 25 +++++++++++-------------- pyxcp/examples/ex_mdf.py | 9 +++++---- pyxcp/utils.py | 2 +- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/pyxcp/examples/ex_arrow.py b/pyxcp/examples/ex_arrow.py index 6abc512..8076174 100644 --- a/pyxcp/examples/ex_arrow.py +++ b/pyxcp/examples/ex_arrow.py @@ -7,7 +7,7 @@ from pathlib import Path # from pprint import pprint -from typing import Any +from typing import Any, List import pyarrow as pa @@ -68,9 +68,11 @@ class Storage: @dataclass class StorageContainer: name: str - arr: list[Storage] = field(default_factory=[]) - ts0: array[float] = field(default_factory=lambda: array("d")) - ts1: array[float] = field(default_factory=lambda: array("d")) + arr: List[Storage] = field(default_factory=[]) + #ts0: array[float] = field(default_factory=lambda: array("d")) + #ts1: array[float] = field(default_factory=lambda: array("d")) + ts0: List[float] = field(default_factory=lambda: array("Q")) + ts1: List[float] = field(default_factory=lambda: array("Q")) class Unfolder(XcpLogFileUnfolder): @@ -89,33 +91,28 @@ def initialize(self): print(f"\t{name!r} {array_txpe} {arrow_type}", sd) result.append(sd) sc = StorageContainer(dl.name, result) - # print(sc) self.arrow_tables.append(sc) def finalize(self) -> Any: # print("finalize()") result = [] for arr in self.arrow_tables: - timestamps = arr.ts0 - names = [] - data = [] + timestamp0 = arr.ts0 + timestamp1 = arr.ts1 + names = ["timestamp0", "timestamp1"] + data = [timestamp0, timestamp1 - names = ["timestamp"] - data = [timestamps] + ] for sd in arr.arr: - # print(sd.name, sd.arrow_type, len(sd.arr)) adt = pa.array(sd.arr, type=sd.arrow_type) names.append(sd.name) data.append(adt) table = pa.Table.from_arrays(data, names=names) - # table = table.append_column("timestamp", [timestamps]) fname = f"{arr.name}.parquet" print("Writing table", fname) pq.write_table(table, fname) print("done.", table.shape) result.append(table) - # xcv = ValueHolder(result) - # print(xcv) return result def on_daq_list(self, daq_list_num: int, timestamp0: float, timestamp1: float, measurements: list): diff --git a/pyxcp/examples/ex_mdf.py b/pyxcp/examples/ex_mdf.py index ece0057..3d1cb2b 100644 --- a/pyxcp/examples/ex_mdf.py +++ b/pyxcp/examples/ex_mdf.py @@ -5,7 +5,7 @@ from array import array from dataclasses import dataclass, field from pathlib import Path -from typing import Any +from typing import Any, List import numpy as np from asammdf import MDF, Signal @@ -67,9 +67,10 @@ class Storage: class StorageContainer: name: str arr: list[Storage] = field(default_factory=[]) - ts0: array[float] = field(default_factory=lambda: array("d")) - ts1: array[float] = field(default_factory=lambda: array("d")) - + #ts0: array[float] = field(default_factory=lambda: array("d")) + #ts1: array[float] = field(default_factory=lambda: array("d")) + ts0: List[float] = field(default_factory=lambda: array("Q")) + ts1: List[float] = field(default_factory=lambda: array("Q")) class Unfolder(XcpLogFileUnfolder): diff --git a/pyxcp/utils.py b/pyxcp/utils.py index e7a5a87..e1196ef 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -91,7 +91,7 @@ def __init__(self, timestamp_ns: int): def __str__(self): return f"""CurrentDatetime( - datetime="{self._dt!s}", + datetime="{datetime.datetime.fromtimestamp(self.timestamp_ns / 1_000_000_000.0)!s}", timezone="{self.timezone}", timestamp_ns={self.timestamp_ns}, utc_offset={self.utc_offset}, From 300fb358a71a2c70111860e6e814155d71bb985c Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sun, 14 Jul 2024 13:30:48 +0300 Subject: [PATCH 84/99] today() --- build_ext.py | 2 +- pyxcp/daq_stim/__init__.py | 2 +- pyxcp/examples/ex_arrow.py | 18 ++--- pyxcp/examples/ex_mdf.py | 11 +-- pyxcp/examples/ex_sqlite.py | 140 ++++++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 19 deletions(-) create mode 100644 pyxcp/examples/ex_sqlite.py diff --git a/build_ext.py b/build_ext.py index 838213a..34f00a9 100644 --- a/build_ext.py +++ b/build_ext.py @@ -78,4 +78,4 @@ def build_extension(debug: bool = False) -> None: if __name__ == "__main__": includes = subprocess.getoutput("pybind11-config --cmakedir") # nosec os.environ["pybind11_DIR"] = includes - build_extension(True) + build_extension(False) diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index a4ffef0..b13868f 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -212,7 +212,7 @@ def initialize(self): hdr = ",".join(["timestamp0", "timestamp1"] + [h[0] for h in daq_list.headers]) out_file.write(f"{hdr}\n") - def on_daq_list(self, daq_list: int, ts0: float, ts1: float, payload: list): + def on_daq_list(self, daq_list: int, ts0: int, ts1: int, payload: list): self.files[daq_list].write(f"{ts0},{ts1},{', '.join([str(x) for x in payload])}\n") def finalize(self): diff --git a/pyxcp/examples/ex_arrow.py b/pyxcp/examples/ex_arrow.py index 8076174..0b11f73 100644 --- a/pyxcp/examples/ex_arrow.py +++ b/pyxcp/examples/ex_arrow.py @@ -48,10 +48,6 @@ } logger = logging.getLogger("PyXCP") -# logger.setLevel(logging.INFO) - - -# sys.argv.append(r"C:\Users\Chris\PycharmProjects\pyxcp\pyxcp\examples\run_daq.xmraw") parser = argparse.ArgumentParser(description="Use .xmraw files in an Apache Arrow application.") parser.add_argument("xmraw_file", help=".xmraw file") @@ -69,10 +65,10 @@ class Storage: class StorageContainer: name: str arr: List[Storage] = field(default_factory=[]) - #ts0: array[float] = field(default_factory=lambda: array("d")) - #ts1: array[float] = field(default_factory=lambda: array("d")) - ts0: List[float] = field(default_factory=lambda: array("Q")) - ts1: List[float] = field(default_factory=lambda: array("Q")) + # ts0: array[int] = field(default_factory=lambda: array("d")) + # ts1: array[int] = field(default_factory=lambda: array("d")) + ts0: List[int] = field(default_factory=lambda: array("Q")) + ts1: List[int] = field(default_factory=lambda: array("Q")) class Unfolder(XcpLogFileUnfolder): @@ -100,9 +96,7 @@ def finalize(self) -> Any: timestamp0 = arr.ts0 timestamp1 = arr.ts1 names = ["timestamp0", "timestamp1"] - data = [timestamp0, timestamp1 - - ] + data = [timestamp0, timestamp1] for sd in arr.arr: adt = pa.array(sd.arr, type=sd.arrow_type) names.append(sd.name) @@ -115,7 +109,7 @@ def finalize(self) -> Any: result.append(table) return result - def on_daq_list(self, daq_list_num: int, timestamp0: float, timestamp1: float, measurements: list): + def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list): sc = self.arrow_tables[daq_list_num] sc.ts0.append(timestamp0) sc.ts1.append(timestamp1) diff --git a/pyxcp/examples/ex_mdf.py b/pyxcp/examples/ex_mdf.py index 3d1cb2b..548ab10 100644 --- a/pyxcp/examples/ex_mdf.py +++ b/pyxcp/examples/ex_mdf.py @@ -67,10 +67,11 @@ class Storage: class StorageContainer: name: str arr: list[Storage] = field(default_factory=[]) - #ts0: array[float] = field(default_factory=lambda: array("d")) - #ts1: array[float] = field(default_factory=lambda: array("d")) - ts0: List[float] = field(default_factory=lambda: array("Q")) - ts1: List[float] = field(default_factory=lambda: array("Q")) + # ts0: array[int] = field(default_factory=lambda: array("d")) + # ts1: array[int] = field(default_factory=lambda: array("d")) + ts0: List[int] = field(default_factory=lambda: array("Q")) + ts1: List[int] = field(default_factory=lambda: array("Q")) + class Unfolder(XcpLogFileUnfolder): @@ -119,7 +120,7 @@ def finalize(self) -> Any: print("Done.") return mdf4 - def on_daq_list(self, daq_list_num: int, timestamp0: float, timestamp1: float, measurements: list): + def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list): sc = self.tables[daq_list_num] sc.ts0.append(timestamp0) sc.ts1.append(timestamp1) diff --git a/pyxcp/examples/ex_sqlite.py b/pyxcp/examples/ex_sqlite.py new file mode 100644 index 0000000..d608fa4 --- /dev/null +++ b/pyxcp/examples/ex_sqlite.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- + +import argparse +import logging +import os +import sqlite3 +from array import array +from dataclasses import dataclass, field +from mmap import PAGESIZE +from pathlib import Path +from typing import Any, List + +from pyxcp.recorder import XcpLogFileUnfolder + + +# sys.argv.append(r"D:\pyxcp\run_daq.xmraw") + +MAP_TO_SQL = { + "U8": "INTEGER", + "I8": "INTEGER", + "U16": "INTEGER", + "I16": "INTEGER", + "U32": "INTEGER", + "I32": "INTEGER", + "U64": "INTEGER", + "I64": "INTEGER", + "F32": "FLOAT", + "F64": "FLOAT", + "F16": "FLOAT", + "BF16": "FLOAT", +} + +MAP_TO_ARRAY = { + "U8": "B", + "I8": "b", + "U16": "H", + "I16": "h", + "U32": "L", + "I32": "l", + "U64": "Q", + "I64": "q", + "F32": "f", + "F64": "d", + "F16": "f", + # "BF16" +} + +logger = logging.getLogger("PyXCP") + +parser = argparse.ArgumentParser(description="Use .xmraw files in an Apache Arrow application.") +parser.add_argument("xmraw_file", help=".xmraw file") +args = parser.parse_args() + + +@dataclass +class Storage: + name: str + arrow_type: Any + arr: array + + +@dataclass +class StorageContainer: + name: str + arr: List[Storage] = field(default_factory=[]) + # ts0: array[int] = field(default_factory=lambda: array("d")) + # ts1: array[int] = field(default_factory=lambda: array("d")) + ts0: List[int] = field(default_factory=lambda: array("Q")) + ts1: List[int] = field(default_factory=lambda: array("Q")) + + +class Unfolder(XcpLogFileUnfolder): + + def __init__(self, recording_file_name: str): + super().__init__(recording_file_name) + self.sq3_file_name = Path(recording_file_name).with_suffix(".sq3") + try: + os.unlink(self.sq3_file_name) + except Exception as e: + print(e) + + def initialize(self): + # print("initialize()") + self.create_database(self.sq3_file_name) + self.arrow_tables = [] + self.insert_stmt = {} + for dl in self.daq_lists: + # print("=" * 80) + # print(dl.name) + result = [] + for name, type_str in dl.headers: + array_txpe = MAP_TO_ARRAY[type_str] + sql_type = MAP_TO_SQL[type_str] + sd = Storage(name, sql_type, array(array_txpe)) + print(f"\t{name!r} {array_txpe} {sql_type}", sd) + result.append(sd) + sc = StorageContainer(dl.name, result) + self.create_table(sc) + self.insert_stmt[sc.name] = ( + f"INSERT INTO {sc.name}({', '.join(['ts0', 'ts1'] + [r.name for r in sc.arr])}) VALUES({', '.join(["?" for _ in range(len(sc.arr) + 2)])})" + ) + self.arrow_tables.append(sc) + + def create_database(self, db_name: str) -> None: + self.conn = sqlite3.Connection(db_name) + self.cursor = self.conn.cursor() + self.cursor.execute("PRAGMA FOREIGN_KEYS=ON") + self.cursor.execute(f"PRAGMA PAGE_SIZE={PAGESIZE}") + self.cursor.execute("PRAGMA SYNCHRONOUS=OFF") + self.cursor.execute("PRAGMA LOCKING_MODE=EXCLUSIVE") + self.cursor.execute("PRAGMA TEMP_STORE=MEMORY") + + def create_table(self, sc: StorageContainer) -> None: + columns = ["ts0 INTEGER", "ts1 INTEGER"] + for elem in sc.arr: + columns.append(f"{elem.name} {elem.arrow_type}") + ddl = f"CREATE TABLE {sc.name}({', '.join(columns)})" + print(ddl) + try: + self.cursor.execute(ddl) + except Exception as e: + print(e) + + def finalize(self) -> Any: + self.conn.commit() + self.conn.close() + + def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list): + sc = self.arrow_tables[daq_list_num] + insert_stmt = self.insert_stmt[sc.name] + data = [timestamp0, timestamp1, *measurements] + self.cursor.execute(insert_stmt, data) + + +logger.info(f"Processing {args.xmraw_file!r}") +logger.info(f"Processing {Path(args.xmraw_file)!r}") + +lfr = Unfolder(args.xmraw_file) +res = lfr.run() +print(res) From 373b1b78232394a48887730f49b8ae5a184fa47b Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 22 Jul 2024 08:48:45 +0300 Subject: [PATCH 85/99] today() --- pyxcp/config/__init__.py | 9 +- pyxcp/cpp_ext/extension_wrapper.cpp | 3 +- pyxcp/cpp_ext/helper.hpp | 13 ++- pyxcp/cpp_ext/mcobject.hpp | 34 ++++++ pyxcp/examples/ex_arrow.py | 8 +- pyxcp/examples/ex_mdf.py | 6 +- pyxcp/examples/ex_sqlite.py | 58 +++++----- pyxcp/examples/run_daq.py | 22 ++-- pyxcp/examples/xcp_recording.py | 7 +- pyxcp/recorder/__init__.py | 5 + pyxcp/recorder/decoders.hpp | 169 ++++++++++++++++++++++++++++ pyxcp/recorder/rekorder.hpp | 1 + pyxcp/recorder/unfolder.hpp | 16 +-- pyxcp/recorder/wrap.cpp | 3 + pyxcp/recorder/writer.hpp | 35 ++++-- pyxcp/transport/can.py | 10 +- pyxcp/transport/eth.py | 6 +- pyxcp/transport/sxi.py | 6 +- pyxcp/transport/usb_transport.py | 2 +- 19 files changed, 325 insertions(+), 88 deletions(-) create mode 100644 pyxcp/recorder/decoders.hpp diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 2ab56c8..97dbb3f 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import io import json +import logging import sys import typing from pathlib import Path @@ -22,7 +23,7 @@ Unicode, Union, ) -from traitlets.config import Application, Instance, SingletonConfigurable +from traitlets.config import Application, Instance, SingletonConfigurable, default from pyxcp.config import legacy @@ -821,7 +822,7 @@ def __init__(self, **kws): class General(SingletonConfigurable): """ """ - loglevel = Unicode("INFO", help="Set the log level by value or name.").tag(config=True) + # loglevel = Unicode("INFO", help="Set the log level by value or name.").tag(config=True) disable_error_handling = Bool(False, help="Disable XCP error-handler for performance reasons.").tag(config=True) disconnect_response_optional = Bool(False, help="Ignore missing response on DISCONNECT request.").tag(config=True) seed_n_key_dll = Unicode("", allow_none=False, help="Dynamic library used for slave resource unlocking.").tag(config=True) @@ -988,6 +989,10 @@ def read_configuration_file(self, file_name: str, emit_warning: bool = True): ) ) + @default("log_level") + def _default_value(self): + return logging.INFO # traitlets default is logging.WARN + aliases = Dict( # type:ignore[assignment] dict( c="PyXCP.config_file", # Application diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index 3e96cae..6b8a599 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -34,6 +34,7 @@ PYBIND11_MODULE(cpp_ext, m) { .def_property("ext", &McObject::get_ext, &McObject::set_ext) .def_property("length", &McObject::get_length, &McObject::set_length) .def_property("data_type", &McObject::get_data_type, &McObject::set_data_type) + .def_property_readonly("components", &McObject::get_components) .def("add_component", &McObject::add_component, "component"_a) .def("__repr__", [](const McObject& self) { return to_string(self); }); @@ -81,7 +82,7 @@ PYBIND11_MODULE(cpp_ext, m) { py::class_(m, "TimestampInfo", py::dynamic_attr()) .def(py::init()) - .def(py::init()) + .def(py::init()) .def_property_readonly("timestamp_ns", &TimestampInfo::get_timestamp_ns) .def_property("utc_offset", &TimestampInfo::get_utc_offset, &TimestampInfo::set_utc_offset) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 84b5b8a..9462a43 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -9,6 +9,7 @@ #include #endif + #include #include #include #include @@ -33,6 +34,10 @@ #define HAS_BFLOAT16 (0) #endif +constexpr std::endian target_byteorder() { + return std::endian::native; +} + template constexpr void DBG_PRINTN(Args &&...args) noexcept { ((std::cout << std::forward(args) << " "), ...); @@ -117,10 +122,10 @@ enum class TimestampType : std::uint8_t { class TimestampInfo { public: - TimestampInfo(const TimestampInfo&) = default; - TimestampInfo(TimestampInfo&&) = default; - TimestampInfo& operator=(const TimestampInfo&) = default; - TimestampInfo& operator=(TimestampInfo&&) = default; + TimestampInfo(const TimestampInfo &) = default; + TimestampInfo(TimestampInfo &&) = default; + TimestampInfo &operator=(const TimestampInfo &) = default; + TimestampInfo &operator=(TimestampInfo &&) = default; TimestampInfo() : m_timestamp_ns(0), m_timezone{}, m_utc_offset(0), m_dst_offset(0) { } diff --git a/pyxcp/cpp_ext/mcobject.hpp b/pyxcp/cpp_ext/mcobject.hpp index ef95c4e..3ce557b 100644 --- a/pyxcp/cpp_ext/mcobject.hpp +++ b/pyxcp/cpp_ext/mcobject.hpp @@ -30,6 +30,40 @@ const std::map> TYPE #endif }; +enum class TypeCode : std::uint8_t { + U8, + I8, + U16, + I16, + U32, + I32, + U64, + I64, + F32, + F64, + F16, + BF16, +}; + +const std::map TYPE_TO_TYPE_CODE_MAP = { + { "U8", TypeCode::U8 }, + { "I8", TypeCode::I8 }, + { "U16", TypeCode::U16 }, + { "I16", TypeCode::I16 }, + { "U32", TypeCode::U32 }, + { "I32", TypeCode::I32 }, + { "U64", TypeCode::U64 }, + { "I64", TypeCode::I64 }, + { "F32", TypeCode::F32 }, + { "F64", TypeCode::F64 }, + #if HAS_FLOAT16 + { "F16", TypeCode::F16 }, + #endif + #if HAS_BFLOAT16 + { "BF16", TypeCode::BF16 }, + #endif +}; + const std::map TYPE_MAP_REV = { { 0, "U8" }, { 1, "I8" }, diff --git a/pyxcp/examples/ex_arrow.py b/pyxcp/examples/ex_arrow.py index 0b11f73..71a0876 100644 --- a/pyxcp/examples/ex_arrow.py +++ b/pyxcp/examples/ex_arrow.py @@ -29,7 +29,7 @@ "F32": pa.float32(), "F64": pa.float64(), "F16": pa.float16(), - # "BF16" + "BF16": pa.float16(), } MAP_TO_ARRAY = { @@ -44,7 +44,7 @@ "F32": "f", "F64": "d", "F16": "f", - # "BF16" + "BF16": "f", } logger = logging.getLogger("PyXCP") @@ -73,7 +73,7 @@ class StorageContainer: class Unfolder(XcpLogFileUnfolder): - def initialize(self): + def initialize(self) -> None: # print("initialize()") self.arrow_tables = [] for dl in self.daq_lists: @@ -109,7 +109,7 @@ def finalize(self) -> Any: result.append(table) return result - def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list): + def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list) -> None: sc = self.arrow_tables[daq_list_num] sc.ts0.append(timestamp0) sc.ts1.append(timestamp1) diff --git a/pyxcp/examples/ex_mdf.py b/pyxcp/examples/ex_mdf.py index 548ab10..8f874fd 100644 --- a/pyxcp/examples/ex_mdf.py +++ b/pyxcp/examples/ex_mdf.py @@ -67,8 +67,6 @@ class Storage: class StorageContainer: name: str arr: list[Storage] = field(default_factory=[]) - # ts0: array[int] = field(default_factory=lambda: array("d")) - # ts1: array[int] = field(default_factory=lambda: array("d")) ts0: List[int] = field(default_factory=lambda: array("Q")) ts1: List[int] = field(default_factory=lambda: array("Q")) @@ -79,7 +77,7 @@ def __init__(self, recording_file_name: str): super().__init__(recording_file_name) self.mdf_file_name = Path(recording_file_name).with_suffix(".mf4") - def initialize(self): + def initialize(self) -> None: self.tables = [] for dl in self.daq_lists: result = [] @@ -120,7 +118,7 @@ def finalize(self) -> Any: print("Done.") return mdf4 - def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list): + def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list) -> None: sc = self.tables[daq_list_num] sc.ts0.append(timestamp0) sc.ts1.append(timestamp1) diff --git a/pyxcp/examples/ex_sqlite.py b/pyxcp/examples/ex_sqlite.py index d608fa4..e422cc5 100644 --- a/pyxcp/examples/ex_sqlite.py +++ b/pyxcp/examples/ex_sqlite.py @@ -11,10 +11,9 @@ from typing import Any, List from pyxcp.recorder import XcpLogFileUnfolder +from pyxcp.recorder.converter import MAP_TO_ARRAY -# sys.argv.append(r"D:\pyxcp\run_daq.xmraw") - MAP_TO_SQL = { "U8": "INTEGER", "I8": "INTEGER", @@ -30,21 +29,6 @@ "BF16": "FLOAT", } -MAP_TO_ARRAY = { - "U8": "B", - "I8": "b", - "U16": "H", - "I16": "h", - "U32": "L", - "I32": "l", - "U64": "Q", - "I64": "q", - "F32": "f", - "F64": "d", - "F16": "f", - # "BF16" -} - logger = logging.getLogger("PyXCP") parser = argparse.ArgumentParser(description="Use .xmraw files in an Apache Arrow application.") @@ -63,8 +47,6 @@ class Storage: class StorageContainer: name: str arr: List[Storage] = field(default_factory=[]) - # ts0: array[int] = field(default_factory=lambda: array("d")) - # ts1: array[int] = field(default_factory=lambda: array("d")) ts0: List[int] = field(default_factory=lambda: array("Q")) ts1: List[int] = field(default_factory=lambda: array("Q")) @@ -79,7 +61,7 @@ def __init__(self, recording_file_name: str): except Exception as e: print(e) - def initialize(self): + def initialize(self) -> None: # print("initialize()") self.create_database(self.sq3_file_name) self.arrow_tables = [] @@ -97,27 +79,40 @@ def initialize(self): sc = StorageContainer(dl.name, result) self.create_table(sc) self.insert_stmt[sc.name] = ( - f"INSERT INTO {sc.name}({', '.join(['ts0', 'ts1'] + [r.name for r in sc.arr])}) VALUES({', '.join(["?" for _ in range(len(sc.arr) + 2)])})" + f"""INSERT INTO {sc.name}({', '.join(['ts0', 'ts1'] + [r.name for r in sc.arr])}) VALUES({', '.join(["?" for _ in range(len(sc.arr) + 2)])})""" ) self.arrow_tables.append(sc) def create_database(self, db_name: str) -> None: self.conn = sqlite3.Connection(db_name) self.cursor = self.conn.cursor() - self.cursor.execute("PRAGMA FOREIGN_KEYS=ON") - self.cursor.execute(f"PRAGMA PAGE_SIZE={PAGESIZE}") - self.cursor.execute("PRAGMA SYNCHRONOUS=OFF") - self.cursor.execute("PRAGMA LOCKING_MODE=EXCLUSIVE") - self.cursor.execute("PRAGMA TEMP_STORE=MEMORY") + self.execute("PRAGMA FOREIGN_KEYS=ON") + self.execute(f"PRAGMA PAGE_SIZE={PAGESIZE}") + self.execute("PRAGMA SYNCHRONOUS=OFF") + self.execute("PRAGMA LOCKING_MODE=EXCLUSIVE") + self.execute("PRAGMA TEMP_STORE=MEMORY") + + timestamp_info = self.parameters.timestamp_info + self.execute( + "CREATE TABLE timestamp_info(timestamp_ns INTEGER, utc_offset INTEGER, dst_offset INTEGER, timezone VARCHAR(255))" + ) + self.execute("CREATE TABLE table_names(name VARCHAR(255))") + self.execute( + "INSERT INTO timestamp_info VALUES(?, ?, ?, ?)", + [timestamp_info.timestamp_ns, timestamp_info.utc_offset, timestamp_info.dst_offset, timestamp_info.timezone], + ) def create_table(self, sc: StorageContainer) -> None: columns = ["ts0 INTEGER", "ts1 INTEGER"] for elem in sc.arr: columns.append(f"{elem.name} {elem.arrow_type}") ddl = f"CREATE TABLE {sc.name}({', '.join(columns)})" - print(ddl) + self.execute(ddl) + self.execute("INSERT INTO table_names VALUES(?)", [sc.name]) + + def execute(self, stmt: str) -> None: try: - self.cursor.execute(ddl) + self.cursor.execute(stmt) except Exception as e: print(e) @@ -125,16 +120,15 @@ def finalize(self) -> Any: self.conn.commit() self.conn.close() - def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list): + def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list) -> None: sc = self.arrow_tables[daq_list_num] insert_stmt = self.insert_stmt[sc.name] data = [timestamp0, timestamp1, *measurements] - self.cursor.execute(insert_stmt, data) + self.execute(insert_stmt, data) logger.info(f"Processing {args.xmraw_file!r}") logger.info(f"Processing {Path(args.xmraw_file)!r}") lfr = Unfolder(args.xmraw_file) -res = lfr.run() -print(res) +lfr.run() diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 9680421..e195197 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -10,8 +10,8 @@ ap = ArgumentParser(description="DAQ test") -XCP_LITE = True -# XCP_LITE = False +# XCP_LITE = True +XCP_LITE = False # Completly random configurations, only for illustrative purposes. # @@ -26,10 +26,10 @@ False, False, [ - ("byteCounter", 0x114A0, 0, "U8"), # - ("wordCounter", 0x114A2, 0, "U16"), - ("dwordCounter", 0x114A4, 0, "U32"), - ("sbyteCounter", 0x114A8, 0, "I8"), + ("byteCounter", 0x203EA, 0, "U8"), + ("wordCounter", 0x203EC, 0, "U16"), + ("dwordCounter", 0x20410, 0, "U32"), + ("sbyteCounter", 0x203EB, 0, "I8"), ], ), DaqList( @@ -38,11 +38,11 @@ False, False, [ - ("swordCounter", 0x114AA, 0, "I16"), - ("sdwordCounter", 0x114AC, 0, "I32"), - ("channel1", 0x10088, 0, "F64"), - ("channel2", 0x10090, 0, "F64"), - ("channel3", 0x10098, 0, "F64"), + ("swordCounter", 0x20414, 0, "I16"), + ("sdwordCounter", 0x20418, 0, "I32"), + ("channel1", 0x203F8, 0, "F64"), + ("channel2", 0x20400, 0, "F64"), + ("channel3", 0x20408, 0, "F64"), ], ), ] diff --git a/pyxcp/examples/xcp_recording.py b/pyxcp/examples/xcp_recording.py index 2dea0f5..6f10025 100644 --- a/pyxcp/examples/xcp_recording.py +++ b/pyxcp/examples/xcp_recording.py @@ -2,7 +2,7 @@ import sys from pprint import pprint -from pyxcp.recorder import XcpLogFileReader, XcpLogFileUnfolder +from pyxcp.recorder import NumpyDecoder, XcpLogFileReader, XcpLogFileUnfolder from pyxcp.utils import hexDump @@ -38,6 +38,11 @@ def on_daq_list(self, daq_list_num, timestamp0, timestamp1, measurement): print(lfr.daq_lists) print(lfr.parameters.timestamp_info) print("Wrap-arounds:", lfr.wraps) + +nnp = NumpyDecoder(args.xmraw_file) +nnp.run() + + sys.exit() reader = XcpLogFileReader(args.xmraw_file) diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 44bd5cc..39a2d68 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -19,6 +19,7 @@ DaqRecorderPolicy, Deserializer, MeasurementParameters, + NumpyDecoder, ValueHolder, XcpLogFileUnfolder, _PyXcpLogFileReader, @@ -52,6 +53,10 @@ class XcpLogFileReader: def __init__(self, file_name): self._reader = _PyXcpLogFileReader(file_name) + @property + def header(self): + return self._reader.get_header() + def get_header(self): return XcpLogFileHeader(*self._reader.get_header_as_tuple()) diff --git a/pyxcp/recorder/decoders.hpp b/pyxcp/recorder/decoders.hpp new file mode 100644 index 0000000..21eef15 --- /dev/null +++ b/pyxcp/recorder/decoders.hpp @@ -0,0 +1,169 @@ + +#if !defined(__DECODERS_HPP) + #define __DECODERS_HPP + + #include "rekorder.hpp" + +// template +class SequentialFileWriter { + public: + + SequentialFileWriter(const std::string& file_name, const std::string& type_str) : + m_file_name(file_name), m_type_code(TYPE_TO_TYPE_CODE_MAP.at(type_str)), m_opened(false) { + #if defined(_WIN32) + m_fd = CreateFileA( + m_file_name.c_str(), GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr + ); + #else + m_fd = open(m_file_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); + #endif + + m_opened = true; + } + + ~SequentialFileWriter() { + std::cout << "Closing " << m_file_name << std::endl; + + if (m_opened) { + #if defined(_WIN32) || defined(_WIN64) + if (!CloseHandle(m_fd)) { + std::cout << error_string("CloseHandle", get_last_error()); + } + #else + if (close(m_fd) == -1) { + std::cout << error_string("close", get_last_error()); + } + #endif + + m_opened = false; + } + } + + void write(const measurement_value_t& variant_value) { + std::string str_value; + auto type_code = m_type_code; + + std::visit( + [&str_value, type_code](auto&& value) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { + if (type_code == TypeCode::U8) { + str_value = to_binary(value); + } else if (type_code == TypeCode::U16) { + str_value = to_binary(value); + } else if (type_code == TypeCode::U32) { + str_value = to_binary(value); + } else if (type_code == TypeCode::U64) { + str_value = to_binary(value); + } + } else if constexpr (std::is_same_v) { + if (type_code == TypeCode::I8) { + str_value = to_binary(value); + } else if (type_code == TypeCode::I16) { + str_value = to_binary(value); + } else if (type_code == TypeCode::I32) { + str_value = to_binary(value); + } else if (type_code == TypeCode::I64) { + str_value = to_binary(value); + } + } else if constexpr (std::is_same_v) { + if (type_code == TypeCode::F32) { + str_value = to_binary(value); + } else if (type_code == TypeCode::F64) { + str_value = to_binary(value); + } + #if HAS_FLOAT16 + else if (type_code == TypeCode::F16) { + str_value = to_binary(value); + } + #endif + #if HAS_BFLOAT16 + else if (type_code == TypeCode::BF16) { + str_value = to_binary(value); + } + #endif + } + }, + variant_value + ); + + #if defined(_WIN32) || defined(_WIN64) + DWORD dwBytesWritten; + + WriteFile(m_fd, str_value.c_str(), std::size(str_value), &dwBytesWritten, nullptr); + #else + write(m_fd, str_value.c_str(), str_value.size()); + #endif + } + + private: + + std::string m_file_name; + TypeCode m_type_code; + bool m_opened = false; + #if defined(_WIN32) || defined(_WIN64) + HANDLE m_fd; + #else + int m_fd; + #endif +}; + +class NumpyDecoder : public XcpLogFileUnfolder { + public: + + using writer_t = std::unique_ptr; + + explicit NumpyDecoder(const std::string& file_name) : XcpLogFileUnfolder(file_name), m_path(file_name) { + std::cout << "\n creating directory: " << m_path.stem() << std::endl; + // std::filesystem::create_directory(m_path.stem()); + } + + void initialize() override { + std::cout << "initialize()" << std::endl; + for (const auto& dl : get_daq_lists()) { + std::vector dl_writers; + // std::cout << "DAQ List: " << m_path.stem() / dl.get_name() << std::endl; + create_dir(dl.get_name()); + for (const auto& [name, tp] : dl.get_headers()) { + // std::cout << "\tName: " << name << " TP: " << tp << std::endl; + const auto file_name = (m_path.stem() / dl.get_name() / name).replace_extension(".dat").string(); + dl_writers.emplace_back(std::make_unique(file_name, tp)); + } + m_writers.emplace_back(std::move(dl_writers)); + } + std::cout << "initialized -- GO!!!" << std::endl; + } + + void finalize() override { + std::cout << "finalize()" << std::endl; + } + + void on_daq_list( + std::uint16_t daq_list_num, std::uint64_t timestamp0, std::uint64_t timestamp1, + const std::vector& measurement + ) override { + auto& dl_writer = m_writers[daq_list_num]; + std::uint16_t count = 0; + + for (const auto& value : measurement) { + dl_writer[count]->write(value); + ++count; + } + } + + protected: + + void create_dir(const std::string& sub_dir) const { + std::filesystem::create_directory(m_path.stem()); + std::filesystem::create_directory(m_path.stem() / sub_dir); + } + + private: + + std::filesystem::path m_path; + std::vector> m_writers{}; +}; + +#endif // __DECODERS_HPP diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index e0b74de..d42e3ee 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -19,6 +19,7 @@ #include #include #include + #include #include #include #include diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index bd1345d..f63947a 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "daqlist.hpp" @@ -687,13 +688,13 @@ class Deserializer { //// timestamp_ns = from_binary(); - //std::cout << "TS: " << timestamp_ns << std::endl; - timezone = from_binary_str(); - //std::cout << "TZ: " << timezone << std::endl; - utc_offset = from_binary(); - //std::cout << "UTC:" << utc_offset << std::endl; - dst_offset = from_binary(); - //std::cout << "DST:" << dst_offset << std::endl; + // std::cout << "TS: " << timestamp_ns << std::endl; + timezone = from_binary_str(); + // std::cout << "TZ: " << timezone << std::endl; + utc_offset = from_binary(); + // std::cout << "UTC:" << utc_offset << std::endl; + dst_offset = from_binary(); + // std::cout << "DST:" << dst_offset << std::endl; TimestampInfo timestamp_info{ timestamp_ns, timezone, utc_offset, dst_offset }; @@ -1087,7 +1088,6 @@ class DaqRecorderPolicy : public DAQPolicyBase { } void finalize() override { - std::cout << "DaqRecorderPolicy::finalize()\n"; m_writer->finalize(); } diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index 875f9e1..d1dca36 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -6,6 +6,7 @@ #include +#include "decoders.hpp" #include "rekorder.hpp" namespace py = pybind11; @@ -182,6 +183,8 @@ PYBIND11_MODULE(rekorder, m) { .def("initialize", &XcpLogFileUnfolder::initialize) .def("finalize", &XcpLogFileUnfolder::finalize); + py::class_(m, "NumpyDecoder").def(py::init()).def("run", &NumpyDecoder::run); + py::class_(m, "ValueHolder") //.def(py::init()) .def(py::init()) diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 37b7426..208e04d 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -15,14 +15,25 @@ class XcpLogFileWriter { } else { m_file_name = file_name; } + m_opened = false; #if defined(_WIN32) m_fd = CreateFileA( m_file_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr ); + if (m_fd == INVALID_HANDLE_VALUE) { + throw std::runtime_error(error_string("XcpLogFileWriter::CreateFileA", get_last_error())); + } else { + m_opened = true; + } #else m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666); + if (m_fd == -1) { + throw std::runtime_error(error_string("XcpLogFileWriter::open", get_last_error())); + } else { + m_opened = true; + } #endif m_hard_limit = megabytes(prealloc); resize(m_hard_limit); @@ -53,6 +64,11 @@ class XcpLogFileWriter { if (!m_finalized) { m_finalized = true; stop_thread(); + + if (!m_opened) { + return; + } + if (m_container_record_count) { compress_frames(); } @@ -65,17 +81,17 @@ class XcpLogFileWriter { m_mmap->unmap(); ec = mio::detail::last_error(); if (ec.value() != 0) { - std::cout << error_string("mio::unmap", ec); + throw std::runtime_error(error_string("XcpLogFileWriter::mio::unmap", ec)); } resize(m_offset); #if defined(_WIN32) if (!CloseHandle(m_fd)) { - std::cout << error_string("CloseHandle", get_last_error()); + throw std::runtime_error(error_string("XcpLogFileWriter::CloseHandle", get_last_error())); } #else if (close(m_fd) == -1) { - std::cout << error_string("close", get_last_error()); + throw std::runtime_error(error_string("XcpLogFileWriter::close", get_last_error())); } #endif delete m_mmap; @@ -99,7 +115,7 @@ class XcpLogFileWriter { m_mmap->unmap(); ec = mio::detail::last_error(); if (ec.value() != 0) { - std::cout << error_string("mio::unmap", ec); + throw std::runtime_error(error_string("XcpLogFileWriter::mio::unmap", ec)); } } @@ -111,21 +127,21 @@ class XcpLogFileWriter { auto err = get_last_error(); if (err.value() != NO_ERROR) { - std::cout << error_string("SetFilePointer", err); + throw std::runtime_error(error_string("XcpLogFileWriter::SetFilePointer", err)); } } if (SetEndOfFile(m_fd) == 0) { - std::cout << error_string("SetEndOfFile", get_last_error()); + throw std::runtime_error(error_string("XcpLogFileWriter::SetEndOfFile", get_last_error())); } #else if (ftruncate(m_fd, size) == -1) { - std::cout << error_string("ftruncate", get_last_error()); + throw std::runtime_error(error_string("XcpLogFileWriter::ftruncate", get_last_error())); } #endif if (remap) { m_mmap->map(m_fd, 0, size, ec); if (ec.value() != 0) { - std::cout << error_string("mio::map", ec); + throw std::runtime_error(error_string("XcpLogFileWriter::mio::map", ec)); } } } @@ -153,7 +169,7 @@ class XcpLogFileWriter { ); if (cp_size < 0) { - throw std::runtime_error("LZ4 compression failed."); + throw std::runtime_error("XcpLogFileWriter - LZ4 compression failed."); } if (m_offset > (m_hard_limit >> 1)) { @@ -258,6 +274,7 @@ class XcpLogFileWriter { std::uint64_t m_offset{ 0 }; std::uint32_t m_chunk_size{ 0 }; std::string m_metadata; + bool m_opened{ false }; std::uint64_t m_num_containers{ 0 }; std::uint64_t m_record_count{ 0UL }; std::uint32_t m_container_record_count{ 0UL }; diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index 0aaee8e..3322058 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -247,8 +247,8 @@ def connect(self): for daq_id in self.parent.daq_identifier: can_filters.append(daq_id.create_filter_from_id()) self.can_interface.set_filters(can_filters) - self.parent.logger.debug(f"Interface: {self.interface_name!r} -- {self.can_interface!s}") - self.parent.logger.debug(f"Filters used: {self.can_interface.filters}") + self.parent.logger.debug(f"XCPonCAN - Interface: {self.interface_name!r} -- {self.can_interface!s}") + self.parent.logger.debug(f"XCPonCAN - Filters used: {self.can_interface.filters}") self.connected = True def close(self): @@ -322,9 +322,9 @@ def __init__(self, config, policy=None): self.interface_name = self.config.interface self.interface_configuration = detect_available_configs(interfaces=[self.interface_name]) parameters = self.get_interface_parameters() - self.logger.info(f"Opening {self.interface_name!r} CAN-interface {list(parameters.items())}") + self.logger.info(f"XCPonCAN - opening {self.interface_name!r} CAN-interface {list(parameters.items())}") self.logger.info( - f"Master-ID (Tx): 0x{self.can_id_master.id:08X}{self.can_id_master.type_str} -- " + f"XCPonCAN - Master-ID (Tx): 0x{self.can_id_master.id:08X}{self.can_id_master.type_str} -- " f"Slave-ID (Rx): 0x{self.can_id_slave.id:08X}{self.can_id_slave.type_str}" ) self.can_interface = PythonCanWrapper(self, self.interface_name, **parameters) @@ -344,7 +344,7 @@ def get_interface_parameters(self) -> Dict[str, Any]: if value is not None: result[n] = value elif value is not None: - self.logger.warning(f"{self.interface_name!r} has no support for parameter {n!r}.") + self.logger.warning(f"XCPonCAN - {self.interface_name!r} has no support for parameter {n!r}.") # Parameter names that need to be mapped. for base_name, name in can_interface_config_class.CAN_PARAM_MAP.items(): diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 9d3d64e..f20e8ea 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -50,7 +50,7 @@ def __init__(self, config=None, policy=None): bind_to_port = self.config.bind_to_port self._local_address = (address_to_bind, bind_to_port) if address_to_bind else None if self.ipv6 and not socket.has_ipv6: - msg = "IPv6 not supported by your platform." + msg = "XCPonEth - IPv6 not supported by your platform." self.logger.critical(msg) raise RuntimeError(msg) else: @@ -69,7 +69,7 @@ def __init__(self, config=None, policy=None): self.sockaddr, ) = addrinfo[0] except BaseException as ex: # noqa: B036 - msg = f"Failed to resolve address {self.host}:{self.port}" + msg = f"XCPonEth - Failed to resolve address {self.host}:{self.port}" self.logger.critical(msg) raise Exception(msg) from ex self.status = 0 @@ -87,7 +87,7 @@ def __init__(self, config=None, policy=None): try: self.sock.bind(self._local_address) except BaseException as ex: # noqa: B036 - msg = f"Failed to bind socket to given address {self._local_address}" + msg = f"XCPonEth - Failed to bind socket to given address {self._local_address}" self.logger.critical(msg) raise Exception(msg) from ex self._packet_listener = threading.Thread( diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index 1fef3cc..f7da41c 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -38,7 +38,7 @@ def __init__(self, config=None, policy=None): self.esc_sync = self.config.esc_sync self.esc_esc = self.config.esc_esc self.make_header() - self.logger.debug(f"Trying to open serial comm_port {self.port_name}.") + self.logger.info(f"XCPonSxI - trying to open serial comm_port {self.port_name}.") try: self.comm_port = serial.Serial( port=self.port_name, @@ -50,7 +50,7 @@ def __init__(self, config=None, policy=None): write_timeout=self.timeout, ) except serial.SerialException as e: - self.logger.critical(f"{e}") + self.logger.critical(f"XCPonSxI - {e}") raise self._packets = deque() @@ -84,7 +84,7 @@ def unpack_len_filler(args): self.unpacker = unpacker def connect(self): - self.logger.info(f"Serial comm_port openend: {self.comm_port.portstr}@{self.baudrate} Bits/Sec.") + self.logger.info(f"XCPonSxI - serial comm_port openend: {self.comm_port.portstr}@{self.baudrate} Bits/Sec.") self.startListener() def output(self, enable): diff --git a/pyxcp/transport/usb_transport.py b/pyxcp/transport/usb_transport.py index 947a656..4221338 100644 --- a/pyxcp/transport/usb_transport.py +++ b/pyxcp/transport/usb_transport.py @@ -87,7 +87,7 @@ def connect(self): except (USBError, USBTimeoutError): continue else: - raise Exception(f"Device with serial {self.serial_number!r} not found") + raise Exception(f"XCPonUSB - device with serial {self.serial_number!r} not found") current_configuration = self.device.get_active_configuration() if current_configuration.bConfigurationValue != self.configuration_number: From f739aa9de06d07bfa69bf000a97c0a540495f4bc Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 22 Jul 2024 14:26:18 +0300 Subject: [PATCH 86/99] today() --- pyxcp/examples/ex_arrow.py | 9 ++++----- pyxcp/examples/ex_mdf.py | 8 ++++---- pyxcp/examples/ex_sqlite.py | 19 +++++++++++-------- pyxcp/examples/run_daq.py | 2 +- pyxcp/examples/xcp_recording.py | 18 +++++++++--------- pyxcp/recorder/__init__.py | 2 +- pyxcp/recorder/decoders.hpp | 4 ++-- pyxcp/recorder/unfolder.hpp | 20 ++++++++++---------- pyxcp/recorder/wrap.cpp | 26 +++++++++++++------------- pyxcp/recorder/writer.hpp | 2 +- 10 files changed, 56 insertions(+), 54 deletions(-) diff --git a/pyxcp/examples/ex_arrow.py b/pyxcp/examples/ex_arrow.py index 71a0876..a435988 100644 --- a/pyxcp/examples/ex_arrow.py +++ b/pyxcp/examples/ex_arrow.py @@ -14,7 +14,7 @@ # import pyarrow.compute as pc import pyarrow.parquet as pq -from pyxcp.recorder import XcpLogFileUnfolder +from pyxcp.recorder import XcpLogFileDecoder MAP_TO_ARROW = { @@ -71,7 +71,7 @@ class StorageContainer: ts1: List[int] = field(default_factory=lambda: array("Q")) -class Unfolder(XcpLogFileUnfolder): +class Decoder(XcpLogFileDecoder): def initialize(self) -> None: # print("initialize()") @@ -121,6 +121,5 @@ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measu logger.info(f"Processing {args.xmraw_file!r}") logger.info(f"Processing {Path(args.xmraw_file)!r}") -lfr = Unfolder(args.xmraw_file) -res = lfr.run() -print(res) +decoder = Decoder(args.xmraw_file) +res = decoder.run() diff --git a/pyxcp/examples/ex_mdf.py b/pyxcp/examples/ex_mdf.py index 8f874fd..382e649 100644 --- a/pyxcp/examples/ex_mdf.py +++ b/pyxcp/examples/ex_mdf.py @@ -12,7 +12,7 @@ from asammdf.blocks.v4_blocks import HeaderBlock # ChannelGroup from asammdf.blocks.v4_constants import FLAG_HD_TIME_OFFSET_VALID # FLAG_HD_LOCAL_TIME, -from pyxcp.recorder import XcpLogFileUnfolder +from pyxcp.recorder import XcpLogFileDecoder MAP_TO_NP = { @@ -71,7 +71,7 @@ class StorageContainer: ts1: List[int] = field(default_factory=lambda: array("Q")) -class Unfolder(XcpLogFileUnfolder): +class Decoder(XcpLogFileDecoder): def __init__(self, recording_file_name: str): super().__init__(recording_file_name) @@ -130,5 +130,5 @@ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measu logger.info(f"Processing {args.xmraw_file!r}") logger.info(f"Processing {Path(args.xmraw_file)!r}") -lfr = Unfolder(args.xmraw_file) -res = lfr.run() +decoder = Decoder(args.xmraw_file) +res = decoder.run() diff --git a/pyxcp/examples/ex_sqlite.py b/pyxcp/examples/ex_sqlite.py index e422cc5..3822b0d 100644 --- a/pyxcp/examples/ex_sqlite.py +++ b/pyxcp/examples/ex_sqlite.py @@ -10,7 +10,7 @@ from pathlib import Path from typing import Any, List -from pyxcp.recorder import XcpLogFileUnfolder +from pyxcp.recorder import XcpLogFileDecoder from pyxcp.recorder.converter import MAP_TO_ARRAY @@ -51,7 +51,7 @@ class StorageContainer: ts1: List[int] = field(default_factory=lambda: array("Q")) -class Unfolder(XcpLogFileUnfolder): +class Decoder(XcpLogFileDecoder): def __init__(self, recording_file_name: str): super().__init__(recording_file_name) @@ -74,14 +74,16 @@ def initialize(self) -> None: array_txpe = MAP_TO_ARRAY[type_str] sql_type = MAP_TO_SQL[type_str] sd = Storage(name, sql_type, array(array_txpe)) - print(f"\t{name!r} {array_txpe} {sql_type}", sd) + # print(f"\t{name!r} {array_txpe} {sql_type}", sd) result.append(sd) sc = StorageContainer(dl.name, result) + print(f"Creating table {sc.name!r}.") self.create_table(sc) self.insert_stmt[sc.name] = ( f"""INSERT INTO {sc.name}({', '.join(['ts0', 'ts1'] + [r.name for r in sc.arr])}) VALUES({', '.join(["?" for _ in range(len(sc.arr) + 2)])})""" ) self.arrow_tables.append(sc) + print("\nInserting data...") def create_database(self, db_name: str) -> None: self.conn = sqlite3.Connection(db_name) @@ -110,15 +112,16 @@ def create_table(self, sc: StorageContainer) -> None: self.execute(ddl) self.execute("INSERT INTO table_names VALUES(?)", [sc.name]) - def execute(self, stmt: str) -> None: + def execute(self, *args: List[str]) -> None: try: - self.cursor.execute(stmt) + self.cursor.execute(*args) except Exception as e: print(e) - def finalize(self) -> Any: + def finalize(self) -> None: self.conn.commit() self.conn.close() + print("Done.") def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list) -> None: sc = self.arrow_tables[daq_list_num] @@ -130,5 +133,5 @@ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measu logger.info(f"Processing {args.xmraw_file!r}") logger.info(f"Processing {Path(args.xmraw_file)!r}") -lfr = Unfolder(args.xmraw_file) -lfr.run() +decoder = Decoder(args.xmraw_file) +decoder.run() diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index e195197..0f3ec9c 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -156,7 +156,7 @@ print("start DAQ lists.") daq_parser.start() # Start DAQ lists. - time.sleep(15.0 * 60.0) + time.sleep(3 * 15.0 * 60.0) # time.sleep(1 * 60.0 * 60.0) # Arbitrary termination condition.ls *. # time.sleep(6 * 60.0 * 60.0) # Arbitrary termination condition.ls *. print("Stop DAQ....") diff --git a/pyxcp/examples/xcp_recording.py b/pyxcp/examples/xcp_recording.py index 6f10025..5feb22e 100644 --- a/pyxcp/examples/xcp_recording.py +++ b/pyxcp/examples/xcp_recording.py @@ -2,7 +2,7 @@ import sys from pprint import pprint -from pyxcp.recorder import NumpyDecoder, XcpLogFileReader, XcpLogFileUnfolder +from pyxcp.recorder import NumpyDecoder, XcpLogFileDecoder, XcpLogFileReader from pyxcp.utils import hexDump @@ -14,7 +14,7 @@ print(args.xmraw_file) -class Unfolder(XcpLogFileUnfolder): +class Decoder(XcpLogFileDecoder): prev = 0 wraps = 0 @@ -26,18 +26,18 @@ def on_daq_list(self, daq_list_num, timestamp0, timestamp1, measurement): self.prev = timestamp1 -lfr = Unfolder(args.xmraw_file) +decoder = Decoder(args.xmraw_file) print("-" * 80) -print(lfr.get_header()) +print(decoder.get_header()) print("-" * 80) -lfr.run() -# print(lfr.parameters) +decoder.run() +# print(decoder.parameters) print("=" * 80) print("=" * 80) print("=" * 80) -print(lfr.daq_lists) -print(lfr.parameters.timestamp_info) -print("Wrap-arounds:", lfr.wraps) +print(decoder.daq_lists) +print(decoder.parameters.timestamp_info) +print("Wrap-arounds:", decoder.wraps) nnp = NumpyDecoder(args.xmraw_file) nnp.run() diff --git a/pyxcp/recorder/__init__.py b/pyxcp/recorder/__init__.py index 39a2d68..e675237 100644 --- a/pyxcp/recorder/__init__.py +++ b/pyxcp/recorder/__init__.py @@ -21,7 +21,7 @@ MeasurementParameters, NumpyDecoder, ValueHolder, - XcpLogFileUnfolder, + XcpLogFileDecoder, _PyXcpLogFileReader, _PyXcpLogFileWriter, data_types, diff --git a/pyxcp/recorder/decoders.hpp b/pyxcp/recorder/decoders.hpp index 21eef15..a4d00bc 100644 --- a/pyxcp/recorder/decoders.hpp +++ b/pyxcp/recorder/decoders.hpp @@ -110,12 +110,12 @@ class SequentialFileWriter { #endif }; -class NumpyDecoder : public XcpLogFileUnfolder { +class NumpyDecoder : public XcpLogFileDecoder { public: using writer_t = std::unique_ptr; - explicit NumpyDecoder(const std::string& file_name) : XcpLogFileUnfolder(file_name), m_path(file_name) { + explicit NumpyDecoder(const std::string& file_name) : XcpLogFileDecoder(file_name), m_path(file_name) { std::cout << "\n creating directory: " << m_path.stem() << std::endl; // std::filesystem::create_directory(m_path.stem()); } diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index f63947a..d8f1739 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -1106,7 +1106,7 @@ class DaqOnlinePolicy : public DAQPolicyBase { DaqOnlinePolicy() = default; void set_parameters(const MeasurementParameters& params) noexcept { - m_unfolder = std::make_unique(params); + m_decoder = std::make_unique(params); DAQPolicyBase::set_parameters(params); } @@ -1119,7 +1119,7 @@ class DaqOnlinePolicy : public DAQPolicyBase { if (frame_cat != static_cast(FrameCategory::DAQ)) { return; } - auto result = m_unfolder->feed(timestamp, payload); + auto result = m_decoder->feed(timestamp, payload); if (result) { const auto& [daq_list, ts0, ts1, meas] = *result; on_daq_list(daq_list, ts0, ts1, meas); @@ -1134,7 +1134,7 @@ class DaqOnlinePolicy : public DAQPolicyBase { private: - std::unique_ptr m_unfolder; + std::unique_ptr m_decoder; }; struct ValueHolder { @@ -1156,22 +1156,22 @@ struct ValueHolder { std::any m_value; }; -class XcpLogFileUnfolder { +class XcpLogFileDecoder { public: - explicit XcpLogFileUnfolder(const std::string& file_name) : m_reader(file_name) { + explicit XcpLogFileDecoder(const std::string& file_name) : m_reader(file_name) { auto metadata = m_reader.get_metadata(); if (metadata != "") { auto des = Deserializer(metadata); m_params = des.run(); - m_unfolder = std::make_unique(m_params); + m_decoder = std::make_unique(m_params); } else { // cannot proceed!!! } } - XcpLogFileUnfolder() = delete; - virtual ~XcpLogFileUnfolder() = default; + XcpLogFileDecoder() = delete; + virtual ~XcpLogFileDecoder() = default; virtual void initialize() { } @@ -1204,7 +1204,7 @@ class XcpLogFileUnfolder { if (frame_cat != static_cast(FrameCategory::DAQ)) { continue; } - auto result = m_unfolder->feed(timestamp, str_data); + auto result = m_decoder->feed(timestamp, str_data); if (result) { const auto& [daq_list, ts0, ts1, meas] = *result; on_daq_list(daq_list, ts0, ts1, meas); @@ -1234,7 +1234,7 @@ class XcpLogFileUnfolder { private: XcpLogFileReader m_reader; - std::unique_ptr m_unfolder; + std::unique_ptr m_decoder; MeasurementParameters m_params; }; diff --git a/pyxcp/recorder/wrap.cpp b/pyxcp/recorder/wrap.cpp index d1dca36..0955c28 100644 --- a/pyxcp/recorder/wrap.cpp +++ b/pyxcp/recorder/wrap.cpp @@ -49,24 +49,24 @@ class PyDaqRecorderPolicy : public DaqRecorderPolicy { } }; -class PyXcpLogFileUnfolder : public XcpLogFileUnfolder { +class PyXcpLogFileDecoder : public XcpLogFileDecoder { public: - using XcpLogFileUnfolder::XcpLogFileUnfolder; + using XcpLogFileDecoder::XcpLogFileDecoder; void on_daq_list( std::uint16_t daq_list_num, std::uint64_t timestamp0, std::uint64_t timestamp1, const std::vector& measurement ) override { - PYBIND11_OVERRIDE_PURE(void, XcpLogFileUnfolder, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); + PYBIND11_OVERRIDE_PURE(void, XcpLogFileDecoder, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement); } void initialize() override { - PYBIND11_OVERRIDE(void, XcpLogFileUnfolder, initialize); + PYBIND11_OVERRIDE(void, XcpLogFileDecoder, initialize); } void finalize() override { - PYBIND11_OVERRIDE(void, XcpLogFileUnfolder, finalize); + PYBIND11_OVERRIDE(void, XcpLogFileDecoder, finalize); } }; @@ -173,15 +173,15 @@ PYBIND11_MODULE(rekorder, m) { .def("set_parameters", &DaqOnlinePolicy::set_parameters) .def("initialize", &DaqOnlinePolicy::initialize); - py::class_(m, "XcpLogFileUnfolder", py::dynamic_attr()) + py::class_(m, "XcpLogFileDecoder", py::dynamic_attr()) .def(py::init()) - .def("run", &XcpLogFileUnfolder::run) - .def("on_daq_list", &XcpLogFileUnfolder::on_daq_list) - .def_property_readonly("parameters", &XcpLogFileUnfolder::get_parameters) - .def_property_readonly("daq_lists", &XcpLogFileUnfolder::get_daq_lists) - .def("get_header", &XcpLogFileUnfolder::get_header) - .def("initialize", &XcpLogFileUnfolder::initialize) - .def("finalize", &XcpLogFileUnfolder::finalize); + .def("run", &XcpLogFileDecoder::run) + .def("on_daq_list", &XcpLogFileDecoder::on_daq_list) + .def_property_readonly("parameters", &XcpLogFileDecoder::get_parameters) + .def_property_readonly("daq_lists", &XcpLogFileDecoder::get_daq_lists) + .def("get_header", &XcpLogFileDecoder::get_header) + .def("initialize", &XcpLogFileDecoder::initialize) + .def("finalize", &XcpLogFileDecoder::finalize); py::class_(m, "NumpyDecoder").def(py::init()).def("run", &NumpyDecoder::run); diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 208e04d..1b947ad 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -71,7 +71,7 @@ class XcpLogFileWriter { if (m_container_record_count) { compress_frames(); - } + }lfr std::uint16_t options = m_metadata.empty() ? 0 : XMRAW_HAS_METADATA; From 01a7cceb3f30dccb7155b0c28321fc8b7cba3a3c Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 22 Jul 2024 14:46:52 +0300 Subject: [PATCH 87/99] today() --- pyxcp/examples/ex_arrow.py | 8 -------- pyxcp/examples/ex_mdf.py | 3 --- pyxcp/examples/ex_sqlite.py | 3 --- 3 files changed, 14 deletions(-) diff --git a/pyxcp/examples/ex_arrow.py b/pyxcp/examples/ex_arrow.py index a435988..32adf3b 100644 --- a/pyxcp/examples/ex_arrow.py +++ b/pyxcp/examples/ex_arrow.py @@ -4,14 +4,9 @@ import logging from array import array from dataclasses import dataclass, field -from pathlib import Path - -# from pprint import pprint from typing import Any, List import pyarrow as pa - -# import pyarrow.compute as pc import pyarrow.parquet as pq from pyxcp.recorder import XcpLogFileDecoder @@ -118,8 +113,5 @@ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measu sto.arr.append(elem) -logger.info(f"Processing {args.xmraw_file!r}") -logger.info(f"Processing {Path(args.xmraw_file)!r}") - decoder = Decoder(args.xmraw_file) res = decoder.run() diff --git a/pyxcp/examples/ex_mdf.py b/pyxcp/examples/ex_mdf.py index 382e649..0129579 100644 --- a/pyxcp/examples/ex_mdf.py +++ b/pyxcp/examples/ex_mdf.py @@ -127,8 +127,5 @@ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measu sto.arr.append(elem) -logger.info(f"Processing {args.xmraw_file!r}") -logger.info(f"Processing {Path(args.xmraw_file)!r}") - decoder = Decoder(args.xmraw_file) res = decoder.run() diff --git a/pyxcp/examples/ex_sqlite.py b/pyxcp/examples/ex_sqlite.py index 3822b0d..6d3d3f3 100644 --- a/pyxcp/examples/ex_sqlite.py +++ b/pyxcp/examples/ex_sqlite.py @@ -130,8 +130,5 @@ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measu self.execute(insert_stmt, data) -logger.info(f"Processing {args.xmraw_file!r}") -logger.info(f"Processing {Path(args.xmraw_file)!r}") - decoder = Decoder(args.xmraw_file) decoder.run() From f598f075e337621bf908ca054dda4a6adb5bdfd6 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 24 Jul 2024 05:49:56 +0300 Subject: [PATCH 88/99] Fix include --- pyxcp/recorder/unfolder.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index d8f1739..67082f4 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -8,7 +8,9 @@ #include #include #include +#if __has_include() #include +#endif #include #include "daqlist.hpp" From 00de7f5848da51abcb7a3c30f122f141c1787fd0 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 24 Jul 2024 08:32:20 +0300 Subject: [PATCH 89/99] fix scope --- CMakeLists.txt | 2 +- pyxcp/asamkeydll.c | 1 - pyxcp/recorder/decoders.hpp | 4 ++-- pyxcp/recorder/writer.hpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63a28ac..912d6bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows") message("Platform is WINDOWS") SET(MSVC_EXTRA_OPTIONS "") elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") - SET(GCC_N_CLANG_EXTRA_OPTIONS "-fcoroutines -fvisibility=hidden -g0") + SET(GCC_N_CLANG_EXTRA_OPTIONS "-fvisibility=hidden -g0") # -fcoroutines message("Platform is LINUX") endif() diff --git a/pyxcp/asamkeydll.c b/pyxcp/asamkeydll.c index 3530c44..eed22b2 100644 --- a/pyxcp/asamkeydll.c +++ b/pyxcp/asamkeydll.c @@ -110,7 +110,6 @@ int main(int argc, char ** argv) } res = GetKey((char *)&dllname, privilege, seedlen, (BYTE *)&seedBuffer, &keylen, (BYTE *)&keyBuffer); - printf("%lu\n", res); if (res == 0) { hexlify(keyBuffer, keylen); } diff --git a/pyxcp/recorder/decoders.hpp b/pyxcp/recorder/decoders.hpp index a4d00bc..1705a00 100644 --- a/pyxcp/recorder/decoders.hpp +++ b/pyxcp/recorder/decoders.hpp @@ -92,9 +92,9 @@ class SequentialFileWriter { #if defined(_WIN32) || defined(_WIN64) DWORD dwBytesWritten; - WriteFile(m_fd, str_value.c_str(), std::size(str_value), &dwBytesWritten, nullptr); + ::WriteFile(m_fd, str_value.c_str(), std::size(str_value), &dwBytesWritten, nullptr); #else - write(m_fd, str_value.c_str(), str_value.size()); + ::write(m_fd, str_value.c_str(), str_value.size()); #endif } diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 1b947ad..208e04d 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -71,7 +71,7 @@ class XcpLogFileWriter { if (m_container_record_count) { compress_frames(); - }lfr + } std::uint16_t options = m_metadata.empty() ? 0 : XMRAW_HAS_METADATA; From 027de2e0954f121c229e652490eb785d709eaec5 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Wed, 24 Jul 2024 13:45:01 +0300 Subject: [PATCH 90/99] today --- pyxcp/cpp_ext/helper.hpp | 3 ++- pyxcp/examples/run_daq.py | 2 +- pyxcp/recorder/writer.hpp | 7 +++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 9462a43..7663aaf 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -16,8 +16,9 @@ #include #if (__cplusplus >= 202302L) || (__STDC_VERSION__ >= 202302L) +#if __has_include() #include - +#endif #if defined(__STDCPP_BFLOAT16_T__) #define HAS_BFLOAT16 (1) #else diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 0f3ec9c..22580f4 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -141,7 +141,7 @@ ), ] """ -# daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. +#daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. daq_parser = DaqRecorder(DAQ_LISTS, "run_daq", 2) with ap.run(policy=daq_parser) as x: diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 208e04d..e5503cb 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -17,6 +17,8 @@ class XcpLogFileWriter { } m_opened = false; + std::cout << "Writer::c_tor()\n"; + #if defined(_WIN32) m_fd = CreateFileA( m_file_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) nullptr, CREATE_ALWAYS, @@ -61,14 +63,19 @@ class XcpLogFileWriter { void finalize() { std::error_code ec; + std::cout << "Enter finalize...\n"; if (!m_finalized) { m_finalized = true; stop_thread(); + std::cout << "opened? "; if (!m_opened) { + std::cout << "No.\n"; return; } + std::cout << "Yes. continue\n"; + if (m_container_record_count) { compress_frames(); } From 9881a8da654c02b57bd0333d1121a1d336172604 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 25 Jul 2024 10:18:39 +0300 Subject: [PATCH 91/99] today() --- pyxcp/daq_stim/__init__.py | 4 ++++ pyxcp/examples/run_daq.py | 24 ++++++++++++------------ pyxcp/recorder/rekorder.hpp | 1 + pyxcp/recorder/unfolder.hpp | 17 ++++++++++++++++- pyxcp/recorder/writer.hpp | 1 + 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index b13868f..6f71bef 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -167,6 +167,7 @@ def first_pids(self): class DaqRecorder(DaqProcessor, _DaqRecorderPolicy): def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 200, chunk_size: int = 1): + print("DaqRecorder::__init__()") DaqProcessor.__init__(self, daq_lists) _DaqRecorderPolicy.__init__(self) self.file_name = file_name @@ -174,14 +175,17 @@ def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 200 self.chunk_size = chunk_size def initialize(self): + print("DaqRecorder::initialize()") metadata = self.measurement_params.dumps() _DaqRecorderPolicy.create_writer(self, self.file_name, self.prealloc, self.chunk_size, metadata) _DaqRecorderPolicy.initialize(self) def finalize(self): + print("DaqRecorder::finalize()") _DaqRecorderPolicy.finalize(self) def start(self): + print("DaqRecorder::start()") DaqProcessor.start(self) diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 22580f4..47c2d00 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -10,8 +10,8 @@ ap = ArgumentParser(description="DAQ test") -# XCP_LITE = True -XCP_LITE = False +XCP_LITE = True +# XCP_LITE = False # Completly random configurations, only for illustrative purposes. # @@ -26,10 +26,10 @@ False, False, [ - ("byteCounter", 0x203EA, 0, "U8"), - ("wordCounter", 0x203EC, 0, "U16"), - ("dwordCounter", 0x20410, 0, "U32"), - ("sbyteCounter", 0x203EB, 0, "I8"), + ("byteCounter", 0x31698, 0, "U8"), + ("wordCounter", 0x3169A, 0, "U16"), + ("dwordCounter", 0x3169C, 0, "U32"), + ("sbyteCounter", 0x316A0, 0, "I8"), ], ), DaqList( @@ -38,11 +38,11 @@ False, False, [ - ("swordCounter", 0x20414, 0, "I16"), - ("sdwordCounter", 0x20418, 0, "I32"), - ("channel1", 0x203F8, 0, "F64"), - ("channel2", 0x20400, 0, "F64"), - ("channel3", 0x20408, 0, "F64"), + ("swordCounter", 0x316A2, 0, "I16"), + ("sdwordCounter", 0x316A4, 0, "I32"), + ("channel1", 0x30280, 0, "F64"), + ("channel2", 0x30288, 0, "F64"), + ("channel3", 0x30290, 0, "F64"), ], ), ] @@ -141,7 +141,7 @@ ), ] """ -#daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. +# daq_parser = DaqToCsv(DAQ_LISTS) # Saves our measurement data to one or more CSV file(s)k. daq_parser = DaqRecorder(DAQ_LISTS, "run_daq", 2) with ap.run(policy=daq_parser) as x: diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index d42e3ee..6be9b46 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -69,6 +69,7 @@ constexpr auto megabytes(std::uint32_t value) -> std::uint32_t { constexpr std::uint16_t XCP_PAYLOAD_MAX = 0xFFFFUL; +constexpr std::uint16_t XMRAW_RELATIVE_TIMESTAMPS = 0x0002UL; constexpr std::uint16_t XMRAW_HAS_METADATA = 0x0004UL; /* diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 67082f4..8708e2a 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -1064,12 +1064,14 @@ class DaqRecorderPolicy : public DAQPolicyBase { public: ~DaqRecorderPolicy() { + std::cout << "DaqRecorderPolicy::~DaqRecorderPolicy()\n"; finalize(); } DaqRecorderPolicy() = default; void set_parameters(const MeasurementParameters& params) noexcept override { + std::cout << "DaqRecorderPolicy::set_parameters()\n"; m_params = params; DAQPolicyBase::set_parameters(params); } @@ -1079,24 +1081,37 @@ class DaqRecorderPolicy : public DAQPolicyBase { // Only record DAQ frames for now. return; } + if (!m_valid) { + std::cerr << "DAQ recorder policy not initialized!" << std::endl; + return; + } + std::cout << "DAQ frame received: frame_cat=" << static_cast(frame_cat) << ", counter=" << counter << ", timestamp=" << timestamp << std::endl; m_writer->add_frame(frame_cat, counter, timestamp, static_cast(payload.size()), payload.c_str()); } void create_writer(const std::string& file_name, std::uint32_t prealloc, std::uint32_t chunk_size, std::string_view metadata) { + std::cout << "DaqRecorderPolicy::create_writer()\n"; m_writer = std::make_unique(file_name, prealloc, chunk_size, metadata); + if (m_writer) { + m_valid=true; + } } void initialize() override { + std::cout << "DaqRecorderPolicy::initialize()\n"; } void finalize() override { + std::cout << "DaqRecorderPolicy::finalize()\n"; m_writer->finalize(); + m_valid=false; } private: - std::unique_ptr m_writer; + std::unique_ptr m_writer{nullptr}; MeasurementParameters m_params; + bool m_valid{false}; }; class DaqOnlinePolicy : public DAQPolicyBase { diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index e5503cb..5057645 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -104,6 +104,7 @@ class XcpLogFileWriter { delete m_mmap; delete[] m_intermediate_storage; } + std::cout << "Exit finalize...\n"; } void add_frame(uint8_t category, uint16_t counter, std::uint64_t timestamp, uint16_t length, char const *data) { From 59e0b5f4364d52f4914fce635bec698051e0b697 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Thu, 25 Jul 2024 11:38:19 +0300 Subject: [PATCH 92/99] Fix crash --- pyxcp/cpp_ext/helper.hpp | 6 +++--- pyxcp/daq_stim/__init__.py | 4 ---- pyxcp/examples/run_daq.py | 4 ++-- pyxcp/recorder/rekorder.hpp | 2 +- pyxcp/recorder/unfolder.hpp | 29 ++++++++++------------------- pyxcp/recorder/writer.hpp | 6 ------ 6 files changed, 16 insertions(+), 35 deletions(-) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 7663aaf..3e9f91f 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -16,9 +16,9 @@ #include #if (__cplusplus >= 202302L) || (__STDC_VERSION__ >= 202302L) -#if __has_include() - #include -#endif + #if __has_include() + #include + #endif #if defined(__STDCPP_BFLOAT16_T__) #define HAS_BFLOAT16 (1) #else diff --git a/pyxcp/daq_stim/__init__.py b/pyxcp/daq_stim/__init__.py index 6f71bef..b13868f 100644 --- a/pyxcp/daq_stim/__init__.py +++ b/pyxcp/daq_stim/__init__.py @@ -167,7 +167,6 @@ def first_pids(self): class DaqRecorder(DaqProcessor, _DaqRecorderPolicy): def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 200, chunk_size: int = 1): - print("DaqRecorder::__init__()") DaqProcessor.__init__(self, daq_lists) _DaqRecorderPolicy.__init__(self) self.file_name = file_name @@ -175,17 +174,14 @@ def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 200 self.chunk_size = chunk_size def initialize(self): - print("DaqRecorder::initialize()") metadata = self.measurement_params.dumps() _DaqRecorderPolicy.create_writer(self, self.file_name, self.prealloc, self.chunk_size, metadata) _DaqRecorderPolicy.initialize(self) def finalize(self): - print("DaqRecorder::finalize()") _DaqRecorderPolicy.finalize(self) def start(self): - print("DaqRecorder::start()") DaqProcessor.start(self) diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index 47c2d00..d1a1868 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -10,8 +10,8 @@ ap = ArgumentParser(description="DAQ test") -XCP_LITE = True -# XCP_LITE = False +# XCP_LITE = True +XCP_LITE = False # Completly random configurations, only for illustrative purposes. # diff --git a/pyxcp/recorder/rekorder.hpp b/pyxcp/recorder/rekorder.hpp index 6be9b46..380eeaf 100644 --- a/pyxcp/recorder/rekorder.hpp +++ b/pyxcp/recorder/rekorder.hpp @@ -70,7 +70,7 @@ constexpr auto megabytes(std::uint32_t value) -> std::uint32_t { constexpr std::uint16_t XCP_PAYLOAD_MAX = 0xFFFFUL; constexpr std::uint16_t XMRAW_RELATIVE_TIMESTAMPS = 0x0002UL; -constexpr std::uint16_t XMRAW_HAS_METADATA = 0x0004UL; +constexpr std::uint16_t XMRAW_HAS_METADATA = 0x0004UL; /* byte-order is, where applicable little ending (LSB first). diff --git a/pyxcp/recorder/unfolder.hpp b/pyxcp/recorder/unfolder.hpp index 8708e2a..57bd0a2 100644 --- a/pyxcp/recorder/unfolder.hpp +++ b/pyxcp/recorder/unfolder.hpp @@ -9,7 +9,7 @@ #include #include #if __has_include() -#include + #include #endif #include @@ -1064,14 +1064,12 @@ class DaqRecorderPolicy : public DAQPolicyBase { public: ~DaqRecorderPolicy() { - std::cout << "DaqRecorderPolicy::~DaqRecorderPolicy()\n"; finalize(); } DaqRecorderPolicy() = default; void set_parameters(const MeasurementParameters& params) noexcept override { - std::cout << "DaqRecorderPolicy::set_parameters()\n"; m_params = params; DAQPolicyBase::set_parameters(params); } @@ -1081,37 +1079,30 @@ class DaqRecorderPolicy : public DAQPolicyBase { // Only record DAQ frames for now. return; } - if (!m_valid) { - std::cerr << "DAQ recorder policy not initialized!" << std::endl; - return; - } - std::cout << "DAQ frame received: frame_cat=" << static_cast(frame_cat) << ", counter=" << counter << ", timestamp=" << timestamp << std::endl; m_writer->add_frame(frame_cat, counter, timestamp, static_cast(payload.size()), payload.c_str()); } void create_writer(const std::string& file_name, std::uint32_t prealloc, std::uint32_t chunk_size, std::string_view metadata) { - std::cout << "DaqRecorderPolicy::create_writer()\n"; m_writer = std::make_unique(file_name, prealloc, chunk_size, metadata); - if (m_writer) { - m_valid=true; - } } void initialize() override { - std::cout << "DaqRecorderPolicy::initialize()\n"; + m_initialized = true; } void finalize() override { - std::cout << "DaqRecorderPolicy::finalize()\n"; + if (!m_initialized) { + return; + } m_writer->finalize(); - m_valid=false; + m_initialized = false; } private: - std::unique_ptr m_writer{nullptr}; + std::unique_ptr m_writer{ nullptr }; MeasurementParameters m_params; - bool m_valid{false}; + bool m_initialized{ false }; }; class DaqOnlinePolicy : public DAQPolicyBase { @@ -1179,8 +1170,8 @@ class XcpLogFileDecoder { explicit XcpLogFileDecoder(const std::string& file_name) : m_reader(file_name) { auto metadata = m_reader.get_metadata(); if (metadata != "") { - auto des = Deserializer(metadata); - m_params = des.run(); + auto des = Deserializer(metadata); + m_params = des.run(); m_decoder = std::make_unique(m_params); } else { // cannot proceed!!! diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 5057645..631e839 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -63,19 +63,14 @@ class XcpLogFileWriter { void finalize() { std::error_code ec; - std::cout << "Enter finalize...\n"; if (!m_finalized) { m_finalized = true; stop_thread(); - std::cout << "opened? "; if (!m_opened) { - std::cout << "No.\n"; return; } - std::cout << "Yes. continue\n"; - if (m_container_record_count) { compress_frames(); } @@ -104,7 +99,6 @@ class XcpLogFileWriter { delete m_mmap; delete[] m_intermediate_storage; } - std::cout << "Exit finalize...\n"; } void add_frame(uint8_t category, uint16_t counter, std::uint64_t timestamp, uint16_t length, char const *data) { From 7d4eef66d697d2c300a1718d2b60a7b9ddc73d05 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Fri, 26 Jul 2024 15:08:34 +0300 Subject: [PATCH 93/99] Some pyrigth fixes --- build_ext.py | 2 +- poetry.lock | 24 +- pyproject.toml | 1 + pyxcp/cmdline.py | 4 +- pyxcp/config/__init__.py | 65 +++-- pyxcp/constants.py | 6 +- pyxcp/dllif.py | 20 +- pyxcp/examples/xcp_user_supplied_driver.py | 2 +- pyxcp/examples/xcphello.py | 3 - pyxcp/master/master.py | 8 +- pyxcp/py.typed | 0 pyxcp/tests/test_can.py | 316 ++++++++++----------- pyxcp/tests/test_config.py | 64 ----- pyxcp/tests/test_frame_padding.py | 6 +- pyxcp/tests/test_master.py | 64 +++-- pyxcp/tests/test_transport.py | 20 +- pyxcp/transport/base.py | 143 +++++----- pyxcp/transport/can.py | 54 ++-- pyxcp/transport/eth.py | 21 +- pyxcp/transport/sxi.py | 17 +- pyxcp/transport/usb_transport.py | 51 ++-- 21 files changed, 433 insertions(+), 458 deletions(-) create mode 100644 pyxcp/py.typed delete mode 100644 pyxcp/tests/test_config.py diff --git a/build_ext.py b/build_ext.py index 34f00a9..5c365e6 100644 --- a/build_ext.py +++ b/build_ext.py @@ -26,7 +26,7 @@ def banner(msg: str) -> None: def build_extension(debug: bool = False) -> None: print("CMakeBuild::build_extension()") - debug = int(os.environ.get("DEBUG", 0)) or debug + debug = bool(os.environ.get("DEBUG", 0)) or debug cfg = "Debug" if debug else "Release" print(f" BUILD-TYPE: {cfg!r}") diff --git a/poetry.lock b/poetry.lock index c8646d9..4db230a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2327,6 +2327,28 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +[[package]] +name = "win-precise-time" +version = "1.4.2" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "win-precise-time-1.4.2.tar.gz", hash = "sha256:89274785cbc5f2997e01675206da3203835a442c60fd97798415c6b3c179c0b9"}, + {file = "win_precise_time-1.4.2-cp310-cp310-win32.whl", hash = "sha256:7fa13a2247c2ef41cd5e9b930f40716eacc7fc1f079ea72853bd5613fe087a1a"}, + {file = "win_precise_time-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:bb8e44b0fc35fde268e8a781cdcd9f47d47abcd8089465d2d1d1063976411c8e"}, + {file = "win_precise_time-1.4.2-cp311-cp311-win32.whl", hash = "sha256:59272655ad6f36910d0b585969402386fa627fca3be24acc9a21be1d550e5db8"}, + {file = "win_precise_time-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:0897bb055f19f3b4336e2ba6bee0115ac20fd7ec615a6d736632e2df77f8851a"}, + {file = "win_precise_time-1.4.2-cp312-cp312-win32.whl", hash = "sha256:0210dcea88a520c91de1708ae4c881e3c0ddc956daa08b9eabf2b7c35f3109f5"}, + {file = "win_precise_time-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:85670f77cc8accd8f1e6d05073999f77561c23012a9ee988cbd44bb7ce655062"}, + {file = "win_precise_time-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:3e23693201a0fc6ca39f016871e2581e20c91123734bd48a69259f8c8724eedb"}, + {file = "win_precise_time-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:07ef644d1bb7705039bc54abfe4b45e99e8dc326dfd1dad5831dab19670508cb"}, + {file = "win_precise_time-1.4.2-cp38-cp38-win32.whl", hash = "sha256:0a953b00772f205602fa712ef68387b8fb213a30b267ae310aa56bf17605e11b"}, + {file = "win_precise_time-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b5d83420925beca302b386b19c3e7414ada84b47b42f0680207f1508917a1731"}, + {file = "win_precise_time-1.4.2-cp39-cp39-win32.whl", hash = "sha256:50d11a6ff92e1be96a8d4bee99ff6dc07a0ea0e2a392b0956bb2192e334f41ba"}, + {file = "win_precise_time-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f510fa92d9c39ea533c983e1d62c7bc66fdf0a3e3c3bdda48d4ebb634ff7034"}, +] + [[package]] name = "wrapt" version = "1.16.0" @@ -2451,4 +2473,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "2afd9ffcdd5ad1a6dbc0ef02a986f769bf99859fc19fb4e96e2e1e73f1773a29" +content-hash = "8a979a0f5f5e9158ba542535bc159d5a698854e0229f960c9d8738153cef2431" diff --git a/pyproject.toml b/pyproject.toml index 5e7ffb8..91469e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,7 @@ toml = "^0.10.2" bandit = "^1.7.8" tomlkit = "^0.12.5" pytz = "^2024.1" +win-precise-time = "^1.4.2" [tool.poetry.group.dev.dependencies] ruff = "^0.1.0" diff --git a/pyxcp/cmdline.py b/pyxcp/cmdline.py index 875874f..876d2d3 100644 --- a/pyxcp/cmdline.py +++ b/pyxcp/cmdline.py @@ -25,10 +25,10 @@ def __init__(self, callout=None, *args, **kws): if callout is not None: warnings.warn("callout argument is not supported anymore", DeprecationWarning, 2) - def run(self, policy=None): + def run(self, policy=None, transport_layer_interface=None): application = create_application() transport = application.transport.layer - master = Master(transport, config=application, policy=policy) + master = Master(transport, config=application, policy=policy, transport_layer_interface=transport_layer_interface) return master @property diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index 97dbb3f..f91e5d9 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -191,7 +191,7 @@ class Kvaser(SingletonConfigurable, CanBase): accept_virtual = Bool(default_value=None, allow_none=True, help="If virtual channels should be accepted.").tag(config=True) no_samp = Enum( - [1, 3], + values=[1, 3], default_value=None, allow_none=True, help="""Either 1 or 3. Some CAN controllers can also sample each bit three times. @@ -274,17 +274,17 @@ class PCan(SingletonConfigurable, CanBase): first one that matches the parameter value. If no device is found, an exception is raised.""", ).tag(config=True) - state = Instance(can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) + state = Instance(klass=can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) f_clock = Enum( - [20000000, 24000000, 30000000, 40000000, 60000000, 80000000], + values=[20000000, 24000000, 30000000, 40000000, 60000000, 80000000], default_value=None, allow_none=True, help="""Ignored if not using CAN-FD. Pass either f_clock or f_clock_mhz.""", ).tag(config=True) f_clock_mhz = Enum( - [20, 24, 30, 40, 60, 80], + values=[20, 24, 30, 40, 60, 80], default_value=None, allow_none=True, help="""Ignored if not using CAN-FD. @@ -338,11 +338,11 @@ class SeeedStudio(SingletonConfigurable, CanBase): timeout = Float(default_value=None, allow_none=True, help="Timeout for the serial device in seconds.").tag(config=True) baudrate = Integer(default_value=None, allow_none=True, help="Baud rate of the serial device in bit/s.").tag(config=True) - frame_type = Enum(["STD", "EXT"], default_value=None, allow_none=True, help="To select standard or extended messages.").tag( - config=True - ) + frame_type = Enum( + values=["STD", "EXT"], default_value=None, allow_none=True, help="To select standard or extended messages." + ).tag(config=True) operation_mode = Enum( - ["normal", "loopback", "silent", "loopback_and_silent"], default_value=None, allow_none=True, help=""" """ + values=["normal", "loopback", "silent", "loopback_and_silent"], default_value=None, allow_none=True, help=""" """ ).tag(config=True) @@ -413,7 +413,7 @@ class Systec(SingletonConfigurable, CanBase): has_receive_own_messages = True - state = Instance(can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) + state = Instance(klass=can.BusState, default_value=None, allow_none=True, help="BusState of the channel.").tag(config=True) device_number = Integer(min=0, max=254, default_value=None, allow_none=True, help="The device number of the USB-CAN.").tag( config=True ) @@ -548,9 +548,9 @@ class Virtual(SingletonConfigurable, CanBase): class Can(SingletonConfigurable): VALID_INTERFACES = can.interfaces.VALID_INTERFACES - interface = Enum(VALID_INTERFACES, default_value=None, allow_none=True, help="CAN interface supported by python-can").tag( - config=True - ) + interface = Enum( + values=VALID_INTERFACES, default_value=None, allow_none=True, help="CAN interface supported by python-can" + ).tag(config=True) channel = Any( default_value=None, allow_none=True, help="Channel identification. Expected type and value is backend dependent." ).tag(config=True) @@ -592,7 +592,7 @@ class Can(SingletonConfigurable): tseg1_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg1 (data).").tag(config=True) tseg2_dbr = Integer(default_value=None, allow_none=True, help="Bus timing value tseg2 (data).").tag(config=True) timing = Union( - [Instance(can.BitTiming)], # , Instance(can.BitTimingFd) + trait_types=[Instance(klass=can.BitTiming), Instance(klass=can.BitTimingFd)], default_value=None, allow_none=True, help="""Custom bit timing settings. @@ -669,7 +669,7 @@ class Eth(SingletonConfigurable): host = Unicode("localhost", help="Hostname or IP address of XCP slave.").tag(config=True) port = Integer(5555, help="TCP/UDP port to connect.").tag(config=True) - protocol = Enum(["TCP", "UDP"], default_value="UDP", help="").tag(config=True) + protocol = Enum(values=["TCP", "UDP"], default_value="UDP", help="").tag(config=True) ipv6 = Bool(False, help="Use IPv6 if `True` else IPv4.").tag(config=True) tcp_nodelay = Bool(False, help="*** Expert option *** -- Disable Nagle's algorithm if `True`.").tag(config=True) bind_to_address = Unicode(default_value=None, allow_none=True, help="Bind to specific local address.").tag(config=True) @@ -681,11 +681,11 @@ class SxI(SingletonConfigurable): port = Unicode("COM1", help="Name of communication interface.").tag(config=True) bitrate = Integer(38400, help="Connection bitrate").tag(config=True) - bytesize = Enum([5, 6, 7, 8], default_value=8, help="Size of byte.").tag(config=True) - parity = Enum(["N", "E", "O", "M", "S"], default_value="N", help="Paritybit calculation.").tag(config=True) - stopbits = Enum([1, 1.5, 2], default_value=1, help="Number of stopbits.").tag(config=True) + bytesize = Enum(values=[5, 6, 7, 8], default_value=8, help="Size of byte.").tag(config=True) + parity = Enum(values=["N", "E", "O", "M", "S"], default_value="N", help="Paritybit calculation.").tag(config=True) + stopbits = Enum(values=[1, 1.5, 2], default_value=1, help="Number of stopbits.").tag(config=True) mode = Enum( - [ + values=[ "ASYNCH_FULL_DUPLEX_MODE", "SYNCH_FULL_DUPLEX_MODE_BYTE", "SYNCH_FULL_DUPLEX_MODE_WORD", @@ -698,7 +698,7 @@ class SxI(SingletonConfigurable): help="Asynchronous (SCI) or synchronous (SPI) communication mode.", ).tag(config=True) header_format = Enum( - [ + values=[ "HEADER_LEN_BYTE", "HEADER_LEN_CTR_BYTE", "HEADER_LEN_FILL_BYTE", @@ -721,7 +721,7 @@ class SxI(SingletonConfigurable): """, ).tag(config=True) tail_format = Enum( - ["NO_CHECKSUM", "CHECKSUM_BYTE", "CHECKSUM_WORD"], default_value="NO_CHECKSUM", help="XCPonSxI tail format." + values=["NO_CHECKSUM", "CHECKSUM_BYTE", "CHECKSUM_WORD"], default_value="NO_CHECKSUM", help="XCPonSxI tail format." ).tag(config=True) framing = Bool(False, help="Enable SCI framing mechanism (ESC chars).").tag(config=True) esc_sync = Integer(0x01, min=0, max=255, help="SCI framing protocol character SYNC.").tag(config=True) @@ -738,7 +738,7 @@ class Usb(SingletonConfigurable): product_id = Integer(0, help="USB product ID.").tag(config=True) library = Unicode("", help="Absolute path to USB shared library.").tag(config=True) header_format = Enum( - [ + values=[ "HEADER_LEN_BYTE", "HEADER_LEN_CTR_BYTE", "HEADER_LEN_FILL_BYTE", @@ -751,34 +751,36 @@ class Usb(SingletonConfigurable): ).tag(config=True) in_ep_number = Integer(1, help="Ingoing USB reply endpoint number (IN-EP for RES/ERR, DAQ, and EV/SERV).").tag(config=True) in_ep_transfer_type = Enum( - ["BULK_TRANSFER", "INTERRUPT_TRANSFER"], default_value="BULK_TRANSFER", help="Ingoing: Supported USB transfer types." + values=["BULK_TRANSFER", "INTERRUPT_TRANSFER"], default_value="BULK_TRANSFER", help="Ingoing: Supported USB transfer types." ).tag(config=True) in_ep_max_packet_size = Integer(512, help="Ingoing: Maximum packet size of endpoint in bytes.").tag(config=True) in_ep_polling_interval = Integer(0, help="Ingoing: Polling interval of endpoint.").tag(config=True) in_ep_message_packing = Enum( - ["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], + values=["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], default_value="MESSAGE_PACKING_SINGLE", help="Ingoing: Packing of XCP Messages.", ).tag(config=True) in_ep_alignment = Enum( - ["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], + values=["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], default_value="ALIGNMENT_8_BIT", help="Ingoing: Alignment border.", ).tag(config=True) in_ep_recommended_host_bufsize = Integer(0, help="Ingoing: Recommended host buffer size.").tag(config=True) out_ep_number = Integer(0, help="Outgoing USB command endpoint number (OUT-EP for CMD and STIM).").tag(config=True) out_ep_transfer_type = Enum( - ["BULK_TRANSFER", "INTERRUPT_TRANSFER"], default_value="BULK_TRANSFER", help="Outgoing: Supported USB transfer types." + values=["BULK_TRANSFER", "INTERRUPT_TRANSFER"], + default_value="BULK_TRANSFER", + help="Outgoing: Supported USB transfer types.", ).tag(config=True) out_ep_max_packet_size = Integer(512, help="Outgoing: Maximum packet size of endpoint in bytes.").tag(config=True) out_ep_polling_interval = Integer(0, help="Outgoing: Polling interval of endpoint.").tag(config=True) out_ep_message_packing = Enum( - ["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], + values=["MESSAGE_PACKING_SINGLE", "MESSAGE_PACKING_MULTIPLE", "MESSAGE_PACKING_STREAMING"], default_value="MESSAGE_PACKING_SINGLE", help="Outgoing: Packing of XCP Messages.", ).tag(config=True) out_ep_alignment = Enum( - ["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], + values=["ALIGNMENT_8_BIT", "ALIGNMENT_16_BIT", "ALIGNMENT_32_BIT", "ALIGNMENT_64_BIT"], default_value="ALIGNMENT_8_BIT", help="Outgoing: Alignment border.", ).tag(config=True) @@ -791,11 +793,14 @@ class Transport(SingletonConfigurable): classes = List([Can, Eth, SxI, Usb]) layer = Enum( - ["CAN", "ETH", "SXI", "USB"], default_value=None, allow_none=True, help="Choose one of the supported XCP transport layers." + values=["CAN", "ETH", "SXI", "USB"], + default_value=None, + allow_none=True, + help="Choose one of the supported XCP transport layers.", ).tag(config=True) create_daq_timestamps = Bool(False, help="Record time of frame reception or set timestamp to 0.").tag(config=True) timestamp_mode = Enum( - ["ABSOLUTE", "RELATIVE"], + values=["ABSOLUTE", "RELATIVE"], default_value="ABSOLUTE", help="""Either absolute timestamps since some epoch or program start, both values are in nano-seconds.""", ).tag(config=True) @@ -804,7 +809,7 @@ class Transport(SingletonConfigurable): help="""raise `XcpTimeoutError` after `timeout` seconds if there is no response to a command.""", ).tag(config=True) - alignment = Enum([1, 2, 4, 8], default_value=1).tag(config=True) + alignment = Enum(values=[1, 2, 4, 8], default_value=1).tag(config=True) can = Instance(Can).tag(config=True) eth = Instance(Eth).tag(config=True) diff --git a/pyxcp/constants.py b/pyxcp/constants.py index f2945c0..c78dad5 100644 --- a/pyxcp/constants.py +++ b/pyxcp/constants.py @@ -1,10 +1,10 @@ #!/usr/bin/env python import struct -from typing import Callable, NewType +from typing import Any, Callable -PackerType = NewType("PackerType", Callable[[int], bytes]) -UnpackerType = NewType("UnpackerType", Callable[[bytes], int]) +PackerType = Callable[[int], bytes] +UnpackerType = Callable[[bytes], tuple[Any, ...]] def makeBytePacker(byteorder: str = "@") -> PackerType: diff --git a/pyxcp/dllif.py b/pyxcp/dllif.py index b203534..fa0f05c 100644 --- a/pyxcp/dllif.py +++ b/pyxcp/dllif.py @@ -23,7 +23,7 @@ class SeedNKeyError(Exception): """""" -LOADER = Path(sys.modules["pyxcp"].__file__).parent / "asamkeydll" # Absolute path to DLL loader. +LOADER = Path(str(sys.modules["pyxcp"].__file__)).parent / "asamkeydll" # Absolute path to DLL loader. bwidth, _ = platform.architecture() @@ -36,15 +36,15 @@ class SeedNKeyError(Exception): raise RuntimeError(f"Platform {sys.platform!r} currently not supported.") -def getKey(logger, dllName: str, privilege: int, seed: str, assume_same_bit_width: bool): +def getKey(logger, dllName: str, privilege: int, seed: bytes, assume_same_bit_width: bool): dllName = str(Path(dllName).absolute()) # Fix loader issues. - use_ctypes = False + use_ctypes: bool = False if assume_same_bit_width: use_ctypes = True if use_ctypes: try: - lib = ctypes.cdll.LoadLibrary(dllName) + lib: ctypes.CDLL = ctypes.cdll.LoadLibrary(dllName) except OSError: logger.error(f"Could not load DLL {dllName!r} -- Probably an 64bit vs 32bit issue?") return (SeedNKeyResult.ERR_COULD_NOT_LOAD_DLL, None) @@ -57,9 +57,9 @@ def getKey(logger, dllName: str, privilege: int, seed: str, assume_same_bit_widt ctypes.POINTER(ctypes.c_uint8), ctypes.c_char_p, ] - key_buffer = ctypes.create_string_buffer(b"\000" * 128) - key_length = ctypes.c_uint8(128) - ret_code = func( + key_buffer: ctypes.Array[ctypes.c_char] = ctypes.create_string_buffer(b"\000" * 128) + key_length: ctypes.c_uint8 = ctypes.c_uint8(128) + ret_code: int = func( privilege, len(seed), ctypes.c_char_p(seed), @@ -80,8 +80,10 @@ def getKey(logger, dllName: str, privilege: int, seed: str, assume_same_bit_widt except OSError as exc: logger.error(f"Cannot execute {LOADER!r} -- {exc}") return (SeedNKeyResult.ERR_COULD_NOT_LOAD_DLL, None) - key = p0.stdout.read() - p0.stdout.close() + key: bytes = bytes(b"") + if p0.stdout: + key = p0.stdout.read() + p0.stdout.close() p0.kill() p0.wait() if not key: diff --git a/pyxcp/examples/xcp_user_supplied_driver.py b/pyxcp/examples/xcp_user_supplied_driver.py index d81e637..c497bcd 100644 --- a/pyxcp/examples/xcp_user_supplied_driver.py +++ b/pyxcp/examples/xcp_user_supplied_driver.py @@ -30,7 +30,7 @@ def connect(self): def close(self): pass - def getTimestampResolution(self): + def get_timestamp_resolution(self): pass def read(self): diff --git a/pyxcp/examples/xcphello.py b/pyxcp/examples/xcphello.py index 67b3c88..932a769 100644 --- a/pyxcp/examples/xcphello.py +++ b/pyxcp/examples/xcphello.py @@ -1,7 +1,6 @@ #!/usr/bin/env python """Very basic hello-world example. """ -import json from pprint import pprint from pyxcp.cmdline import ArgumentParser @@ -44,8 +43,6 @@ def callout(master, args): print(f" {k:6s}: {v}") daq = x.getDaqInfo() pprint(daq) - with open("daq.json", "wt") as of: - json.dump(daq, of) if daq_info: dqp = x.getDaqProcessorInfo() print("\nDAQ Processor Info:") diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 1267a20..0c2a6c9 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -26,7 +26,7 @@ ) from pyxcp.daq_stim.stim import DaqEventInfo, Stim from pyxcp.master.errorhandler import SystemExit, disable_error_handling, wrapped -from pyxcp.transport.base import createTransport +from pyxcp.transport.base import create_transport from pyxcp.utils import SHORT_SLEEP, decode_bytes, delay @@ -64,7 +64,7 @@ class Master: config: dict """ - def __init__(self, transport_name: str, config, policy=None): + def __init__(self, transport_name: str, config, policy=None, transport_layer_interface=None): self.ctr = 0 self.succeeded = True self.config = config.general @@ -73,7 +73,7 @@ def __init__(self, transport_name: str, config, policy=None): disable_error_handling(self.config.disable_error_handling) self.transport_name = transport_name.lower() transport_config = config.transport - self.transport = createTransport(transport_name, transport_config, policy) + self.transport = create_transport(transport_name, transport_config, policy, transport_layer_interface) self.stim = Stim(self.config.stim_support) self.stim.clear() @@ -1794,7 +1794,7 @@ def cond_unlock(self, resources=None): protection_status = self.getCurrentProtectionStatus() if any(protection_status.values()) and (not (self.seed_n_key_dll or self.seed_n_key_function)): - raise RuntimeError("Neither seed and key DLL nor function specified, cannot proceed.") # TODO: ConfigurationError + raise RuntimeError("Neither seed-and-key DLL nor function specified, cannot proceed.") # TODO: ConfigurationError if resources is None: result = [] if self.slaveProperties["supportsCalpag"]: diff --git a/pyxcp/py.typed b/pyxcp/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyxcp/tests/test_can.py b/pyxcp/tests/test_can.py index f7d90f5..f55c35f 100644 --- a/pyxcp/tests/test_can.py +++ b/pyxcp/tests/test_can.py @@ -6,109 +6,109 @@ MAX_29_BIT_IDENTIFIER, Identifier, IdentifierOutOfRangeError, - calculateFilter, - isExtendedIdentifier, - padFrame, - setDLC, + calculate_filter, + is_extended_identifier, + pad_frame, + set_DLC, stripIdentifier, ) def testSet0(): - assert setDLC(0) == 0 + assert set_DLC(0) == 0 def testSet4(): - assert setDLC(4) == 4 + assert set_DLC(4) == 4 def testSet8(): - assert setDLC(8) == 8 + assert set_DLC(8) == 8 def testSet9(): - assert setDLC(9) == 12 + assert set_DLC(9) == 12 def testSet12(): - assert setDLC(12) == 12 + assert set_DLC(12) == 12 def testSet13(): - assert setDLC(13) == 16 + assert set_DLC(13) == 16 def testSet16(): - assert setDLC(16) == 16 + assert set_DLC(16) == 16 def testSet17(): - assert setDLC(17) == 20 + assert set_DLC(17) == 20 def testSet20(): - assert setDLC(20) == 20 + assert set_DLC(20) == 20 def testSet23(): - assert setDLC(23) == 24 + assert set_DLC(23) == 24 def testSet24(): - assert setDLC(24) == 24 + assert set_DLC(24) == 24 def testSet25(): - assert setDLC(25) == 32 + assert set_DLC(25) == 32 def testSet32(): - assert setDLC(32) == 32 + assert set_DLC(32) == 32 def testSet33(): - assert setDLC(33) == 48 + assert set_DLC(33) == 48 def testSet48(): - assert setDLC(48) == 48 + assert set_DLC(48) == 48 def testSet49(): - assert setDLC(49) == 64 + assert set_DLC(49) == 64 def testSet64(): - assert setDLC(64) == 64 + assert set_DLC(64) == 64 def testSet128(): with pytest.raises(ValueError): - setDLC(128) + set_DLC(128) def testNegative(): with pytest.raises(ValueError): - setDLC(-1) + set_DLC(-1) def testfilter1(): - assert calculateFilter([0x101, 0x102, 0x103]) == (0x100, 0x7FC) + assert calculate_filter([0x101, 0x102, 0x103]) == (0x100, 0x7FC) def testfilter2(): - assert calculateFilter([0x101, 0x102 | CAN_EXTENDED_ID, 0x103]) == ( + assert calculate_filter([0x101, 0x102 | CAN_EXTENDED_ID, 0x103]) == ( 0x100, 0x1FFFFFFC, ) def testfilter3(): - assert calculateFilter([0x1567 | CAN_EXTENDED_ID]) == (0x1567, 0x1FFFFFFF) + assert calculate_filter([0x1567 | CAN_EXTENDED_ID]) == (0x1567, 0x1FFFFFFF) def testfilter4(): - assert calculateFilter( + assert calculate_filter( [ 0x1560 | CAN_EXTENDED_ID, 0x1561, @@ -131,18 +131,18 @@ def testfilter4(): def testfilter5(): - assert calculateFilter([0x1560 | CAN_EXTENDED_ID, 0x1561, 0x1562, 0x1563, 0x1564, 0x1565, 0x1566, 0x1567]) == ( + assert calculate_filter([0x1560 | CAN_EXTENDED_ID, 0x1561, 0x1562, 0x1563, 0x1564, 0x1565, 0x1566, 0x1567]) == ( 0x1560, 0x1FFFFFF8, ) def testIsExtendedIdentifier1(): - assert isExtendedIdentifier(0x280) is False + assert is_extended_identifier(0x280) is False def testIsExtendedIdentifier2(): - assert isExtendedIdentifier(0x280 | CAN_EXTENDED_ID) is True + assert is_extended_identifier(0x280 | CAN_EXTENDED_ID) is True def testStripIdentifier1(): @@ -247,145 +247,145 @@ def test_filter_for_identifier_extended(capsys): def test_pad_frame_no_padding_1(): frame = bytearray(b"\xaa") padded_frame = bytearray(b"\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_2(): frame = bytearray(b"\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_3(): frame = bytearray(b"\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_4(): frame = bytearray(b"\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_5(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_6(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_7(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_8(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_9(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_10(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_11(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_12(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_13(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_14(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_15(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_16(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_17(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_18(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_19(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_20(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_21(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_22(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_23(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_24(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_25(): @@ -393,7 +393,7 @@ def test_pad_frame_no_padding_25(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_26(): @@ -401,7 +401,7 @@ def test_pad_frame_no_padding_26(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_27(): @@ -411,7 +411,7 @@ def test_pad_frame_no_padding_27(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_28(): @@ -421,7 +421,7 @@ def test_pad_frame_no_padding_28(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_29(): @@ -431,7 +431,7 @@ def test_pad_frame_no_padding_29(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_30(): @@ -441,7 +441,7 @@ def test_pad_frame_no_padding_30(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_31(): @@ -451,7 +451,7 @@ def test_pad_frame_no_padding_31(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_32(): @@ -461,7 +461,7 @@ def test_pad_frame_no_padding_32(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_33(): @@ -471,7 +471,7 @@ def test_pad_frame_no_padding_33(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_34(): @@ -481,7 +481,7 @@ def test_pad_frame_no_padding_34(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_35(): @@ -491,7 +491,7 @@ def test_pad_frame_no_padding_35(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_36(): @@ -501,7 +501,7 @@ def test_pad_frame_no_padding_36(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_37(): @@ -511,7 +511,7 @@ def test_pad_frame_no_padding_37(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_38(): @@ -521,7 +521,7 @@ def test_pad_frame_no_padding_38(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_39(): @@ -531,7 +531,7 @@ def test_pad_frame_no_padding_39(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_40(): @@ -541,7 +541,7 @@ def test_pad_frame_no_padding_40(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_41(): @@ -551,7 +551,7 @@ def test_pad_frame_no_padding_41(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_42(): @@ -561,7 +561,7 @@ def test_pad_frame_no_padding_42(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_43(): @@ -571,7 +571,7 @@ def test_pad_frame_no_padding_43(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_44(): @@ -581,7 +581,7 @@ def test_pad_frame_no_padding_44(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_45(): @@ -591,7 +591,7 @@ def test_pad_frame_no_padding_45(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_46(): @@ -601,7 +601,7 @@ def test_pad_frame_no_padding_46(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_47(): @@ -611,7 +611,7 @@ def test_pad_frame_no_padding_47(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_48(): @@ -621,7 +621,7 @@ def test_pad_frame_no_padding_48(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_49(): @@ -631,7 +631,7 @@ def test_pad_frame_no_padding_49(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_50(): @@ -641,7 +641,7 @@ def test_pad_frame_no_padding_50(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_51(): @@ -651,7 +651,7 @@ def test_pad_frame_no_padding_51(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_52(): @@ -661,7 +661,7 @@ def test_pad_frame_no_padding_52(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_53(): @@ -671,7 +671,7 @@ def test_pad_frame_no_padding_53(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_54(): @@ -681,7 +681,7 @@ def test_pad_frame_no_padding_54(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_55(): @@ -691,7 +691,7 @@ def test_pad_frame_no_padding_55(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_56(): @@ -701,7 +701,7 @@ def test_pad_frame_no_padding_56(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_57(): @@ -711,7 +711,7 @@ def test_pad_frame_no_padding_57(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_58(): @@ -721,7 +721,7 @@ def test_pad_frame_no_padding_58(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_59(): @@ -731,7 +731,7 @@ def test_pad_frame_no_padding_59(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_60(): @@ -741,7 +741,7 @@ def test_pad_frame_no_padding_60(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_61(): @@ -751,7 +751,7 @@ def test_pad_frame_no_padding_61(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_62(): @@ -761,7 +761,7 @@ def test_pad_frame_no_padding_62(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_63(): @@ -771,7 +771,7 @@ def test_pad_frame_no_padding_63(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_no_padding_64(): @@ -781,151 +781,151 @@ def test_pad_frame_no_padding_64(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" ) - assert padFrame(frame, False, 0x00) == padded_frame + assert pad_frame(frame, False, 0x00) == padded_frame def test_pad_frame_padded_1(): frame = bytearray(b"\xaa") padded_frame = bytearray(b"\xaa\x00\x00\x00\x00\x00\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_2(): frame = bytearray(b"\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\x00\x00\x00\x00\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_3(): frame = bytearray(b"\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\x00\x00\x00\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_4(): frame = bytearray(b"\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\x00\x00\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_5(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_6(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_7(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_8(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_9(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_10(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_11(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_12(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_13(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_14(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_15(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_16(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_17(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_18(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_19(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_20(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_21(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_22(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_23(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_24(): frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") padded_frame = bytearray(b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa") - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_25(): @@ -933,7 +933,7 @@ def test_pad_frame_padded_25(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_26(): @@ -941,7 +941,7 @@ def test_pad_frame_padded_26(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_27(): @@ -951,7 +951,7 @@ def test_pad_frame_padded_27(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_28(): @@ -961,7 +961,7 @@ def test_pad_frame_padded_28(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_29(): @@ -971,7 +971,7 @@ def test_pad_frame_padded_29(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_30(): @@ -981,7 +981,7 @@ def test_pad_frame_padded_30(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_31(): @@ -991,7 +991,7 @@ def test_pad_frame_padded_31(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_32(): @@ -1001,7 +1001,7 @@ def test_pad_frame_padded_32(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_33(): @@ -1011,7 +1011,7 @@ def test_pad_frame_padded_33(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_34(): @@ -1021,7 +1021,7 @@ def test_pad_frame_padded_34(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_35(): @@ -1031,7 +1031,7 @@ def test_pad_frame_padded_35(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_36(): @@ -1041,7 +1041,7 @@ def test_pad_frame_padded_36(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_37(): @@ -1051,7 +1051,7 @@ def test_pad_frame_padded_37(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_38(): @@ -1061,7 +1061,7 @@ def test_pad_frame_padded_38(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_39(): @@ -1071,7 +1071,7 @@ def test_pad_frame_padded_39(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_40(): @@ -1081,7 +1081,7 @@ def test_pad_frame_padded_40(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_41(): @@ -1091,7 +1091,7 @@ def test_pad_frame_padded_41(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_42(): @@ -1101,7 +1101,7 @@ def test_pad_frame_padded_42(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_43(): @@ -1111,7 +1111,7 @@ def test_pad_frame_padded_43(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_44(): @@ -1121,7 +1121,7 @@ def test_pad_frame_padded_44(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_45(): @@ -1131,7 +1131,7 @@ def test_pad_frame_padded_45(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_46(): @@ -1141,7 +1141,7 @@ def test_pad_frame_padded_46(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_47(): @@ -1151,7 +1151,7 @@ def test_pad_frame_padded_47(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_48(): @@ -1161,7 +1161,7 @@ def test_pad_frame_padded_48(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_49(): @@ -1171,7 +1171,7 @@ def test_pad_frame_padded_49(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_50(): @@ -1181,7 +1181,7 @@ def test_pad_frame_padded_50(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_51(): @@ -1191,7 +1191,7 @@ def test_pad_frame_padded_51(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_52(): @@ -1201,7 +1201,7 @@ def test_pad_frame_padded_52(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_53(): @@ -1211,7 +1211,7 @@ def test_pad_frame_padded_53(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_54(): @@ -1221,7 +1221,7 @@ def test_pad_frame_padded_54(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_55(): @@ -1231,7 +1231,7 @@ def test_pad_frame_padded_55(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_56(): @@ -1241,7 +1241,7 @@ def test_pad_frame_padded_56(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_57(): @@ -1251,7 +1251,7 @@ def test_pad_frame_padded_57(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_58(): @@ -1261,7 +1261,7 @@ def test_pad_frame_padded_58(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_59(): @@ -1271,7 +1271,7 @@ def test_pad_frame_padded_59(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_60(): @@ -1281,7 +1281,7 @@ def test_pad_frame_padded_60(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_61(): @@ -1291,7 +1291,7 @@ def test_pad_frame_padded_61(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_62(): @@ -1301,7 +1301,7 @@ def test_pad_frame_padded_62(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_63(): @@ -1311,7 +1311,7 @@ def test_pad_frame_padded_63(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame def test_pad_frame_padded_64(): @@ -1321,4 +1321,4 @@ def test_pad_frame_padded_64(): padded_frame = bytearray( b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" ) - assert padFrame(frame, True, 0x00) == padded_frame + assert pad_frame(frame, True, 0x00) == padded_frame diff --git a/pyxcp/tests/test_config.py b/pyxcp/tests/test_config.py deleted file mode 100644 index 59421aa..0000000 --- a/pyxcp/tests/test_config.py +++ /dev/null @@ -1,64 +0,0 @@ -from collections import OrderedDict -from io import StringIO - -from pyxcp.config import readConfiguration - - -JSON = """{ - "PORT": "COM10", - "BITRATE": 38400, - "BYTESIZE": 8, - "PARITY": "N", - "STOPBITS": 1, - "CREATE_DAQ_TIMESTAMPS": false -}""" - -TOML = """PORT = "COM10" -BITRATE = 38400 -PARITY = "N" -BYTESIZE = 8 -STOPBITS = 1 -CREATE_DAQ_TIMESTAMPS = false -""" - -CONF_JSON = StringIO(JSON) -CONF_JSON.name = "hello.json" - -CONF_TOML = StringIO(TOML) -CONF_TOML.name = "hello.toml" - - -def test_read_empty_config(): - assert readConfiguration(None) == {} - assert readConfiguration({}) == {} - - -def test_read_json_config(): - assert readConfiguration(CONF_JSON) == { - "BITRATE": 38400, - "BYTESIZE": 8, - "CREATE_DAQ_TIMESTAMPS": False, - "PARITY": "N", - "PORT": "COM10", - "STOPBITS": 1, - } - - -def test_read_toml_config(): - assert readConfiguration(CONF_TOML) == { - "BITRATE": 38400, - "BYTESIZE": 8, - "CREATE_DAQ_TIMESTAMPS": False, - "PARITY": "N", - "PORT": "COM10", - "STOPBITS": 1, - } - - -def test_read_dict(): - assert readConfiguration({"A": 1, "B": 2, "C": 3}) == {"A": 1, "B": 2, "C": 3} - assert readConfiguration(OrderedDict({"A": 1, "B": 2, "C": 3})) == { - "A": 1, - "B": 2, - "C": 3, - } diff --git a/pyxcp/tests/test_frame_padding.py b/pyxcp/tests/test_frame_padding.py index 00f09f1..f92dae6 100644 --- a/pyxcp/tests/test_frame_padding.py +++ b/pyxcp/tests/test_frame_padding.py @@ -1,4 +1,4 @@ -from pyxcp.transport.can import padFrame +from pyxcp.transport.can import pad_frame def test_frame_padding_no_padding_length(): @@ -71,7 +71,7 @@ def test_frame_padding_no_padding_length(): ) for frame_len in range(65): frame = bytes(frame_len) - padded_frame = padFrame(frame, padding_value=0, padding_len=0) + padded_frame = pad_frame(frame, padding_value=0, padding_len=0) frame_len = len(padded_frame) _, expected_len = EXPECTED[frame_len] assert frame_len == expected_len @@ -147,7 +147,7 @@ def test_frame_padding_padding_length32(): ) for frame_len in range(65): frame = bytes(frame_len) - padded_frame = padFrame(frame, padding_value=0, padding_len=32) + padded_frame = pad_frame(frame, padding_value=0, padding_len=32) frame_len = len(padded_frame) _, expected_len = EXPECTED[frame_len] assert frame_len == expected_len diff --git a/pyxcp/tests/test_master.py b/pyxcp/tests/test_master.py index c951245..d1659e1 100644 --- a/pyxcp/tests/test_master.py +++ b/pyxcp/tests/test_master.py @@ -6,7 +6,19 @@ from pyxcp import types from pyxcp.master import Master -from pyxcp.transport.can import CanInterfaceBase + + +def create_config(): + # Exception: XCPonEth - Failed to resolve address : + config = mock.MagicMock() + config.general.return_value = mock.MagicMock() + config.transport.return_value = mock.MagicMock() + config.transport.eth.return_value = mock.MagicMock() + config.transport.eth.host = "localhost" + config.transport.eth.port = 5555 + config.transport.eth.bind_to_address = "" + config.transport.eth.bind_to_port = 0 + return config class MockSocket: @@ -48,7 +60,7 @@ def connect(self): pass -class MockCanInterface(CanInterfaceBase): +class MockCanInterface: # CanInterfaceBase def __init__(self): self.data = deque() self.receive_callback = None @@ -87,7 +99,7 @@ def connect(self): def read(self): pass - def getTimestampResolution(self): + def get_timestamp_resolution(self): pass @@ -97,7 +109,7 @@ class TestMaster: @mock.patch("pyxcp.transport.eth") def testConnect(self, eth): - with Master("eth") as xm: + with Master("eth", config=create_config()) as xm: xm.transport = eth() xm.transport.request.return_value = bytes([0x1D, 0xC0, 0xFF, 0xDC, 0x05, 0x01, 0x01]) @@ -120,7 +132,7 @@ def testConnect(self, eth): @mock.patch("pyxcp.transport.eth") def testDisconnect(self, eth): - with Master("eth") as xm: + with Master("eth", config=create_config()) as xm: xm.transport = eth() xm.transport.request.return_value = bytes([]) res = xm.disconnect() @@ -128,7 +140,7 @@ def testDisconnect(self, eth): @mock.patch("pyxcp.transport.eth") def testGetStatus(self, eth): - with Master("eth") as xm: + with Master("eth", config=create_config()) as xm: xm.transport = eth() xm.transport.request.return_value = bytes([0x1D, 0xC0, 0xFF, 0xDC, 0x05, 0x01, 0x01]) @@ -151,7 +163,7 @@ def testGetStatus(self, eth): @mock.patch("pyxcp.transport.eth") def testSync(self, eth): - with Master("eth") as xm: + with Master("eth", config=create_config()) as xm: xm.transport = eth() xm.transport.request.return_value = bytes([0x00]) res = xm.synch() @@ -159,7 +171,7 @@ def testSync(self, eth): @mock.patch("pyxcp.transport.eth") def testGetCommModeInfo(self, eth): - with Master("eth") as xm: + with Master("eth", config=create_config()) as xm: xm.transport = eth() xm.transport.request.return_value = bytes([0x1D, 0xC0, 0xFF, 0xDC, 0x05, 0x01, 0x01]) @@ -178,7 +190,7 @@ def testGetCommModeInfo(self, eth): @mock.patch("pyxcp.transport.eth") def testGetId(self, eth): - with Master("eth") as xm: + with Master("eth", config=create_config()) as xm: xm.transport = eth() xm.transport.MAX_DATAGRAM_SIZE = 512 xm.transport.request.return_value = bytes([0x1D, 0xC0, 0xFF, 0xDC, 0x05, 0x01, 0x01]) @@ -203,7 +215,7 @@ def testConnect2(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -247,7 +259,7 @@ def testDisconnect2(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -270,7 +282,7 @@ def testGetStatus2(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -302,7 +314,7 @@ def testSynch(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -325,7 +337,7 @@ def testGetCommModeInfo2(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -353,7 +365,7 @@ def testGetId2(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -416,7 +428,7 @@ def testSetRequest(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -439,7 +451,7 @@ def testGetSeed(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -463,7 +475,7 @@ def testUnlock(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -489,7 +501,7 @@ def testSetMta(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -529,7 +541,7 @@ def testUpload(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -568,7 +580,7 @@ def testShortUpload(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -608,7 +620,7 @@ def testBuildChecksum(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -649,7 +661,7 @@ def testTransportLayerCmd(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -673,7 +685,7 @@ def testUserCmd(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -697,7 +709,7 @@ def testGetVersion(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() @@ -723,7 +735,7 @@ def testDownload(self, mock_selector, mock_socket): mock_socket.return_value.recv.side_effect = ms.recv mock_selector.return_value.select.side_effect = ms.select - with Master("eth", config={"HOST": "localhost", "LOGLEVEL": "DEBUG"}) as xm: + with Master("eth", config=create_config()) as xm: ms.push_packet(self.DefaultConnectResponse) res = xm.connect() diff --git a/pyxcp/tests/test_transport.py b/pyxcp/tests/test_transport.py index cad6b6d..82d1617 100644 --- a/pyxcp/tests/test_transport.py +++ b/pyxcp/tests/test_transport.py @@ -4,10 +4,10 @@ def test_factory_works(): - assert isinstance(tr.createTransport("eth"), tr.BaseTransport) - assert isinstance(tr.createTransport("sxi"), tr.BaseTransport) + assert isinstance(tr.create_transport("eth"), tr.BaseTransport) + assert isinstance(tr.create_transport("sxi"), tr.BaseTransport) assert isinstance( - tr.createTransport( + tr.create_transport( "can", config={ "CAN_ID_MASTER": 1, @@ -20,10 +20,10 @@ def test_factory_works(): def test_factory_works_case_insensitive(): - assert isinstance(tr.createTransport("ETH"), tr.BaseTransport) - assert isinstance(tr.createTransport("SXI"), tr.BaseTransport) + assert isinstance(tr.create_transport("ETH"), tr.BaseTransport) + assert isinstance(tr.create_transport("SXI"), tr.BaseTransport) assert isinstance( - tr.createTransport( + tr.create_transport( "CAN", config={ "CAN_ID_MASTER": 1, @@ -37,11 +37,11 @@ def test_factory_works_case_insensitive(): def test_factory_invalid_transport_name_raises(): with pytest.raises(ValueError): - tr.createTransport("xCp") + tr.create_transport("xCp") def test_transport_names(): - transports = tr.availableTransports() + transports = tr.available_transports() assert "can" in transports assert "eth" in transports @@ -49,7 +49,7 @@ def test_transport_names(): def test_transport_names_are_lower_case_only(): - transports = tr.availableTransports() + transports = tr.available_transports() assert "CAN" not in transports assert "ETH" not in transports @@ -57,7 +57,7 @@ def test_transport_names_are_lower_case_only(): def test_transport_classes(): - transports = tr.availableTransports() + transports = tr.available_transports() assert issubclass(transports.get("can"), tr.BaseTransport) assert issubclass(transports.get("eth"), tr.BaseTransport) diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index eb2e9f4..8e09b6a 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -1,11 +1,11 @@ #!/usr/bin/env python import abc import threading -import typing from collections import deque # from datetime import datetime from time import sleep +from typing import Any, Dict, Optional, Set, Type import pyxcp.types as types @@ -35,11 +35,11 @@ class FrameAcquisitionPolicy: ==> care only about DAQ frames. """ - def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None): + def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None): self._frame_types_to_filter_out = filter_out or set() @property - def filtered_out(self) -> typing.Set[types.FrameCategory]: + def filtered_out(self) -> Set[types.FrameCategory]: return self._frame_types_to_filter_out def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: ... # noqa: E704 @@ -63,7 +63,7 @@ class LegacyFrameAcquisitionPolicy(FrameAcquisitionPolicy): Deprecated: Use only for compatibility reasons. """ - def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None): + def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None): super().__init__(filter_out) self.reqQueue = deque() self.resQueue = deque() @@ -87,7 +87,9 @@ def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: # print(f"{frame_type.name:8} {counter:6} {timestamp:7.7f} {hexDump(payload)}") if frame_type not in self.filtered_out: - self.QUEUE_MAP.get(frame_type).append((counter, timestamp, payload)) + queue = self.QUEUE_MAP.get(frame_type) + if queue: + queue.append((counter, timestamp, payload)) class FrameRecorderPolicy(FrameAcquisitionPolicy): @@ -96,7 +98,7 @@ class FrameRecorderPolicy(FrameAcquisitionPolicy): def __init__( self, file_name: str, - filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None, + filter_out: Optional[Set[types.FrameCategory]] = None, prealloc: int = 10, chunk_size: int = 1, ): @@ -114,7 +116,7 @@ def finalize(self) -> None: class StdoutPolicy(FrameAcquisitionPolicy): """Frame acquisition policy that prints frames to stdout.""" - def __init__(self, filter_out: typing.Optional[typing.Set[types.FrameCategory]] = None): + def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None): super().__init__(filter_out) def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: @@ -138,59 +140,59 @@ class BaseTransport(metaclass=abc.ABCMeta): """ - def __init__(self, config, policy: FrameAcquisitionPolicy = None): + def __init__(self, config, policy: Optional[FrameAcquisitionPolicy] = None, transport_layer_interface: Optional[Any] = None): + self.has_user_supplied_interface: bool = transport_layer_interface is not None + self.transport_layer_interface: Optional[Any] = transport_layer_interface self.parent = None - self.policy = policy or LegacyFrameAcquisitionPolicy() - self.closeEvent = threading.Event() - - self.command_lock = threading.Lock() - self.policy_lock = threading.Lock() - - self.logger = config.log - self._debug = self.logger.level == 10 - - self.counterSend: int = 0 - self.counterReceived: int = -1 - self.create_daq_timestamps = config.create_daq_timestamps + self.policy: FrameAcquisitionPolicy = policy or LegacyFrameAcquisitionPolicy() + self.closeEvent: threading.Event = threading.Event() + + self.command_lock: threading.Lock = threading.Lock() + self.policy_lock: threading.Lock = threading.Lock() + + self.logger: Any = config.log + self._debug: bool = self.logger.level == 10 + if transport_layer_interface: + self.logger.info(f"User supplied transport layer interface '{transport_layer_interface!s}'.") + self.counter_send: int = 0 + self.counter_received: int = -1 + self.create_daq_timestamps: bool = config.create_daq_timestamps timestamp_mode = TimestampType.ABSOLUTE_TS if config.timestamp_mode == "ABSOLUTE" else TimestampType.RELATIVE_TS self.timestamp = Timestamp(timestamp_mode) - # Reference point for timestamping (may relative). - self._start_datetime = CurrentDatetime(self.timestamp.initial_value) - self.alignment = config.alignment - self.timeout = seconds_to_nanoseconds(config.timeout) - self.timer_restart_event = threading.Event() - self.timing = Timing() - self.resQueue = deque() - self.listener = threading.Thread( + self._start_datetime: CurrentDatetime = CurrentDatetime(self.timestamp.initial_value) + self.alignment: int = config.alignment + self.timeout: int = seconds_to_nanoseconds(config.timeout) + self.timer_restart_event: threading.Event = threading.Event() + self.timing: Timing = Timing() + self.resQueue: deque = deque() + self.listener: threading.Thread = threading.Thread( target=self.listen, args=(), kwargs={}, ) - self.first_daq_timestamp = None - + self.first_daq_timestamp: Optional[int] = None # self.timestamp_origin = self.timestamp.value # self.datetime_origin = datetime.fromtimestamp(self.timestamp_origin) - - self.pre_send_timestamp = self.timestamp.value - self.post_send_timestamp = self.timestamp.value - self.recv_timestamp = self.timestamp.value + self.pre_send_timestamp: int = self.timestamp.value + self.post_send_timestamp: int = self.timestamp.value + self.recv_timestamp: int = self.timestamp.value def __del__(self): - self.finishListener() - self.closeConnection() + self.finish_listener() + self.close_connection() def load_config(self, config): """Load configuration data.""" - class_name = self.__class__.__name__.lower() - self.config = getattr(config, class_name) + class_name: str = self.__class__.__name__.lower() + self.config: Any = getattr(config, class_name) def close(self): """Close the transport-layer connection and event-loop.""" - self.finishListener() + self.finish_listener() if self.listener.is_alive(): self.listener.join() - self.closeConnection() + self.close_connection() @abc.abstractmethod def connect(self): @@ -198,10 +200,10 @@ def connect(self): def get(self): """Get an item from a deque considering a timeout condition.""" - start = self.timestamp.value + start: int = self.timestamp.value while not self.resQueue: if self.timer_restart_event.is_set(): - start = self.timestamp.value + start: int = self.timestamp.value self.timer_restart_event.restart_event.clear() if self.timestamp.value - start > self.timeout: raise EmptyFrameError @@ -215,15 +217,15 @@ def start_datetime(self) -> int: """""" return self._start_datetime - def startListener(self): + def start_listener(self): if self.listener.is_alive(): - self.finishListener() + self.finish_listener() self.listener.join() self.listener = threading.Thread(target=self.listen) self.listener.start() - def finishListener(self): + def finish_listener(self): if hasattr(self, "closeEvent"): self.closeEvent.set() @@ -232,7 +234,7 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): frame = self._prepare_request(cmd, *data) self.timing.start() with self.policy_lock: - self.policy.feed(types.FrameCategory.CMD, self.counterSend, self.timestamp.value, frame) + self.policy.feed(types.FrameCategory.CMD, self.counter_send, self.timestamp.value, frame) self.send(frame) try: xcpPDU = self.get() @@ -240,7 +242,7 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): if not ignore_timeout: MSG = f"Response timed out (timeout={self.timeout}s)" with self.policy_lock: - self.policy.feed(types.FrameCategory.METADATA, self.counterSend, self.timestamp.value, bytes(MSG, "ascii")) + self.policy.feed(types.FrameCategory.METADATA, self.counter_send, self.timestamp.value, bytes(MSG, "ascii")) raise types.XcpTimeoutError(MSG) from None else: self.timing.stop() @@ -249,7 +251,7 @@ def _request_internal(self, cmd, ignore_timeout=False, *data): pid = types.Response.parse(xcpPDU).type if pid == "ERR" and cmd.name != "SYNCH": with self.policy_lock: - self.policy.feed(types.FrameCategory.ERROR, self.counterReceived, self.timestamp.value, xcpPDU[1:]) + self.policy.feed(types.FrameCategory.ERROR, self.counter_received, self.timestamp.value, xcpPDU[1:]) err = types.XcpError.parse(xcpPDU[1:]) raise types.XcpResponseError(err) return xcpPDU[1:] @@ -282,7 +284,7 @@ def block_request(self, cmd, *data): with self.policy_lock: self.policy.feed( types.FrameCategory.CMD if int(cmd) >= 0xC0 else types.FrameCategory.STIM, - self.counterSend, + self.counter_send, self.timestamp.value, frame, ) @@ -296,11 +298,11 @@ def _prepare_request(self, cmd, *data): self.logger.debug(cmd.name) self.parent._setService(cmd) - cmdlen = cmd.bit_length() // 8 # calculate bytes needed for cmd - packet = bytes(flatten(cmd.to_bytes(cmdlen, "big"), data)) + cmd_len = cmd.bit_length() // 8 # calculate bytes needed for cmd + packet = bytes(flatten(cmd.to_bytes(cmd_len, "big"), data)) - header = self.HEADER.pack(len(packet), self.counterSend) - self.counterSend = (self.counterSend + 1) & 0xFFFF + header = self.HEADER.pack(len(packet), self.counter_send) + self.counter_send = (self.counter_send + 1) & 0xFFFF frame = header + packet @@ -348,7 +350,7 @@ def send(self, frame): pass @abc.abstractmethod - def closeConnection(self): + def close_connection(self): """Does the actual connection shutdown. Needs to be implemented by any sub-class. """ @@ -365,13 +367,13 @@ def process_event_packet(self, packet): if ev_type == types.Event.EV_CMD_PENDING: self.timer_restart_event.set() - def processResponse(self, response, length, counter, recv_timestamp=None): - if counter == self.counterReceived: + def process_response(self, response: bytes, length: int, counter: int, recv_timestamp: int) -> None: + if counter == self.counter_received: self.logger.warning(f"Duplicate message counter {counter} received from the XCP slave") if self._debug: self.logger.debug(f"<- L{length} C{counter} {hexDump(response[:512])}") return - self.counterReceived = counter + self.counter_received = counter pid = response[0] if pid >= 0xFC: if self._debug: @@ -379,15 +381,15 @@ def processResponse(self, response, length, counter, recv_timestamp=None): if pid >= 0xFE: self.resQueue.append(response) with self.policy_lock: - self.policy.feed(types.FrameCategory.RESPONSE, self.counterReceived, self.timestamp.value, response) + self.policy.feed(types.FrameCategory.RESPONSE, self.counter_received, self.timestamp.value, response) self.recv_timestamp = recv_timestamp elif pid == 0xFD: self.process_event_packet(response) with self.policy_lock: - self.policy.feed(types.FrameCategory.EVENT, self.counterReceived, self.timestamp.value, response) + self.policy.feed(types.FrameCategory.EVENT, self.counter_received, self.timestamp.value, response) elif pid == 0xFC: with self.policy_lock: - self.policy.feed(types.FrameCategory.SERV, self.counterReceived, self.timestamp.value, response) + self.policy.feed(types.FrameCategory.SERV, self.counter_received, self.timestamp.value, response) else: if self._debug: self.logger.debug(f"<- L{length} C{counter} ODT_Data[0:8] {hexDump(response[:8])}") @@ -398,10 +400,19 @@ def processResponse(self, response, length, counter, recv_timestamp=None): else: timestamp = 0.0 with self.policy_lock: - self.policy.feed(types.FrameCategory.DAQ, self.counterReceived, timestamp, response) + self.policy.feed(types.FrameCategory.DAQ, self.counter_received, timestamp, response) + + # @abc.abstractproperty + # @property + # def transport_layer_interface(self) -> Any: + # pass + + # @transport_layer_interface.setter + # def transport_layer_interface(self, value: Any) -> None: + # self._transport_layer_interface = value -def createTransport(name, *args, **kws): +def create_transport(name: str, *args, **kws) -> BaseTransport: """Factory function for transports. Returns @@ -409,15 +420,15 @@ def createTransport(name, *args, **kws): :class:`BaseTransport` derived instance. """ name = name.lower() - transports = availableTransports() + transports = available_transports() if name in transports: - transportClass = transports[name] + transport_class: Type[BaseTransport] = transports[name] else: raise ValueError(f"{name!r} is an invalid transport -- please choose one of [{' | '.join(transports.keys())}].") - return transportClass(*args, **kws) + return transport_class(*args, **kws) -def availableTransports(): +def available_transports() -> Dict[str, Type[BaseTransport]]: """List all subclasses of :class:`BaseTransport`. Returns diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index 3322058..ef6ea7e 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -5,10 +5,10 @@ import functools import operator from bisect import bisect_left -from typing import Any, Dict, Optional +from typing import Any, Dict, Optional, Type from can import CanError, CanInitializationError, Message, detect_available_configs -from can.interface import _get_class_for_interface +from can.interface import BusABC, _get_class_for_interface from rich.console import Console from pyxcp.config import CAN_INTERFACE_MAP @@ -32,7 +32,7 @@ class IdentifierOutOfRangeError(Exception): pass -def isExtendedIdentifier(identifier: int) -> bool: +def is_extended_identifier(identifier: int) -> bool: """Check for extendend CAN identifier. Parameters @@ -80,7 +80,7 @@ def samplePointToTsegs(tqs: int, samplePoint: float) -> tuple: return (tseg1, tseg2) -def padFrame(frame: bytes, pad_frame: bool, padding_value: int) -> bytes: +def pad_frame(frame: bytes, pad_frame: bool, padding_value: int) -> bytes: """Pad frame to next discrete DLC value (CAN-FD) or on request (CAN-Classic). References: @@ -117,7 +117,7 @@ class Identifier: def __init__(self, raw_id: int): self._raw_id = raw_id self._id = stripIdentifier(raw_id) - self._is_extended = isExtendedIdentifier(raw_id) + self._is_extended = is_extended_identifier(raw_id) if self._is_extended: if self._id > MAX_29_BIT_IDENTIFIER: raise IdentifierOutOfRangeError(f"29-bit identifier {self._id!r} is out of range") @@ -229,19 +229,19 @@ def __repr__(self): class PythonCanWrapper: """Wrapper around python-can - github.com/hardbyte/python-can""" - def __init__(self, parent, interface_name: str, **parameters): + def __init__(self, parent, interface_name: str, **parameters) -> None: self.parent = parent - self.interface_name = interface_name + self.interface_name: str = interface_name self.parameters = parameters - self.can_interface_class = _get_class_for_interface(self.interface_name) - self.connected = False + self.can_interface_class: Type[BusABC] = _get_class_for_interface(self.interface_name) + self.connected: bool = False def connect(self): if self.connected: return can_filters = [] can_filters.append(self.parent.can_id_slave.create_filter_from_id()) # Primary CAN filter. - self.can_interface = self.can_interface_class(interface=self.interface_name, **self.parameters) + self.can_interface: BusABC = self.can_interface_class(interface=self.interface_name, **self.parameters) if self.parent.daq_identifier: # Add filters for DAQ identifiers. for daq_id in self.parent.daq_identifier: @@ -284,7 +284,7 @@ def read(self) -> Optional[Frame]: timestamp=seconds_to_nanoseconds(frame.timestamp), ) - def getTimestampResolution(self) -> int: + def get_timestamp_resolution(self) -> int: return 10 * 1000 @@ -302,8 +302,8 @@ class Can(BaseTransport): HEADER = EmptyHeader() HEADER_SIZE = 0 - def __init__(self, config, policy=None): - super().__init__(config, policy) + def __init__(self, config, policy=None, transport_layer_interface: Optional[BusABC] = None): + super().__init__(config, policy, transport_layer_interface) self.load_config(config) self.useDefaultListener = self.config.use_default_listener self.can_id_master = Identifier(self.config.can_id_master) @@ -336,8 +336,8 @@ def get_interface_parameters(self) -> Dict[str, Any]: can_interface_config_class = CAN_INTERFACE_MAP[self.interface_name] # Optional base class parameters. - optional = [(p, p.removeprefix("has_")) for p in can_interface_config_class.OPTIONAL_BASE_PARAMS] - for o, n in optional: + optional_parameters = [(p, p.removeprefix("has_")) for p in can_interface_config_class.OPTIONAL_BASE_PARAMS] + for o, n in optional_parameters: opt = getattr(can_interface_config_class, o) value = getattr(self.config, n) if opt: @@ -360,11 +360,11 @@ def get_interface_parameters(self) -> Dict[str, Any]: result[name] = value return result - def dataReceived(self, payload: bytes, recv_timestamp: int = None): - self.processResponse( + def data_received(self, payload: bytes, recv_timestamp: int): + self.process_response( payload, len(payload), - counter=(self.counterReceived + 1) & 0xFFFF, + counter=(self.counter_received + 1) & 0xFFFF, recv_timestamp=recv_timestamp, ) @@ -374,11 +374,11 @@ def listen(self): return frame = self.can_interface.read() if frame: - self.dataReceived(frame.data, frame.timestamp) + self.data_received(frame.data, frame.timestamp) def connect(self): if self.useDefaultListener: - self.startListener() + self.start_listener() try: self.can_interface.connect() except CanInitializationError: @@ -391,19 +391,19 @@ def connect(self): def send(self, frame: bytes) -> None: # send the request self.pre_send_timestamp = self.timestamp.value - self.can_interface.transmit(payload=padFrame(frame, self.max_dlc_required, self.padding_value)) + self.can_interface.transmit(payload=pad_frame(frame, self.max_dlc_required, self.padding_value)) self.post_send_timestamp = self.timestamp.value - def closeConnection(self): + def close_connection(self): if hasattr(self, "can_interface"): self.can_interface.close() def close(self): - self.finishListener() - self.closeConnection() + self.finish_listener() + self.close_connection() -def setDLC(length: int): +def set_DLC(length: int): """Return DLC value according to CAN-FD. :param length: Length value to be mapped to a valid CAN-FD DLC. @@ -422,14 +422,14 @@ def setDLC(length: int): raise ValueError("DLC could be at most 64.") -def calculateFilter(ids: list): +def calculate_filter(ids: list): """ :param ids: An iterable (usually list or tuple) containing CAN identifiers. :return: Calculated filter and mask. :rtype: tuple (int, int) """ - any_extended_ids = any(isExtendedIdentifier(i) for i in ids) + any_extended_ids = any(is_extended_identifier(i) for i in ids) raw_ids = [stripIdentifier(i) for i in ids] cfilter = functools.reduce(operator.and_, raw_ids) cmask = functools.reduce(operator.or_, raw_ids) ^ cfilter diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index f20e8ea..30d5ffe 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -5,6 +5,7 @@ import threading from collections import deque from time import sleep +from typing import Optional from pyxcp.transport.base import BaseTransport from pyxcp.utils import SHORT_SLEEP @@ -38,8 +39,8 @@ class Eth(BaseTransport): HEADER = struct.Struct("= length: response = data[current_position : current_position + length] - processResponse(response, length, counter, timestamp) + process_response(response, length, counter, timestamp) current_size -= length current_position += length @@ -222,7 +223,7 @@ def send(self, frame): self.sock.send(frame) self.post_send_timestamp = self.timestamp.value - def closeConnection(self): + def close_connection(self): if not self.invalidSocket: # Seems to be problematic /w IPv6 # if self.status == 1: diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index f7da41c..91f2c5e 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -3,6 +3,7 @@ import struct from collections import deque from dataclasses import dataclass +from typing import Optional import serial @@ -23,8 +24,8 @@ class HeaderValues: class SxI(BaseTransport): """""" - def __init__(self, config=None, policy=None): - super().__init__(config, policy) + def __init__(self, config=None, policy=None, transport_layer_interface: Optional[serial.Serial] = None): + super().__init__(config, policy, transport_layer_interface) self.load_config(config) self.port_name = self.config.port self.baudrate = self.config.bitrate @@ -55,7 +56,7 @@ def __init__(self, config=None, policy=None): self._packets = deque() def __del__(self): - self.closeConnection() + self.close_connection() def make_header(self): def unpack_len(args): @@ -85,7 +86,7 @@ def unpack_len_filler(args): def connect(self): self.logger.info(f"XCPonSxI - serial comm_port openend: {self.comm_port.portstr}@{self.baudrate} Bits/Sec.") - self.startListener() + self.start_listener() def output(self, enable): if enable: @@ -98,8 +99,8 @@ def output(self, enable): def flush(self): self.comm_port.flush() - def startListener(self): - super().startListener() + def start_listener(self): + super().start_listener() def listen(self): while True: @@ -117,13 +118,13 @@ def listen(self): if len(response) != length: raise types.FrameSizeError("Size mismatch.") - self.processResponse(response, length, counter, recv_timestamp) + self.process_response(response, length, counter, recv_timestamp) def send(self, frame): self.pre_send_timestamp = self.timestamp.value self.comm_port.write(frame) self.post_send_timestamp = self.timestamp.value - def closeConnection(self): + def close_connection(self): if hasattr(self, "comm_port") and self.comm_port.isOpen(): self.comm_port.close() diff --git a/pyxcp/transport/usb_transport.py b/pyxcp/transport/usb_transport.py index 4221338..528d8fd 100644 --- a/pyxcp/transport/usb_transport.py +++ b/pyxcp/transport/usb_transport.py @@ -3,7 +3,8 @@ import threading from array import array from collections import deque -from time import perf_counter, sleep +from time import sleep +from typing import Optional import usb.backend.libusb0 as libusb0 import usb.backend.libusb1 as libusb1 @@ -19,14 +20,17 @@ RECV_SIZE = 16384 +FIVE_MS = 5_000_000 # Five milliseconds in nanoseconds. + + class Usb(BaseTransport): """""" HEADER = struct.Struct("<2H") HEADER_SIZE = HEADER.size - def __init__(self, config=None, policy=None): - super().__init__(config, policy) + def __init__(self, config=None, policy=None, transport_layer_interface: Optional[usb.core.Device] = None): + super().__init__(config, policy, transport_layer_interface) self.load_config(config) self.serial_number = self.config.serial_number self.vendor_id = self.config.vendor_id @@ -99,11 +103,11 @@ def connect(self): self.out_ep = interface[self.out_ep_number] self.in_ep = interface[self.in_ep_number] - self.startListener() + self.start_listener() self.status = 1 # connected - def startListener(self): - super().startListener() + def start_listener(self): + super().start_listener() if self._packet_listener.is_alive(): self._packet_listener.join() self._packet_listener = threading.Thread(target=self._packet_listen) @@ -111,22 +115,19 @@ def startListener(self): def close(self): """Close the transport-layer connection and event-loop.""" - self.finishListener() + self.finish_listener() if self.listener.is_alive(): self.listener.join() if self._packet_listener.is_alive(): self._packet_listener.join() - self.closeConnection() + self.close_connection() def _packet_listen(self): close_event_set = self.closeEvent.is_set - _packets = self._packets read = self.in_ep.read - buffer = array("B", bytes(RECV_SIZE)) buffer_view = memoryview(buffer) - while True: try: if close_event_set(): @@ -150,42 +151,31 @@ def _packet_listen(self): def listen(self): HEADER_UNPACK_FROM = self.HEADER.unpack_from HEADER_SIZE = self.HEADER_SIZE - popleft = self._packets.popleft - - processResponse = self.processResponse + process_response = self.process_response close_event_set = self.closeEvent.is_set - _packets = self._packets length, counter = None, None - data = bytearray(b"") - - last_sleep = perf_counter() + last_sleep = self.timestamp.value while True: if close_event_set(): return - count = len(_packets) - if not count: sleep(SHORT_SLEEP) - last_sleep = perf_counter() + last_sleep = self.timestamp.value continue - for _ in range(count): bts, timestamp = popleft() - data += bts current_size = len(data) current_position = 0 - while True: - if perf_counter() - last_sleep >= 0.005: + if self.timestamp.value - last_sleep >= FIVE_MS: sleep(SHORT_SLEEP) - last_sleep = perf_counter() - + last_sleep = self.timestamp.value if length is None: if current_size >= HEADER_SIZE: length, counter = HEADER_UNPACK_FROM(data, current_position) @@ -197,13 +187,10 @@ def listen(self): else: if current_size >= length: response = data[current_position : current_position + length] - processResponse(response, length, counter, timestamp) - + process_response(response, length, counter, timestamp) current_size -= length current_position += length - length = None - else: data = data[current_position:] break @@ -220,6 +207,6 @@ def send(self, frame): pass self.post_send_timestamp = self.timestamp.value - def closeConnection(self): + def close_connection(self): if self.device is not None: usb.util.dispose_resources(self.device) From 701c31fa504db489d248c26ea9b6b38f133ebec2 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Sat, 27 Jul 2024 11:59:21 +0300 Subject: [PATCH 94/99] sleep fixes --- poetry.lock | 24 +--------- pyproject.toml | 1 - pyxcp/__init__.py | 15 ------- pyxcp/cpp_ext/__init__.py | 2 + pyxcp/cpp_ext/extension_wrapper.cpp | 3 ++ pyxcp/cpp_ext/helper.hpp | 8 ++++ pyxcp/master/master.py | 5 +-- pyxcp/transport/base.py | 15 +++---- pyxcp/transport/can.py | 44 +++++++++++-------- pyxcp/transport/eth.py | 68 +++++++++++------------------ pyxcp/transport/sxi.py | 57 +++++++++++++----------- pyxcp/transport/usb_transport.py | 50 ++++++++++----------- pyxcp/utils.py | 7 ++- 13 files changed, 133 insertions(+), 166 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4db230a..c8646d9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2327,28 +2327,6 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] -[[package]] -name = "win-precise-time" -version = "1.4.2" -description = "" -optional = false -python-versions = ">=3.7" -files = [ - {file = "win-precise-time-1.4.2.tar.gz", hash = "sha256:89274785cbc5f2997e01675206da3203835a442c60fd97798415c6b3c179c0b9"}, - {file = "win_precise_time-1.4.2-cp310-cp310-win32.whl", hash = "sha256:7fa13a2247c2ef41cd5e9b930f40716eacc7fc1f079ea72853bd5613fe087a1a"}, - {file = "win_precise_time-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:bb8e44b0fc35fde268e8a781cdcd9f47d47abcd8089465d2d1d1063976411c8e"}, - {file = "win_precise_time-1.4.2-cp311-cp311-win32.whl", hash = "sha256:59272655ad6f36910d0b585969402386fa627fca3be24acc9a21be1d550e5db8"}, - {file = "win_precise_time-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:0897bb055f19f3b4336e2ba6bee0115ac20fd7ec615a6d736632e2df77f8851a"}, - {file = "win_precise_time-1.4.2-cp312-cp312-win32.whl", hash = "sha256:0210dcea88a520c91de1708ae4c881e3c0ddc956daa08b9eabf2b7c35f3109f5"}, - {file = "win_precise_time-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:85670f77cc8accd8f1e6d05073999f77561c23012a9ee988cbd44bb7ce655062"}, - {file = "win_precise_time-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:3e23693201a0fc6ca39f016871e2581e20c91123734bd48a69259f8c8724eedb"}, - {file = "win_precise_time-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:07ef644d1bb7705039bc54abfe4b45e99e8dc326dfd1dad5831dab19670508cb"}, - {file = "win_precise_time-1.4.2-cp38-cp38-win32.whl", hash = "sha256:0a953b00772f205602fa712ef68387b8fb213a30b267ae310aa56bf17605e11b"}, - {file = "win_precise_time-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b5d83420925beca302b386b19c3e7414ada84b47b42f0680207f1508917a1731"}, - {file = "win_precise_time-1.4.2-cp39-cp39-win32.whl", hash = "sha256:50d11a6ff92e1be96a8d4bee99ff6dc07a0ea0e2a392b0956bb2192e334f41ba"}, - {file = "win_precise_time-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f510fa92d9c39ea533c983e1d62c7bc66fdf0a3e3c3bdda48d4ebb634ff7034"}, -] - [[package]] name = "wrapt" version = "1.16.0" @@ -2473,4 +2451,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "8a979a0f5f5e9158ba542535bc159d5a698854e0229f960c9d8738153cef2431" +content-hash = "2afd9ffcdd5ad1a6dbc0ef02a986f769bf99859fc19fb4e96e2e1e73f1773a29" diff --git a/pyproject.toml b/pyproject.toml index 91469e0..5e7ffb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,7 +87,6 @@ toml = "^0.10.2" bandit = "^1.7.8" tomlkit = "^0.12.5" pytz = "^2024.1" -win-precise-time = "^1.4.2" [tool.poetry.group.dev.dependencies] ruff = "^0.1.0" diff --git a/pyxcp/__init__.py b/pyxcp/__init__.py index 373be26..d5325c2 100644 --- a/pyxcp/__init__.py +++ b/pyxcp/__init__.py @@ -1,6 +1,5 @@ #!/usr/bin/env python """Universal Calibration Protocol for Python""" -import sys from rich import pretty from rich.console import Console @@ -16,19 +15,5 @@ console = Console() tb_install(show_locals=True, max_frames=3) # Install custom exception handler. -if sys.platform == "win32" and sys.version_info[:2] < (3, 11): - # patch the time module with the high resolution alternatives - try: - import time - - from win_precise_time import sleep as wpsleep - from win_precise_time import time as wptime - - time.sleep = wpsleep - time.time = wptime - - except ImportError: - pass - # if you update this manually, do not forget to update .bumpversion.cfg __version__ = "0.21.6" diff --git a/pyxcp/cpp_ext/__init__.py b/pyxcp/cpp_ext/__init__.py index 4a07cae..2026297 100644 --- a/pyxcp/cpp_ext/__init__.py +++ b/pyxcp/cpp_ext/__init__.py @@ -5,4 +5,6 @@ Timestamp, TimestampInfo, TimestampType, + sleep_ms, + sleep_ns, ) diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index 6b8a599..4dd7c78 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -23,6 +23,9 @@ class PyTimestampInfo : public TimestampInfo { PYBIND11_MODULE(cpp_ext, m) { m.doc() = "C++ extensions for pyXCP."; + m.def("sleep_ms", &sleep_ms, "milliseconds"_a); + m.def("sleep_ns", &sleep_ns, "nanoseconds"_a); + py::class_(m, "McObject") .def( py::init< diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 3e9f91f..f99ffc0 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -245,4 +245,12 @@ class Timestamp { std::uint64_t m_initial; }; +inline void sleep_ms(std::uint64_t milliseconds) { + std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); +} + +inline void sleep_ns(std::uint64_t nanoseconds) { + std::this_thread::sleep_for(std::chrono::nanoseconds(nanoseconds)); +} + #endif // __HELPER_HPP diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index 0c2a6c9..b39f7db 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -10,7 +10,6 @@ import struct import traceback import warnings -from time import sleep from typing import Any, Callable, Collection, Dict, List, Optional, Tuple from pyxcp import checksum, types @@ -27,7 +26,7 @@ from pyxcp.daq_stim.stim import DaqEventInfo, Stim from pyxcp.master.errorhandler import SystemExit, disable_error_handling, wrapped from pyxcp.transport.base import create_transport -from pyxcp.utils import SHORT_SLEEP, decode_bytes, delay +from pyxcp.utils import decode_bytes, delay, short_sleep def broadcasted(func: Callable): @@ -446,7 +445,7 @@ def upload(self, length: int): response += data[1 : rem + 1] rem = byte_count - len(response) else: - sleep(SHORT_SLEEP) + short_sleep() return response @wrapped diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 8e09b6a..b56955b 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -2,9 +2,6 @@ import abc import threading from collections import deque - -# from datetime import datetime -from time import sleep from typing import Any, Dict, Optional, Set, Type import pyxcp.types as types @@ -13,11 +10,11 @@ from ..recorder import XcpLogFileWriter from ..timing import Timing from ..utils import ( - SHORT_SLEEP, CurrentDatetime, flatten, hexDump, seconds_to_nanoseconds, + short_sleep, ) @@ -44,7 +41,7 @@ def filtered_out(self) -> Set[types.FrameCategory]: def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: ... # noqa: E704 - def finalize(self, *args) -> None: + def finalize(self) -> None: """ Finalize the frame acquisition policy (if required). """ @@ -153,7 +150,7 @@ def __init__(self, config, policy: Optional[FrameAcquisitionPolicy] = None, tran self.logger: Any = config.log self._debug: bool = self.logger.level == 10 if transport_layer_interface: - self.logger.info(f"User supplied transport layer interface '{transport_layer_interface!s}'.") + self.logger.info(f"Transport - User Supplied Transport-Layer Interface: '{transport_layer_interface!s}'") self.counter_send: int = 0 self.counter_received: int = -1 self.create_daq_timestamps: bool = config.create_daq_timestamps @@ -207,7 +204,7 @@ def get(self): self.timer_restart_event.restart_event.clear() if self.timestamp.value - start > self.timeout: raise EmptyFrameError - sleep(SHORT_SLEEP) + short_sleep() item = self.resQueue.popleft() # print("Q", item) return item @@ -342,7 +339,7 @@ def block_receive(self, length_required: int) -> bytes: else: if self.timestamp.value - start > self.timeout: raise types.XcpTimeoutError("Response timed out [block_receive].") from None - sleep(SHORT_SLEEP) + short_sleep() return block_response @abc.abstractmethod @@ -398,7 +395,7 @@ def process_response(self, response: bytes, length: int, counter: int, recv_time if self.create_daq_timestamps: timestamp = recv_timestamp else: - timestamp = 0.0 + timestamp = 0 with self.policy_lock: self.policy.feed(types.FrameCategory.DAQ, self.counter_received, timestamp, response) diff --git a/pyxcp/transport/can.py b/pyxcp/transport/can.py index ef6ea7e..acaa3bb 100644 --- a/pyxcp/transport/can.py +++ b/pyxcp/transport/can.py @@ -8,7 +8,8 @@ from typing import Any, Dict, Optional, Type from can import CanError, CanInitializationError, Message, detect_available_configs -from can.interface import BusABC, _get_class_for_interface +from can.bus import BusABC +from can.interface import _get_class_for_interface from rich.console import Console from pyxcp.config import CAN_INTERFACE_MAP @@ -169,7 +170,7 @@ def type_str(self) -> str: return "E" if self.is_extended else "S" @staticmethod - def make_identifier(identifier: int, extended: bool) -> int: + def make_identifier(identifier: int, extended: bool) -> "Identifier": """Factory method. Parameters @@ -201,26 +202,26 @@ def create_filter_from_id(self) -> Dict: "extended": self.is_extended, } - def __eq__(self, other): + def __eq__(self, other) -> bool: return (self.id == other.id) and (self.is_extended == other.is_extended) - def __str__(self): + def __str__(self) -> str: return f"Identifier(id = 0x{self.id:08x}, is_extended = {self.is_extended})" - def __repr__(self): + def __repr__(self) -> str: return f"Identifier(0x{self.raw_id:08x})" class Frame: """""" - def __init__(self, id_: Identifier, dlc: int, data: bytes, timestamp: int): - self.id = id_ - self.dlc = dlc - self.data = data - self.timestamp = timestamp + def __init__(self, id_: Identifier, dlc: int, data: bytes, timestamp: int) -> None: + self.id: Identifier = id_ + self.dlc: int = dlc + self.data: bytes = data + self.timestamp: int = timestamp - def __repr__(self): + def __repr__(self) -> str: return f"Frame(id = 0x{self.id:08x}, dlc = {self.dlc}, data = {self.data}, timestamp = {self.timestamp})" __str__ = __repr__ @@ -229,11 +230,13 @@ def __repr__(self): class PythonCanWrapper: """Wrapper around python-can - github.com/hardbyte/python-can""" - def __init__(self, parent, interface_name: str, **parameters) -> None: + def __init__(self, parent, interface_name: str, timeout: int, **parameters) -> None: self.parent = parent self.interface_name: str = interface_name + self.timeout: int = timeout self.parameters = parameters self.can_interface_class: Type[BusABC] = _get_class_for_interface(self.interface_name) + self.can_interface: BusABC self.connected: bool = False def connect(self): @@ -241,18 +244,22 @@ def connect(self): return can_filters = [] can_filters.append(self.parent.can_id_slave.create_filter_from_id()) # Primary CAN filter. - self.can_interface: BusABC = self.can_interface_class(interface=self.interface_name, **self.parameters) + if self.parent.has_user_supplied_interface: + self.can_interface = self.parent.transport_layer_interface + else: + self.can_interface = self.can_interface_class(interface=self.interface_name, **self.parameters) if self.parent.daq_identifier: # Add filters for DAQ identifiers. for daq_id in self.parent.daq_identifier: can_filters.append(daq_id.create_filter_from_id()) self.can_interface.set_filters(can_filters) - self.parent.logger.debug(f"XCPonCAN - Interface: {self.interface_name!r} -- {self.can_interface!s}") - self.parent.logger.debug(f"XCPonCAN - Filters used: {self.can_interface.filters}") + self.parent.logger.info(f"XCPonCAN - Using Interface: '{self.can_interface!s}'") + self.parent.logger.info(f"XCPonCAN - Filters used: {self.can_interface.filters}") + self.parent.logger.info(f"XCPonCAN - State: {self.can_interface.state!s}") self.connected = True def close(self): - if self.connected: + if self.connected and not self.parent.has_user_supplied_interface: self.can_interface.shutdown() self.connected = False @@ -322,13 +329,12 @@ def __init__(self, config, policy=None, transport_layer_interface: Optional[BusA self.interface_name = self.config.interface self.interface_configuration = detect_available_configs(interfaces=[self.interface_name]) parameters = self.get_interface_parameters() - self.logger.info(f"XCPonCAN - opening {self.interface_name!r} CAN-interface {list(parameters.items())}") + self.can_interface = PythonCanWrapper(self, self.interface_name, config.timeout, **parameters) + self.logger.info(f"XCPonCAN - Interface-Type: {self.interface_name!r} Parameters: {list(parameters.items())}") self.logger.info( f"XCPonCAN - Master-ID (Tx): 0x{self.can_id_master.id:08X}{self.can_id_master.type_str} -- " f"Slave-ID (Rx): 0x{self.can_id_slave.id:08X}{self.can_id_slave.type_str}" ) - self.can_interface = PythonCanWrapper(self, self.interface_name, **parameters) - self.can_interface.timeout = config.timeout # c.Transport.timeout def get_interface_parameters(self) -> Dict[str, Any]: result = dict(channel=self.config.channel) diff --git a/pyxcp/transport/eth.py b/pyxcp/transport/eth.py index 30d5ffe..6494dde 100644 --- a/pyxcp/transport/eth.py +++ b/pyxcp/transport/eth.py @@ -4,11 +4,10 @@ import struct import threading from collections import deque -from time import sleep from typing import Optional from pyxcp.transport.base import BaseTransport -from pyxcp.utils import SHORT_SLEEP +from pyxcp.utils import short_sleep DEFAULT_XCP_PORT = 5555 @@ -39,16 +38,16 @@ class Eth(BaseTransport): HEADER = struct.Struct(" None: super().__init__(config, policy, transport_layer_interface) self.load_config(config) - self.host = self.config.host - self.port = self.config.port - self.protocol = self.config.protocol - self.ipv6 = self.config.ipv6 - self.use_tcp_no_delay = self.config.tcp_nodelay - address_to_bind = self.config.bind_to_address - bind_to_port = self.config.bind_to_port + self.host: str = self.config.host + self.port: int = self.config.port + self.protocol: int = self.config.protocol + self.ipv6: bool = self.config.ipv6 + self.use_tcp_no_delay: bool = self.config.tcp_nodelay + address_to_bind: str = self.config.bind_to_address + bind_to_port: int = self.config.bind_to_port self._local_address = (address_to_bind, bind_to_port) if address_to_bind else None if self.ipv6 and not socket.has_ipv6: msg = "XCPonEth - IPv6 not supported by your platform." @@ -73,7 +72,7 @@ def __init__(self, config=None, policy=None, transport_layer_interface: Optional msg = f"XCPonEth - Failed to resolve address {self.host}:{self.port}" self.logger.critical(msg) raise Exception(msg) from ex - self.status = 0 + self.status: int = 0 self.sock = socket.socket(self.address_family, self.socktype, self.proto) self.selector = selectors.DefaultSelector() self.selector.register(self.sock, selectors.EVENT_READ) @@ -98,21 +97,21 @@ def __init__(self, config=None, policy=None, transport_layer_interface: Optional ) self._packets = deque() - def connect(self): + def connect(self) -> None: if self.status == 0: self.sock.connect(self.sockaddr) self.logger.info(socket_to_str(self.sock)) self.start_listener() self.status = 1 # connected - def start_listener(self): + def start_listener(self) -> None: super().start_listener() if self._packet_listener.is_alive(): self._packet_listener.join() self._packet_listener = threading.Thread(target=self._packet_listen) self._packet_listener.start() - def close(self): + def close(self) -> None: """Close the transport-layer connection and event-loop.""" self.finish_listener() if self.listener.is_alive(): @@ -121,21 +120,17 @@ def close(self): self._packet_listener.join() self.close_connection() - def _packet_listen(self): - use_tcp = self.use_tcp + def _packet_listen(self) -> None: + use_tcp: bool = self.use_tcp EVENT_READ = selectors.EVENT_READ - close_event_set = self.closeEvent.is_set socket_fileno = self.sock.fileno select = self.selector.select - _packets = self._packets - if use_tcp: sock_recv = self.sock.recv else: sock_recv = self.sock.recvfrom - while True: try: if close_event_set() or socket_fileno() == -1: @@ -144,7 +139,6 @@ def _packet_listen(self): for _, events in sel: if events & EVENT_READ: recv_timestamp = self.timestamp.value - if use_tcp: response = sock_recv(RECV_SIZE) if not response: @@ -165,36 +159,29 @@ def _packet_listen(self): self.status = 0 # disconnected break - def listen(self): + def listen(self) -> None: HEADER_UNPACK_FROM = self.HEADER.unpack_from HEADER_SIZE = self.HEADER_SIZE process_response = self.process_response popleft = self._packets.popleft - close_event_set = self.closeEvent.is_set socket_fileno = self.sock.fileno - _packets = self._packets - length, counter = None, None - - data = bytearray(b"") - + length: Optional[int] = None + counter: int = 0 + data: bytearray = bytearray(b"") while True: if close_event_set() or socket_fileno() == -1: return - - count = len(_packets) - + count: int = len(_packets) if not count: - sleep(SHORT_SLEEP) + short_sleep() continue - for _ in range(count): bts, timestamp = popleft() data += bts - current_size = len(data) - current_position = 0 - + current_size: int = len(data) + current_position: int = 0 while True: if length is None: if current_size >= HEADER_SIZE: @@ -208,22 +195,19 @@ def listen(self): if current_size >= length: response = data[current_position : current_position + length] process_response(response, length, counter, timestamp) - current_size -= length current_position += length - length = None - else: data = data[current_position:] break - def send(self, frame): + def send(self, frame) -> None: self.pre_send_timestamp = self.timestamp.value self.sock.send(frame) self.post_send_timestamp = self.timestamp.value - def close_connection(self): + def close_connection(self) -> None: if not self.invalidSocket: # Seems to be problematic /w IPv6 # if self.status == 1: @@ -231,5 +215,5 @@ def close_connection(self): self.sock.close() @property - def invalidSocket(self): + def invalidSocket(self) -> bool: return not hasattr(self, "sock") or self.sock.fileno() == -1 diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index 91f2c5e..0977c75 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -24,7 +24,7 @@ class HeaderValues: class SxI(BaseTransport): """""" - def __init__(self, config=None, policy=None, transport_layer_interface: Optional[serial.Serial] = None): + def __init__(self, config=None, policy=None, transport_layer_interface: Optional[serial.Serial] = None) -> None: super().__init__(config, policy, transport_layer_interface) self.load_config(config) self.port_name = self.config.port @@ -39,26 +39,31 @@ def __init__(self, config=None, policy=None, transport_layer_interface: Optional self.esc_sync = self.config.esc_sync self.esc_esc = self.config.esc_esc self.make_header() - self.logger.info(f"XCPonSxI - trying to open serial comm_port {self.port_name}.") - try: - self.comm_port = serial.Serial( - port=self.port_name, - baudrate=self.baudrate, - bytesize=self.bytesize, - parity=self.parity, - stopbits=self.stopbits, - timeout=self.timeout, - write_timeout=self.timeout, - ) - except serial.SerialException as e: - self.logger.critical(f"XCPonSxI - {e}") - raise + self.comm_port: serial.Serial + + if self.has_user_supplied_interface and transport_layer_interface: + self.comm_port = transport_layer_interface + else: + self.logger.info(f"XCPonSxI - trying to open serial comm_port {self.port_name}.") + try: + self.comm_port = serial.Serial( + port=self.port_name, + baudrate=self.baudrate, + bytesize=self.bytesize, + parity=self.parity, + stopbits=self.stopbits, + timeout=self.timeout, + write_timeout=self.timeout, + ) + except serial.SerialException as e: + self.logger.critical(f"XCPonSxI - {e}") + raise self._packets = deque() - def __del__(self): + def __del__(self) -> None: self.close_connection() - def make_header(self): + def make_header(self) -> None: def unpack_len(args): (length,) = args return HeaderValues(length=length) @@ -84,11 +89,11 @@ def unpack_len_filler(args): self.HEADER_SIZE = self.HEADER.size self.unpacker = unpacker - def connect(self): + def connect(self) -> None: self.logger.info(f"XCPonSxI - serial comm_port openend: {self.comm_port.portstr}@{self.baudrate} Bits/Sec.") self.start_listener() - def output(self, enable): + def output(self, enable) -> None: if enable: self.comm_port.rts = False self.comm_port.dtr = False @@ -96,17 +101,17 @@ def output(self, enable): self.comm_port.rts = True self.comm_port.dtr = True - def flush(self): + def flush(self) -> None: self.comm_port.flush() - def start_listener(self): + def start_listener(self) -> None: super().start_listener() - def listen(self): + def listen(self) -> None: while True: if self.closeEvent.is_set(): return - if not self.comm_port.inWaiting(): + if not self.comm_port.in_waiting(): continue recv_timestamp = self.timestamp.value @@ -120,11 +125,11 @@ def listen(self): raise types.FrameSizeError("Size mismatch.") self.process_response(response, length, counter, recv_timestamp) - def send(self, frame): + def send(self, frame) -> None: self.pre_send_timestamp = self.timestamp.value self.comm_port.write(frame) self.post_send_timestamp = self.timestamp.value - def close_connection(self): - if hasattr(self, "comm_port") and self.comm_port.isOpen(): + def close_connection(self) -> None: + if hasattr(self, "comm_port") and self.comm_port.is_open() and not self.has_user_supplied_interface: self.comm_port.close() diff --git a/pyxcp/transport/usb_transport.py b/pyxcp/transport/usb_transport.py index 528d8fd..de26ee0 100644 --- a/pyxcp/transport/usb_transport.py +++ b/pyxcp/transport/usb_transport.py @@ -3,7 +3,6 @@ import threading from array import array from collections import deque -from time import sleep from typing import Optional import usb.backend.libusb0 as libusb0 @@ -14,12 +13,10 @@ from usb.core import USBError, USBTimeoutError from pyxcp.transport.base import BaseTransport -from pyxcp.utils import SHORT_SLEEP +from pyxcp.utils import short_sleep RECV_SIZE = 16384 - - FIVE_MS = 5_000_000 # Five milliseconds in nanoseconds. @@ -32,27 +29,27 @@ class Usb(BaseTransport): def __init__(self, config=None, policy=None, transport_layer_interface: Optional[usb.core.Device] = None): super().__init__(config, policy, transport_layer_interface) self.load_config(config) - self.serial_number = self.config.serial_number - self.vendor_id = self.config.vendor_id - self.product_id = self.config.product_id - self.configuration_number = self.config.configuration_number - self.interface_number = self.config.interface_number - self.library = self.config.library - self.header_format = self.config.header_format + self.serial_number: str = self.config.serial_number + self.vendor_id: int = self.config.vendor_id + self.product_id: int = self.config.product_id + self.configuration_number: int = self.config.configuration_number + self.interface_number: int = self.config.interface_number + self.library: str = self.config.library + self.header_format: str = self.config.header_format ## IN-EP (RES/ERR, DAQ, and EV/SERV) Parameters. - self.in_ep_number = self.config.in_ep_number + self.in_ep_number: int = self.config.in_ep_number self.in_ep_transfer_type = self.config.in_ep_transfer_type - self.in_ep_max_packet_size = self.config.in_ep_max_packet_size - self.in_ep_polling_interval = self.config.in_ep_polling_interval + self.in_ep_max_packet_size: int = self.config.in_ep_max_packet_size + self.in_ep_polling_interval: int = self.config.in_ep_polling_interval self.in_ep_message_packing = self.config.in_ep_message_packing self.in_ep_alignment = self.config.in_ep_alignment - self.in_ep_recommended_host_bufsize = self.config.in_ep_recommended_host_bufsize + self.in_ep_recommended_host_bufsize: int = self.config.in_ep_recommended_host_bufsize ## OUT-EP (CMD and STIM) Parameters. - self.out_ep_number = self.config.out_ep_number + self.out_ep_number: int = self.config.out_ep_number - self.device = None + self.device: Optional[usb.core.Device] = None self.status = 0 self._packet_listener = threading.Thread( @@ -141,7 +138,7 @@ def _packet_listen(self): _packets.append((buffer.tobytes(), recv_timestamp)) except (USBError, USBTimeoutError): # print(format_exc()) - sleep(SHORT_SLEEP) + short_sleep() continue except BaseException: # noqa: B036 # Note: catch-all only permitted if the intention is re-raising. @@ -155,26 +152,27 @@ def listen(self): process_response = self.process_response close_event_set = self.closeEvent.is_set _packets = self._packets - length, counter = None, None - data = bytearray(b"") - last_sleep = self.timestamp.value + length: Optional[int] = None + counter: int = 0 + data: bytearray = bytearray(b"") + last_sleep: int = self.timestamp.value while True: if close_event_set(): return - count = len(_packets) + count: int = len(_packets) if not count: - sleep(SHORT_SLEEP) + short_sleep() last_sleep = self.timestamp.value continue for _ in range(count): bts, timestamp = popleft() data += bts - current_size = len(data) - current_position = 0 + current_size: int = len(data) + current_position: int = 0 while True: if self.timestamp.value - last_sleep >= FIVE_MS: - sleep(SHORT_SLEEP) + short_sleep() last_sleep = self.timestamp.value if length is None: if current_size >= HEADER_SIZE: diff --git a/pyxcp/utils.py b/pyxcp/utils.py index e1196ef..47761e2 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -9,7 +9,7 @@ import chardet import pytz -from pyxcp.cpp_ext import TimestampInfo +from pyxcp.cpp_ext import TimestampInfo, sleep_ns def hexDump(arr): @@ -69,7 +69,10 @@ def decode_bytes(byte_str: bytes) -> str: PYTHON_VERSION = getPythonVersion() -SHORT_SLEEP = 0.0005 + + +def short_sleep(): + sleep_ns(500000) # Sleep for 500µs. def delay(amount: float): From 4e141f0e0cc8f5b511b75c7e5f4022d81e1a7b7b Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Mon, 5 Aug 2024 08:00:12 +0300 Subject: [PATCH 95/99] today() --- .pre-commit-config.yaml | 14 +- CMakeLists.txt | 2 +- pyproject.toml | 18 ++ pyxcp/cmdline.py | 5 +- pyxcp/daq_stim/linreg.hpp | 351 ----------------------------------- pyxcp/dllif.py | 2 +- pyxcp/examples/ex_arrow.py | 2 - pyxcp/examples/ex_mdf.py | 2 - pyxcp/examples/ex_sqlite.py | 2 - pyxcp/master/errorhandler.py | 50 +++-- pyxcp/master/master.py | 4 +- pyxcp/recorder/writer.hpp | 2 - pyxcp/transport/base.py | 16 +- pyxcp/transport/sxi.py | 2 - pyxcp/utils.py | 6 +- 15 files changed, 77 insertions(+), 401 deletions(-) delete mode 100644 pyxcp/daq_stim/linreg.hpp diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 156b1de..b6dba1a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -107,13 +107,13 @@ repos: language: system types_or: [cython, pyi, python] args: ["--filter-files"] - #- id: pyupgrade - #name: pyupgrade - #description: Automatically upgrade syntax for newer versions. - #entry: pyupgrade - #language: system - #types: [python] - #args: [--py37-plus] + - id: pyupgrade + name: pyupgrade + description: Automatically upgrade syntax for newer versions. + entry: pyupgrade + language: system + types: [python] + args: [--py38-plus] - id: trailing-whitespace name: Trim Trailing Whitespace entry: trailing-whitespace-fixer diff --git a/CMakeLists.txt b/CMakeLists.txt index 912d6bf..f9db36f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows") message("Platform is WINDOWS") SET(MSVC_EXTRA_OPTIONS "") elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") - SET(GCC_N_CLANG_EXTRA_OPTIONS "-fvisibility=hidden -g0") # -fcoroutines + SET(GCC_N_CLANG_EXTRA_OPTIONS "-fvisibility=hidden -g0") # -fcoroutines message("Platform is LINUX") endif() diff --git a/pyproject.toml b/pyproject.toml index 5e7ffb8..c083a1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -169,3 +169,21 @@ build-verbosity = 3 build = "cp3{7,8,9,10,11,12}-*" skip = ["*-manylinux_i686", "*-musllinux_x86_64", "*-musllinux_i686"] # Skip Linux 32bit and MUSL builds. build-frontend = "build" + +[tool.pyright] +include = ["pyxcp", "build_ext.py"] +ignore = ["pyxcp/conf_test.py", "pyxcp/config_new.py", "pyxcp/config_tr.py", "pyxcp/examples/memxfer.py", + "pyxcp/examples/ts_tester.py", "pyxcp/recorder/converter/**", "pyxcp/transport/candriver/**", "pyxcp/recorder/simdjson/**", + "pyxcp/recorder/mio/**", "pyxcp/recorder/lz4/**"] +#defineConstant = { DEBUG = true } +#stubPath = "src/stubs" + +reportMissingImports = true +reportMissingTypeStubs = false + +#executionEnvironments = [ +# { root = "src/web", pythonVersion = "3.5", pythonPlatform = "Windows", extraPaths = [ "src/service_libs" ] }, +# { root = "src/sdk", pythonVersion = "3.0", extraPaths = [ "src/backend" ] }, +# { root = "src/tests", extraPaths = ["src/tests/e2e", "src/sdk" ]}, +# { root = "src" } +#] diff --git a/pyxcp/cmdline.py b/pyxcp/cmdline.py index 876d2d3..ca76421 100644 --- a/pyxcp/cmdline.py +++ b/pyxcp/cmdline.py @@ -27,8 +27,9 @@ def __init__(self, callout=None, *args, **kws): def run(self, policy=None, transport_layer_interface=None): application = create_application() - transport = application.transport.layer - master = Master(transport, config=application, policy=policy, transport_layer_interface=transport_layer_interface) + master = Master( + application.transport.layer, config=application, policy=policy, transport_layer_interface=transport_layer_interface + ) return master @property diff --git a/pyxcp/daq_stim/linreg.hpp b/pyxcp/daq_stim/linreg.hpp deleted file mode 100644 index a2f5005..0000000 --- a/pyxcp/daq_stim/linreg.hpp +++ /dev/null @@ -1,351 +0,0 @@ -// -// Created by Chris on 26.12.2023. -// - -#ifndef PYXCP_LINREG_HPP -#define PYXCP_LINREG_HPP - -#include -#include -#include -#include -#include - -#if 0 - #include "linreg.h" - #include "print.h" - #include "servo_private.h" -#endif - -/* Maximum and minimum number of points used in regression, - defined as a power of 2 */ -#define MAX_SIZE 6 -#define MIN_SIZE 2 - -#define MAX_POINTS (1 << MAX_SIZE) - -/* Smoothing factor used for long-term prediction error */ -#define ERR_SMOOTH 0.02 -/* Number of updates used for initialization */ -#define ERR_INITIAL_UPDATES 10 -/* Maximum ratio of two err values to be considered equal */ -#define ERR_EQUALS 1.05 - -struct servo { - double max_frequency; - double step_threshold; - double first_step_threshold; - int first_update; - int64_t offset_threshold; - int num_offset_values; - int curr_offset_values; - - void (*destroy)(struct servo *servo); - f double (*sample)(struct servo *servo, int64_t offset, uint64_t local_ts, double weight, enum servo_state *state); - - void (*sync_interval)(struct servo *servo, double interval); - - void (*reset)(struct servo *servo); - - double (*rate_ratio)(struct servo *servo); - - void (*leap)(struct servo *servo, int leap); -}; - -/* Uncorrected local time vs remote time */ -struct point { - uint64_t x; - uint64_t y; - double w; -}; - -struct result { - /* Slope and intercept from latest regression */ - double slope; - double intercept; - /* Exponential moving average of prediction error */ - double err; - /* Number of initial err updates */ - int err_updates; -}; - -struct linreg_servo { - struct servo servo; - /* Circular buffer of points */ - struct point points[MAX_POINTS]; - /* Current time in x, y */ - struct point reference; - /* Number of stored points */ - unsigned int num_points; - /* Index of the newest point */ - unsigned int last_point; - /* Remainder from last update of reference.x */ - double x_remainder; - /* Local time stamp of last update */ - uint64_t last_update; - /* Regression results for all sizes */ - struct result results[MAX_SIZE - MIN_SIZE + 1]; - /* Selected size */ - unsigned int size; - /* Current frequency offset of the clock */ - double clock_freq; - /* Expected interval between updates */ - double update_interval; - /* Current ratio between remote and local frequency */ - double frequency_ratio; - /* Upcoming leap second */ - int leap; -}; - -class LinearRegression { - public: - private: -}; - -static void linreg_destroy(struct servo *servo) { - struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); - free(s); -} - -static void move_reference(struct linreg_servo *s, int64_t x, int64_t y) { - struct result *res; - unsigned int i; - - s->reference.x += x; - s->reference.y += y; - - /* Update intercepts for new reference */ - for (i = MIN_SIZE; i <= MAX_SIZE; i++) { - res = &s->results[i - MIN_SIZE]; - res->intercept += x * res->slope - y; - } -} - -static void update_reference(struct linreg_servo *s, uint64_t local_ts) { - double x_interval; - int64_t y_interval; - - if (s->last_update) { - y_interval = local_ts - s->last_update; - - /* Remove current frequency correction from the interval */ - x_interval = y_interval / (1.0 + s->clock_freq / 1e9); - x_interval += s->x_remainder; - s->x_remainder = x_interval - (int64_t)x_interval; - - move_reference(s, (int64_t)x_interval, y_interval); - } - - s->last_update = local_ts; -} - -static void add_sample(struct linreg_servo *s, int64_t offset, double weight) { - s->last_point = (s->last_point + 1) % MAX_POINTS; - - s->points[s->last_point].x = s->reference.x; - s->points[s->last_point].y = s->reference.y - offset; - s->points[s->last_point].w = weight; - - if (s->num_points < MAX_POINTS) - s->num_points++; -} - -static void regress(struct linreg_servo *s) { - double x, y, y0, e, x_sum, y_sum, xy_sum, x2_sum, w, w_sum; - unsigned int i, l, n, size; - struct result *res; - - x_sum = 0.0, y_sum = 0.0, xy_sum = 0.0, x2_sum = 0.0; - w_sum = 0.0; - i = 0; - - y0 = (int64_t)(s->points[s->last_point].y - s->reference.y); - - for (size = MIN_SIZE; size <= MAX_SIZE; size++) { - n = 1 << size; - if (n > s->num_points) - /* Not enough points for this size */ - break; - - res = &s->results[size - MIN_SIZE]; - - /* Update moving average of the prediction error */ - if (res->slope) { - e = fabs(res->intercept - y0); - if (res->err_updates < ERR_INITIAL_UPDATES) { - res->err *= res->err_updates; - res->err += e; - res->err_updates++; - res->err /= res->err_updates; - } else { - res->err += ERR_SMOOTH * (e - res->err); - } - } - - for (; i < n; i++) { - /* Iterate points from newest to oldest */ - l = (MAX_POINTS + s->last_point - i) % MAX_POINTS; - - x = (int64_t)(s->points[l].x - s->reference.x); - y = (int64_t)(s->points[l].y - s->reference.y); - w = s->points[l].w; - - x_sum += x * w; - y_sum += y * w; - xy_sum += x * y * w; - x2_sum += x * x * w; - w_sum += w; - } - - /* Get new intercept and slope */ - res->slope = (xy_sum - x_sum * y_sum / w_sum) / (x2_sum - x_sum * x_sum / w_sum); - res->intercept = (y_sum - res->slope * x_sum) / w_sum; - } -} - -static void update_size(struct linreg_servo *s) { - struct result *res; - double best_err; - int size, best_size; - - /* Find largest size with smallest prediction error */ - - best_size = 0; - best_err = 0.0; - - for (size = MIN_SIZE; size <= MAX_SIZE; size++) { - res = &s->results[size - MIN_SIZE]; - if ((!best_size && res->slope) || (best_err * ERR_EQUALS > res->err && res->err_updates >= ERR_INITIAL_UPDATES)) { - best_size = size; - best_err = res->err; - } - } - - s->size = best_size; -} - -static double linreg_sample(struct servo *servo, int64_t offset, uint64_t local_ts, double weight, enum servo_state *state) { - struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); - struct result *res; - int corr_interval; - - /* - * The current time and the time when will be the frequency of the - * clock actually updated is assumed here to be equal to local_ts - * (which is the time stamp of the received sync message). As long as - * the differences are smaller than the update interval, the loop - * should be robust enough to handle this simplification. - */ - - update_reference(s, local_ts); - add_sample(s, offset, weight); - regress(s); - - update_size(s); - - if (s->size < MIN_SIZE) { - /* Not enough points, wait for more */ - *state = SERVO_UNLOCKED; - return -s->clock_freq; - } - - res = &s->results[s->size - MIN_SIZE]; - - pr_debug("linreg: points %d slope %.9f intercept %.0f err %.0f", 1 << s->size, res->slope, res->intercept, res->err); - - if ((servo->first_update && servo->first_step_threshold && servo->first_step_threshold < fabs(res->intercept)) || - (servo->step_threshold && servo->step_threshold < fabs(res->intercept))) { - /* The clock will be stepped by offset */ - move_reference(s, 0, -offset); - s->last_update -= offset; - *state = SERVO_JUMP; - } else { - *state = SERVO_LOCKED; - } - - /* Set clock frequency to the slope */ - s->clock_freq = 1e9 * (res->slope - 1.0); - - /* - * Adjust the frequency to correct the time offset. Use longer - * correction interval with larger sizes to reduce the frequency error. - * The update interval is assumed to be not affected by the frequency - * adjustment. If it is (e.g. phc2sys controlling the system clock), a - * correction slowing down the clock will result in an overshoot. With - * the system clock's maximum adjustment of 10% that's acceptable. - */ - corr_interval = s->size <= 4 ? 1 : s->size / 2; - s->clock_freq += res->intercept / s->update_interval / corr_interval; - - /* Clamp the frequency to the allowed maximum */ - if (s->clock_freq > servo->max_frequency) - s->clock_freq = servo->max_frequency; - else if (s->clock_freq < -servo->max_frequency) - s->clock_freq = -servo->max_frequency; - - s->frequency_ratio = res->slope / (1.0 + s->clock_freq / 1e9); - - return -s->clock_freq; -} - -static void linreg_sync_interval(struct servo *servo, double interval) { - struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); - - s->update_interval = interval; -} - -static void linreg_reset(struct servo *servo) { - struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); - unsigned int i; - - s->num_points = 0; - s->last_update = 0; - s->size = 0; - s->frequency_ratio = 1.0; - - for (i = MIN_SIZE; i <= MAX_SIZE; i++) { - s->results[i - MIN_SIZE].slope = 0.0; - s->results[i - MIN_SIZE].err_updates = 0; - } -} - -static double linreg_rate_ratio(struct servo *servo) { - struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); - - return s->frequency_ratio; -} - -static void linreg_leap(struct servo *servo, int leap) { - struct linreg_servo *s = container_of(servo, struct linreg_servo, servo); - - /* - * Move reference when leap second is applied to the reference - * time as if the clock was stepped in the opposite direction - */ - if (s->leap && !leap) - move_reference(s, 0, s->leap * 1000000000); - - s->leap = leap; -} - -struct servo *linreg_servo_create(double fadj) { - struct linreg_servo *s; - - s = calloc(1, sizeof(*s)); - if (!s) - return NULL; - - s->servo.destroy = linreg_destroy; - s->servo.sample = linreg_sample; - s->servo.sync_interval = linreg_sync_interval; - s->servo.reset = linreg_reset; - s->servo.rate_ratio = linreg_rate_ratio; - s->servo.leap = linreg_leap; - - s->clock_freq = -fadj; - s->frequency_ratio = 1.0; - - return &s->servo; -} - -#endif // PYXCP_LINREG_HPP diff --git a/pyxcp/dllif.py b/pyxcp/dllif.py index fa0f05c..afc1766 100644 --- a/pyxcp/dllif.py +++ b/pyxcp/dllif.py @@ -80,7 +80,7 @@ def getKey(logger, dllName: str, privilege: int, seed: bytes, assume_same_bit_wi except OSError as exc: logger.error(f"Cannot execute {LOADER!r} -- {exc}") return (SeedNKeyResult.ERR_COULD_NOT_LOAD_DLL, None) - key: bytes = bytes(b"") + key: bytes = b"" if p0.stdout: key = p0.stdout.read() p0.stdout.close() diff --git a/pyxcp/examples/ex_arrow.py b/pyxcp/examples/ex_arrow.py index 32adf3b..59cca71 100644 --- a/pyxcp/examples/ex_arrow.py +++ b/pyxcp/examples/ex_arrow.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import argparse import logging from array import array diff --git a/pyxcp/examples/ex_mdf.py b/pyxcp/examples/ex_mdf.py index 0129579..8946553 100644 --- a/pyxcp/examples/ex_mdf.py +++ b/pyxcp/examples/ex_mdf.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import argparse import logging from array import array diff --git a/pyxcp/examples/ex_sqlite.py b/pyxcp/examples/ex_sqlite.py index 6d3d3f3..74c90f4 100644 --- a/pyxcp/examples/ex_sqlite.py +++ b/pyxcp/examples/ex_sqlite.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import argparse import logging import os diff --git a/pyxcp/master/errorhandler.py b/pyxcp/master/errorhandler.py index 1ae9115..a118372 100644 --- a/pyxcp/master/errorhandler.py +++ b/pyxcp/master/errorhandler.py @@ -7,6 +7,7 @@ import time import types from collections import namedtuple +from typing import Generic, List, Optional, TypeVar import can @@ -120,12 +121,14 @@ def __init__(self, args=None, kwargs=None): self.args = tuple(args) self.kwargs = kwargs or {} - def __str__(self): + def __str__(self) -> str: res = f"{self.__class__.__name__}(ARGS = {self.args}, KWS = {self.kwargs})" return res - def __eq__(self, other): - return (self.args == other.args if other is not None else ()) and (self.kwargs == other.kwargs if other is not None else {}) + def __eq__(self, other) -> bool: + return (self.args == other.args if other is not None else False) and ( + self.kwargs == other.kwargs if other is not None else False + ) __repr__ = __str__ @@ -187,7 +190,9 @@ def __init__(self, instance, func, arguments, error_code=None): self.func = func self.arguments = arguments self.service = self.instance.service - self.error_code = error_code + self._error_code: int = 0 + if error_code is not None: + self._error_code = error_code self._repeater = None self.logger = logging.getLogger("PyXCP") @@ -199,6 +204,14 @@ def __eq__(self, other): return False return (self.instance == other.instance) and (self.func == other.func) and (self.arguments == other.arguments) + @property + def error_code(self) -> int: + return self._error_code + + @error_code.setter + def error_code(self, value: int) -> None: + self._error_code = value + @property def repeater(self): # print("\tGet repeater", hex(id(self._repeater)), self._repeater is None) @@ -283,39 +296,43 @@ def actions(self, preActions, actions): return result_pre_actions, result_actions, Repeater(repetitionCount) -class HandlerStack: +T = TypeVar("T") + + +class HandlerStack(Generic[T]): """""" - def __init__(self): - self._stack = [] + def __init__(self) -> None: + self._stack: List[T] = [] - def push(self, handler): - if handler != self.tos(): - self._stack.append(handler) + def push(self, value: T): + if value != self.tos(): + self._stack.append(value) - def pop(self): + def pop(self) -> None: if len(self) > 0: self._stack.pop() - def tos(self): + def tos(self) -> Optional[T]: if len(self) > 0: return self._stack[-1] else: return None + # raise ValueError("empty stack.") - def empty(self): + def empty(self) -> bool: return self._stack == [] - def __len__(self): + def __len__(self) -> int: return len(self._stack) - def __repr__(self): + def __repr__(self) -> str: result = [] for idx in range(len(self)): result.append(str(self[idx])) return "\n".join(result) - def __getitem__(self, ndx): + def __getitem__(self, ndx: int) -> T: return self._stack[ndx] __str__ = __repr__ @@ -339,7 +356,6 @@ def __call__(self, inst, func, arguments): self.arguments = arguments handler = Handler(inst, func, arguments) self.handlerStack.push(handler) - # print("\tENTER handler:", hex(id(handler))) try: while True: try: diff --git a/pyxcp/master/master.py b/pyxcp/master/master.py index b39f7db..3417a1e 100644 --- a/pyxcp/master/master.py +++ b/pyxcp/master/master.py @@ -63,7 +63,9 @@ class Master: config: dict """ - def __init__(self, transport_name: str, config, policy=None, transport_layer_interface=None): + def __init__(self, transport_name: Optional[str], config, policy=None, transport_layer_interface=None): + if transport_name is None: + raise ValueError("No transport-layer selected") # Never reached -- to keep type-checkers happy. self.ctr = 0 self.succeeded = True self.config = config.general diff --git a/pyxcp/recorder/writer.hpp b/pyxcp/recorder/writer.hpp index 631e839..208e04d 100644 --- a/pyxcp/recorder/writer.hpp +++ b/pyxcp/recorder/writer.hpp @@ -17,8 +17,6 @@ class XcpLogFileWriter { } m_opened = false; - std::cout << "Writer::c_tor()\n"; - #if defined(_WIN32) m_fd = CreateFileA( m_file_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) nullptr, CREATE_ALWAYS, diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index b56955b..4dad903 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -60,7 +60,7 @@ class LegacyFrameAcquisitionPolicy(FrameAcquisitionPolicy): Deprecated: Use only for compatibility reasons. """ - def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None): + def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None) -> None: super().__init__(filter_out) self.reqQueue = deque() self.resQueue = deque() @@ -98,7 +98,7 @@ def __init__( filter_out: Optional[Set[types.FrameCategory]] = None, prealloc: int = 10, chunk_size: int = 1, - ): + ) -> None: super().__init__(filter_out) self.recorder = XcpLogFileWriter(file_name, prealloc=prealloc, chunk_size=chunk_size) @@ -113,7 +113,7 @@ def finalize(self) -> None: class StdoutPolicy(FrameAcquisitionPolicy): """Frame acquisition policy that prints frames to stdout.""" - def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None): + def __init__(self, filter_out: Optional[Set[types.FrameCategory]] = None) -> None: super().__init__(filter_out) def feed(self, frame_type: types.FrameCategory, counter: int, timestamp: int, payload: bytes) -> None: @@ -175,16 +175,16 @@ def __init__(self, config, policy: Optional[FrameAcquisitionPolicy] = None, tran self.post_send_timestamp: int = self.timestamp.value self.recv_timestamp: int = self.timestamp.value - def __del__(self): + def __del__(self) -> None: self.finish_listener() self.close_connection() - def load_config(self, config): + def load_config(self, config) -> None: """Load configuration data.""" class_name: str = self.__class__.__name__.lower() self.config: Any = getattr(config, class_name) - def close(self): + def close(self) -> None: """Close the transport-layer connection and event-loop.""" self.finish_listener() if self.listener.is_alive(): @@ -192,7 +192,7 @@ def close(self): self.close_connection() @abc.abstractmethod - def connect(self): + def connect(self) -> None: pass def get(self): @@ -201,7 +201,7 @@ def get(self): while not self.resQueue: if self.timer_restart_event.is_set(): start: int = self.timestamp.value - self.timer_restart_event.restart_event.clear() + self.timer_restart_event.clear() if self.timestamp.value - start > self.timeout: raise EmptyFrameError short_sleep() diff --git a/pyxcp/transport/sxi.py b/pyxcp/transport/sxi.py index 0977c75..de45079 100644 --- a/pyxcp/transport/sxi.py +++ b/pyxcp/transport/sxi.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import struct from collections import deque from dataclasses import dataclass diff --git a/pyxcp/utils.py b/pyxcp/utils.py index 47761e2..c475ec4 100644 --- a/pyxcp/utils.py +++ b/pyxcp/utils.py @@ -4,12 +4,12 @@ import operator import sys from binascii import hexlify -from time import perf_counter +from time import perf_counter, sleep import chardet import pytz -from pyxcp.cpp_ext import TimestampInfo, sleep_ns +from pyxcp.cpp_ext import TimestampInfo def hexDump(arr): @@ -72,7 +72,7 @@ def decode_bytes(byte_str: bytes) -> str: def short_sleep(): - sleep_ns(500000) # Sleep for 500µs. + sleep(0.0005) def delay(amount: float): From a662e5e22e8e1b83ce3db30f05551d64c624a4af Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 13 Aug 2024 12:43:58 +0300 Subject: [PATCH 96/99] today() --- .gitmodules | 4 - .pdm.toml | 25 - pyxcp/cxx/asynchiofactory.hpp | 24 - pyxcp/cxx/blocking_client.cpp | 44 -- pyxcp/cxx/blocking_socket.cpp | 43 -- pyxcp/cxx/blocking_socket.hpp | 558 ------------------ pyxcp/cxx/concurrent_queue.hpp | 60 -- pyxcp/cxx/eth.hpp | 57 -- pyxcp/cxx/exceptions.hpp | 30 - pyxcp/cxx/iasyncioservice.hpp | 31 - pyxcp/cxx/iresource.hpp | 17 - pyxcp/cxx/isocket.hpp | 22 - pyxcp/cxx/linux/epoll.cpp | 51 -- pyxcp/cxx/linux/epoll.hpp | 87 --- pyxcp/cxx/linux/lit_tester.cpp | 19 - pyxcp/cxx/linux/socket.hpp | 234 -------- pyxcp/cxx/linux/timeout.hpp | 81 --- pyxcp/cxx/memoryblock.hpp | 42 -- pyxcp/cxx/pool.hpp | 81 --- pyxcp/cxx/poolmgr.cpp | 6 - pyxcp/cxx/poolmgr.hpp | 31 - pyxcp/cxx/test_queue.cpp | 69 --- pyxcp/cxx/timestamp.hpp | 84 --- pyxcp/cxx/utils.cpp | 38 -- pyxcp/cxx/utils.hpp | 29 - pyxcp/cxx/win/iocp.cpp | 242 -------- pyxcp/cxx/win/iocp.hpp | 42 -- pyxcp/cxx/win/perhandledata.hpp | 24 - pyxcp/cxx/win/periodata.hpp | 97 --- pyxcp/cxx/win/socket.hpp | 185 ------ pyxcp/cxx/win/timeout.hpp | 83 --- pyxcp/transport/cxx_ext/CMakeLists.txt | 50 -- pyxcp/transport/cxx_ext/setup.py | 48 -- .../cxx_ext/tests/test_basic_socket.cpp | 39 -- pyxcp/transport/cxx_ext/tests/test_pool.cpp | 39 -- .../cxx_ext/tests/test_timestamp.cpp | 27 - setup.cfg | 26 - setup.py | 187 ------ 38 files changed, 2856 deletions(-) delete mode 100644 .gitmodules delete mode 100644 .pdm.toml delete mode 100644 pyxcp/cxx/asynchiofactory.hpp delete mode 100644 pyxcp/cxx/blocking_client.cpp delete mode 100644 pyxcp/cxx/blocking_socket.cpp delete mode 100644 pyxcp/cxx/blocking_socket.hpp delete mode 100644 pyxcp/cxx/concurrent_queue.hpp delete mode 100644 pyxcp/cxx/eth.hpp delete mode 100644 pyxcp/cxx/exceptions.hpp delete mode 100644 pyxcp/cxx/iasyncioservice.hpp delete mode 100644 pyxcp/cxx/iresource.hpp delete mode 100644 pyxcp/cxx/isocket.hpp delete mode 100644 pyxcp/cxx/linux/epoll.cpp delete mode 100644 pyxcp/cxx/linux/epoll.hpp delete mode 100644 pyxcp/cxx/linux/lit_tester.cpp delete mode 100644 pyxcp/cxx/linux/socket.hpp delete mode 100644 pyxcp/cxx/linux/timeout.hpp delete mode 100644 pyxcp/cxx/memoryblock.hpp delete mode 100644 pyxcp/cxx/pool.hpp delete mode 100644 pyxcp/cxx/poolmgr.cpp delete mode 100644 pyxcp/cxx/poolmgr.hpp delete mode 100644 pyxcp/cxx/test_queue.cpp delete mode 100644 pyxcp/cxx/timestamp.hpp delete mode 100644 pyxcp/cxx/utils.cpp delete mode 100644 pyxcp/cxx/utils.hpp delete mode 100644 pyxcp/cxx/win/iocp.cpp delete mode 100644 pyxcp/cxx/win/iocp.hpp delete mode 100644 pyxcp/cxx/win/perhandledata.hpp delete mode 100644 pyxcp/cxx/win/periodata.hpp delete mode 100644 pyxcp/cxx/win/socket.hpp delete mode 100644 pyxcp/cxx/win/timeout.hpp delete mode 100644 pyxcp/transport/cxx_ext/CMakeLists.txt delete mode 100644 pyxcp/transport/cxx_ext/setup.py delete mode 100644 pyxcp/transport/cxx_ext/tests/test_basic_socket.cpp delete mode 100644 pyxcp/transport/cxx_ext/tests/test_pool.cpp delete mode 100644 pyxcp/transport/cxx_ext/tests/test_timestamp.cpp delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 6944698..0000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "pybind11"] - path = pybind11 - url = https://github.com/pybind/pybind11.git - branch = master diff --git a/.pdm.toml b/.pdm.toml deleted file mode 100644 index 09a9763..0000000 --- a/.pdm.toml +++ /dev/null @@ -1,25 +0,0 @@ -[python] -auto_global = false -build_isolation = false -check_update = false -parallel_install = true -project_max_depth = 5 -use_venv = false -path = "/usr/bin/python" - -[python.feature] -install_cache = true -install_cache_method = "symlink" - -[python.pypi] -json_api = true -url = "https://pypi.org/simple" -verify_ssl = false - -[python.python] -use_pyenv = false - -[python.strategy] -resolve_max_rounds = 10 -save = "minimum" -update = "reuse" diff --git a/pyxcp/cxx/asynchiofactory.hpp b/pyxcp/cxx/asynchiofactory.hpp deleted file mode 100644 index 9e94816..0000000 --- a/pyxcp/cxx/asynchiofactory.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#if !defined(__ASYNCHIOFACTORY_HPP) -#define __ASYNCHIOFACTORY_HPP - -#include - -#include "iasyncioservice.hpp" - -#if defined(_WIN32) - #include "iocp.hpp" -#else - #include "epoll.hpp" -#endif - - -inline std::unique_ptr createAsyncIoService() -{ -#if defined(_WIN32) - return std::make_unique(); -#else - return std::make_unique(); -#endif -} - -#endif // __ASYNCHIOFACTORY_HPP diff --git a/pyxcp/cxx/blocking_client.cpp b/pyxcp/cxx/blocking_client.cpp deleted file mode 100644 index f4e1a7d..0000000 --- a/pyxcp/cxx/blocking_client.cpp +++ /dev/null @@ -1,44 +0,0 @@ - - -#include "blocking_socket.hpp" - -#include -#include - -using std::cout; -using std::endl; -using std::setw; -using std::internal; -using std::fixed; -using std::setfill; - -using namespace std; - -std::array hellomsg {"hello world!!!"}; - -int main(void) -{ - CAddress address; - auto sock = Socket {PF_INET, SOCK_STREAM, IPPROTO_TCP}; - - - //sock.getaddrinfo(PF_INET, SOCK_STREAM, IPPROTO_TCP, "localhost", 50007, address, 0); - sock.getaddrinfo(PF_INET, SOCK_STREAM, IPPROTO_TCP, "192.168.168.100", 50007, address, 0); - sock.connect(address); - - //auto opt_val = sock.get_option(SO_REUSEADDR, SOL_SOCKET); - auto opt_val = sock.get_option(SO_RCVBUF, SOL_SOCKET); - printf("before: %u\n", opt_val); - sock.set_option(SO_SNDBUF, SOL_SOCKET, 64 * 1024); - sock.set_option(SO_RCVBUF, SOL_SOCKET, 64 * 1024); - opt_val = sock.get_option(SO_RCVBUF, SOL_SOCKET); - printf("after: %u\n", opt_val); - - - sock.startReceiverThread(); - //sock.getaddrinfo(PF_INET, SOCK_STREAM, IPPROTO_TCP, "google.de", 80, address, 0); - //printf("addr: %x", address.address); -// sock.shutdownReceiverThread(); - sock.write(hellomsg); - Sleep(2000); -} diff --git a/pyxcp/cxx/blocking_socket.cpp b/pyxcp/cxx/blocking_socket.cpp deleted file mode 100644 index 99d159a..0000000 --- a/pyxcp/cxx/blocking_socket.cpp +++ /dev/null @@ -1,43 +0,0 @@ - -#include "blocking_socket.hpp" - - -void * blockingReceiverThread(void * param) { - Socket * const socket = reinterpret_cast(param); - std::array buffer; - int nbytes; - - printf("Starting thread... [%d]\n", socket->getSocket()); - - nbytes = socket->read(buffer, 128); - - printf("[%d] bytes received.\n", nbytes); - if (nbytes) { - printf("data: [%s]\n", buffer.data()); - } - - printf("Exiting thread...\n"); - - return NULL; -} - -#include "blocking_socket.hpp" - - -[[noreturn]] void blockingReceiverThread(Socket * socket) { - //Socket * const socket = reinterpret_cast(param); - std::array buffer; - int nbytes; - - printf("Starting thread... [%d]\n", socket->getSocket()); - - while (true) { - nbytes = socket->read(buffer, 128); - - printf("[%d] bytes received.\n", nbytes); - if (nbytes) { - printf("data: [%s]\n", buffer.data()); - } - } - printf("Exiting thread...\n"); -} diff --git a/pyxcp/cxx/blocking_socket.hpp b/pyxcp/cxx/blocking_socket.hpp deleted file mode 100644 index cefa351..0000000 --- a/pyxcp/cxx/blocking_socket.hpp +++ /dev/null @@ -1,558 +0,0 @@ - -#include - -#include "utils.hpp" - -#if !defined(__BLOCKING_SOCKET_HPP) -#define __BLOCKING_SOCKET_HPP - -#if defined(_WIN32) - #include - #include - #include - #include - #include -#elif defined(__unix__) - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #define INVALID_SOCKET (-1) - #define SOCKET_ERROR (-1) - #define ADDRINFO addrinfo - #define SOCKADDR struct sockaddr - #define SOCKADDR_STORAGE sockaddr_storage - - typedef int SOCKET; -#endif - -#include - -#define ADDR_LEN sizeof(SOCKADDR_STORAGE) - -template using buffer_t = std::array; - -void * blockingReceiverThread(void * param); - -struct CAddress { - int length; - struct sockaddr address; -}; - -class Socket { - public: - - explicit Socket(int family = PF_INET, int socktype = SOCK_STREAM, int protocol = IPPROTO_TCP) : m_family(family), m_socktype(socktype), - m_protocol(protocol), m_connected(false), m_addr(nullptr), m_thread(0) { - m_socket = ::socket(m_family, m_socktype, m_protocol); - m_connected_socket = 0; - if (m_socket == INVALID_SOCKET) { - SocketErrorExit("Socket::Socket()"); - } - blocking(true); - ZeroOut(&m_peerAddress, sizeof(SOCKADDR_STORAGE)); - } - - ~Socket() { -#if defined(__unix__) - ::close(m_socket); -#elif #defined(_WIN32) - ::closesocket(m_socket); -#endif - } - - void blocking(bool enabled) { - int flags = ::fcntl(m_socket, F_GETFL); - - if (flags == -1) { - SocketErrorExit("blocking::fcntl()"); - } - flags = enabled ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); - if (::fcntl(m_socket, F_SETFL, flags) == -1) { - SocketErrorExit("blocking::fcntl()"); - } - } - - void set_option(int optname, int level, int value) { - - if (::setsockopt(m_socket, level, optname, (const char*) &value, sizeof(value)) == SOCKET_ERROR) { - SocketErrorExit("Socket::set_option()"); - } - } - - int get_option(int optname, int level) { - int value; - socklen_t len; - - len = sizeof(value); - if (::getsockopt(m_socket, level, optname, (char*) &value, &len) == SOCKET_ERROR) { - SocketErrorExit("Socket::get_option()"); - } - return value; - } - - bool get_reuse_addr() { - return static_cast(get_option(SO_REUSEADDR, SOL_SOCKET)); - } - - void set_reuse_addr(bool on) { - set_option(SO_REUSEADDR, SOL_SOCKET, static_cast(on)); - } - - int get_send_buffer_size() { - return get_option(SO_SNDBUF, SOL_SOCKET); - } - - void set_send_buffer_size(int size) { - set_option(SO_SNDBUF, SOL_SOCKET, size); - } - - int get_rcv_buffer_size() { - return get_option(SO_RCVBUF, SOL_SOCKET); - } - - void set_rcv_buffer_size(int size) { - set_option(SO_RCVBUF, SOL_SOCKET, size); - } - - bool getaddrinfo(int family, int socktype, int protocol, const char * hostname, int port, CAddress & address, int flags = AI_PASSIVE) { - int err; - ADDRINFO hints; - ADDRINFO * t_addr; - char port_str[16] = {0}; - - ZeroOut(&hints, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = socktype; - hints.ai_protocol = protocol; - hints.ai_flags = flags; - ::sprintf(port_str, "%d", port); - err = ::getaddrinfo(hostname, port_str, &hints, &t_addr); - if (err != 0) { - printf("%s\n", gai_strerror(err)); - ::freeaddrinfo(t_addr); - SocketErrorExit("getaddrinfo()"); - return false; - } - address.length = t_addr->ai_addrlen; - CopyMemory(&address.address, t_addr->ai_addr, sizeof(SOCKADDR)); - ::freeaddrinfo(t_addr); - return true; - } - - void connect(CAddress & address) { - if (::connect(m_socket, &address.address, address.length) == SOCKET_ERROR) { - SocketErrorExit("Socket::connect()"); - } - m_connected_socket = m_socket; - printf("Sock-conn: %d\n", m_connected_socket); - } - - void bind(CAddress & address) { - if (::bind(m_socket, &address.address, address.length) == SOCKET_ERROR) { - SocketErrorExit("Socket::bind()"); - } - } - - void listen(int backlog = 1) { - if (::listen(m_socket, backlog) == SOCKET_ERROR) { - SocketErrorExit("Socket::listen()"); - } - } - - void accept(CAddress & peerAddress) { - - peerAddress.length = sizeof peerAddress.address; - m_connected_socket = ::accept(m_socket, (SOCKADDR *)&peerAddress.address, (socklen_t*)&peerAddress.length); - - if (m_connected_socket == INVALID_SOCKET) { - SocketErrorExit("Socket::accept()"); - } - } - - void startReceiverThread() { - int res = 0; - - res = ::pthread_create(&m_thread, NULL, blockingReceiverThread, this); - if (res == -1) { - OsErrorExit("startReceiverThread::pthread_create"); - } - } - - void shutdownReceiverThread() { - int res = 0; - - res = ::pthread_kill(m_thread, SIGINT); - if (res == -1) { - OsErrorExit("shutdownReceiverThread::pthread_kill"); - } - res = pthread_join(m_thread, NULL); - if (res == -1) { - OsErrorExit("shutdownReceiverThread::pthread_join"); - } - } - - template - int read(std::array& arr, size_t len) { - int nbytes; - - nbytes = ::recv(m_connected_socket, (char*)arr.data(), len, 0); - if (nbytes == -1) { - OsErrorExit("read::recv"); - } - - return nbytes; - } - - template - void write(std::array& arr) { - if (m_socktype == SOCK_DGRAM) { -#if 0 - if (sendto(m_socket, (char const *)arr.data(), arr.size(), 0, - (SOCKADDR * )(SOCKADDR_STORAGE const *)&XcpTl_Connection.connectionAddress, ADDR_LEN) == SOCKET_ERROR) { - SocketErrorExit("send::sendto()"); - } -#endif - } else if (m_socktype == SOCK_STREAM) { - if (::send(m_connected_socket, (char const *)arr.data(), arr.size(), 0) == SOCKET_ERROR) { - SocketErrorExit("send::send()"); -#if defined(_WIN32) - closesocket(m_connected_socket); -#elif defined(__unix__) - close(m_connected_socket); -#endif - } - } - } - - SOCKET getSocket() const { - return m_socket; - } - -protected: - - -private: - int m_family; - int m_socktype; - int m_protocol; - bool m_connected; - addrinfo * m_addr; - pthread_t m_thread; - //TimeoutTimer m_timeout {150}; - - SOCKET m_socket; - SOCKET m_connected_socket; - //CAddress ourAddress; - SOCKADDR_STORAGE m_peerAddress; -}; - - -#endif // __BLOCKING_SOCKET_HPP - -/* - * pyXCP - * - * (C) 2021 by Christoph Schueler - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * s. FLOSS-EXCEPTION.txt - */ - - -#include -#include -#include - -#include "utils.hpp" - -#if !defined(__BLOCKING_SOCKET_HPP) -#define __BLOCKING_SOCKET_HPP - -#if defined(_WIN32) - #include - #include - #include - #include - #include -#elif defined(__unix__) - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #define INVALID_SOCKET (-1) - #define SOCKET_ERROR (-1) - #define ADDRINFO addrinfo - #define SOCKADDR struct sockaddr - #define SOCKADDR_STORAGE sockaddr_storage - - typedef int SOCKET; -#endif - -#include - -#define ADDR_LEN sizeof(SOCKADDR_STORAGE) - -template using buffer_t = std::array; - -class Socket; - -[[noreturn]] void blockingReceiverThread(Socket * socket); - -struct CAddress { - int length; - struct sockaddr address; -}; - -class Socket { -public: - - using listen_thread_t = std::function; - - explicit Socket(int family = PF_INET, int socktype = SOCK_STREAM, int protocol = IPPROTO_TCP) : m_family(family), m_socktype(socktype), - m_protocol(protocol), m_connected(false), m_addr(nullptr) { - m_socket = ::socket(m_family, m_socktype, m_protocol); - m_connected_socket = 0; - if (m_socket == INVALID_SOCKET) { - SocketErrorExit("Socket::Socket()"); - } - blocking(true); - ZeroOut(&m_peerAddress, sizeof(SOCKADDR_STORAGE)); - } - - ~Socket() { -#if defined(__unix__) - ::close(m_socket); -#elif #defined(_WIN32) - ::closesocket(m_socket); -#endif - } - - void blocking(bool enabled) { - int flags = ::fcntl(m_socket, F_GETFL); - - if (flags == -1) { - SocketErrorExit("blocking::fcntl()"); - } - flags = enabled ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); - if (::fcntl(m_socket, F_SETFL, flags) == -1) { - SocketErrorExit("blocking::fcntl()"); - } - } - - void set_option(int optname, int level, int value) { - - if (::setsockopt(m_socket, level, optname, (const char*) &value, sizeof(value)) == SOCKET_ERROR) { - SocketErrorExit("Socket::set_option()"); - } - } - - int get_option(int optname, int level) { - int value; - socklen_t len; - - len = sizeof(value); - if (::getsockopt(m_socket, level, optname, (char*) &value, &len) == SOCKET_ERROR) { - SocketErrorExit("Socket::get_option()"); - } - return value; - } - - bool get_reuse_addr() { - return static_cast(get_option(SO_REUSEADDR, SOL_SOCKET)); - } - - void set_reuse_addr(bool on) { - set_option(SO_REUSEADDR, SOL_SOCKET, static_cast(on)); - } - - int get_send_buffer_size() { - return get_option(SO_SNDBUF, SOL_SOCKET); - } - - void set_send_buffer_size(int size) { - set_option(SO_SNDBUF, SOL_SOCKET, size); - } - - int get_rcv_buffer_size() { - return get_option(SO_RCVBUF, SOL_SOCKET); - } - - void set_rcv_buffer_size(int size) { - set_option(SO_RCVBUF, SOL_SOCKET, size); - } - - bool getaddrinfo(int family, int socktype, int protocol, const char * hostname, int port, CAddress & address, int flags = AI_PASSIVE) { - int err; - ADDRINFO hints; - ADDRINFO * t_addr; - char port_str[16] = {0}; - - ZeroOut(&hints, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = socktype; - hints.ai_protocol = protocol; - hints.ai_flags = flags; - ::sprintf(port_str, "%d", port); - err = ::getaddrinfo(hostname, port_str, &hints, &t_addr); - if (err != 0) { - printf("%s\n", gai_strerror(err)); - ::freeaddrinfo(t_addr); - SocketErrorExit("getaddrinfo()"); - return false; - } - address.length = t_addr->ai_addrlen; - CopyMemory(&address.address, t_addr->ai_addr, sizeof(SOCKADDR)); - ::freeaddrinfo(t_addr); - return true; - } - - void connect(CAddress & address) { - if (::connect(m_socket, &address.address, address.length) == SOCKET_ERROR) { - SocketErrorExit("Socket::connect()"); - } - m_connected_socket = m_socket; - printf("Sock-conn: %d\n", m_connected_socket); - } - - void bind(CAddress & address) { - if (::bind(m_socket, &address.address, address.length) == SOCKET_ERROR) { - SocketErrorExit("Socket::bind()"); - } - } - - void listen(int backlog = 1) { - if (::listen(m_socket, backlog) == SOCKET_ERROR) { - SocketErrorExit("Socket::listen()"); - } - } - - void accept(CAddress & peerAddress) { - - peerAddress.length = sizeof peerAddress.address; - m_connected_socket = ::accept(m_socket, (SOCKADDR *)&peerAddress.address, (socklen_t*)&peerAddress.length); - - if (m_connected_socket == INVALID_SOCKET) { - SocketErrorExit("Socket::accept()"); - } - } - - void startReceiverThread() { - int res = 0; - - m_thread = new std::thread(blockingReceiverThread, this); - } - - void shutdownReceiverThread() { - int res = 0; - - //res = ::pthread_kill(m_thread->native_handle(), SIGINT); - res = ::pthread_cancel(m_thread->native_handle()); - if (res == -1) { - OsErrorExit("shutdownReceiverThread::pthread_kill"); - } - m_thread->join(); - delete m_thread; - } - - template - int read(std::array& arr, size_t len) { - int nbytes; - - nbytes = ::recv(m_connected_socket, (char*)arr.data(), len, 0); - if (nbytes == -1) { - OsErrorExit("read::recv"); - } - - return nbytes; - } - - template - void write(std::array& arr) { - if (m_socktype == SOCK_DGRAM) { -#if 0 - if (sendto(m_socket, (char const *)arr.data(), arr.size(), 0, - (SOCKADDR * )(SOCKADDR_STORAGE const *)&XcpTl_Connection.connectionAddress, ADDR_LEN) == SOCKET_ERROR) { - SocketErrorExit("send::sendto()"); - } -#endif - } else if (m_socktype == SOCK_STREAM) { - if (::send(m_connected_socket, (char const *)arr.data(), arr.size(), 0) == SOCKET_ERROR) { - SocketErrorExit("send::send()"); -#if defined(_WIN32) - closesocket(m_connected_socket); -#elif defined(__unix__) - close(m_connected_socket); -#endif - } - } - } - - SOCKET getSocket() const { - return m_socket; - } - -protected: - - -private: - int m_family; - int m_socktype; - int m_protocol; - bool m_connected; - addrinfo * m_addr; - std::thread * m_thread = nullptr; - //TimeoutTimer m_timeout {150}; - - SOCKET m_socket; - SOCKET m_connected_socket; - //CAddress ourAddress; - SOCKADDR_STORAGE m_peerAddress; -}; - - -#endif // __BLOCKING_SOCKET_HPP diff --git a/pyxcp/cxx/concurrent_queue.hpp b/pyxcp/cxx/concurrent_queue.hpp deleted file mode 100644 index 6fb087a..0000000 --- a/pyxcp/cxx/concurrent_queue.hpp +++ /dev/null @@ -1,60 +0,0 @@ - -#if !defined(__CONCURRENT_QUEUE) -#define __CONCURRENT_QUEUE - -#include -#include -#include -#include -#include - -template class ConcurrentQueue { -public: - - explicit ConcurrentQueue<_Ty>() = default; - - ConcurrentQueue<_Ty>(const ConcurrentQueue<_Ty>& other) noexcept : - m_elements(other.m_elements), m_mtx(), m_cond() - {} - - bool empty() const { - std::unique_lock lock(m_mtx); - - return m_elements.empty(); - } - - void enqueue(const _Ty& item) { - std::lock_guard lock(m_mtx); - bool const empty = m_elements.empty(); - - m_elements.emplace_back(std::move(item)); - m_mtx.unlock(); - - if (empty) { - m_cond.notify_one(); - } - - } - - bool dequeue(_Ty& item, uint32_t timeout = 50) { - std::unique_lock lock(m_mtx); - - while (m_elements.empty()) { - if (m_cond.wait_for(lock, std::chrono::milliseconds(timeout)) == std::cv_status::timeout) { - return false; // Wait timed out. - } - } - - item = std::move(m_elements.front()); - m_elements.pop_front(); - return true; - } - -private: - //std::queue<_Ty> m_elements {}; - std::deque<_Ty> m_elements {}; - mutable std::mutex m_mtx {}; - std::condition_variable m_cond {}; -}; - -#endif // __CONCURRENT_QUEUE diff --git a/pyxcp/cxx/eth.hpp b/pyxcp/cxx/eth.hpp deleted file mode 100644 index 6f9b0ea..0000000 --- a/pyxcp/cxx/eth.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#if !defined(__ETH_HPP) -#define __ETH_HPP - -#if defined(_WIN32) - #include -#else - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -#endif - -#include "config.h" -#include "utils.hpp" - -#include - -#if defined(_WIN32) - -struct Eth { - - Eth() { - WSAData data; - if (::WSAStartup(MAKEWORD(2, 2), &data) != 0) { - OsErrorExit("Eth::Eth() -- WSAStartup"); - } - } - - ~Eth() { - ::WSACleanup(); - } -}; - -#else - -struct Eth { - - Eth() = default; - ~Eth() = default; -}; - -#endif - -#endif // __ETH_HPP diff --git a/pyxcp/cxx/exceptions.hpp b/pyxcp/cxx/exceptions.hpp deleted file mode 100644 index 934d906..0000000 --- a/pyxcp/cxx/exceptions.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#if !defined(__EXCEPTIONS_HPP) -#define __EXCEPTIONS_HPP - -#include - - -struct OSException : public std::exception { - const char * what () const throw() { - return "OS Exception"; - } -}; - -struct TimeoutException : public std::exception { - const char * what () const throw() { - return "Timeout Exception"; - } -}; - -struct CapacityExhaustedException : public std::exception { - const char * what () const throw() { - return "Capacity Exhausted Exception"; - } -}; - -struct InvalidObjectException : public std::exception { - const char * what () const throw() { - return "Invalid Object Exception"; - } -}; -#endif // __EXCEPTIONS_HPP diff --git a/pyxcp/cxx/iasyncioservice.hpp b/pyxcp/cxx/iasyncioservice.hpp deleted file mode 100644 index 396d989..0000000 --- a/pyxcp/cxx/iasyncioservice.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * - * Interface for asynchronous I/O services (IOCP, epoll, kqueue...). - * - * - */ - -#if !defined(__IASYNCHIOSERVICE_HPP) -#define __IASYNCHIOSERVICE_HPP - -#include -#include - -#include "socket.hpp" - -enum class MessageCode : uint64_t { - QUIT, - TIMEOUT -}; - -class IAsyncIoService { -public: - virtual ~IAsyncIoService() = default; - virtual void registerSocket(Socket& socket) = 0; - virtual void postUserMessage(MessageCode messageCode, void * data = nullptr) const = 0; - virtual void postQuitMessage() const = 0; - virtual HANDLE getHandle() const = 0; - -}; - -#endif // __IASYNCHIOSERVICE_HPP diff --git a/pyxcp/cxx/iresource.hpp b/pyxcp/cxx/iresource.hpp deleted file mode 100644 index 273ae13..0000000 --- a/pyxcp/cxx/iresource.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#if !defined(__IRESOURCE_HPP) -#define __IRESOURCE_HPP - -/* - * - * Interface for pool-able resources. - * - */ -class IResource { -public: - - virtual ~IResource() = default; - virtual void reset() = 0; - -}; - -#endif // __IRESOURCE_HPP diff --git a/pyxcp/cxx/isocket.hpp b/pyxcp/cxx/isocket.hpp deleted file mode 100644 index 8d79b00..0000000 --- a/pyxcp/cxx/isocket.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#if !defined(__ISOCKET_HPP) -#define __ISOCKET_HPP - -struct CAddress { - int length; - struct sockaddr address; -}; - - -class ISocket { -public: - ~ISocket() = default; - - virtual void connect(CAddress & address) = 0; - virtual void bind(CAddress & address) = 0; - virtual void listen(int backlog = 10) = 0; - virtual void accept(CAddress & peerAddress) = 0; - virtual void option(int optname, int level, int * value) = 0; - virtual bool getaddrinfo(int family, int socktype, int protocol, const char * hostname, int port, CAddress & address, int flags = AI_PASSIVE) = 0; -}; - -#endif // __ISOCKET_HPP diff --git a/pyxcp/cxx/linux/epoll.cpp b/pyxcp/cxx/linux/epoll.cpp deleted file mode 100644 index 31888bf..0000000 --- a/pyxcp/cxx/linux/epoll.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "epoll.hpp" - -constexpr size_t MAX_EVENTS = 8; - -static struct epoll_event events[MAX_EVENTS]; - -void * WorkerThread(void * param) -{ - Epoll const * const epoll = reinterpret_cast(param); - Socket const * socket; - TimeoutTimer const * timeout_timer; - EventRecord * event_record; - int nfds; - int idx; - char buffer[128]; - int evt_mask; - uint64_t timeout_value; - - printf("Entering worker thread...\n"); - - for (;;) { - nfds = epoll_wait(epoll->getHandle() ,events, MAX_EVENTS, 500); - for (idx = 0; idx < nfds; ++idx) { - evt_mask = events[idx].events; - event_record = reinterpret_cast(events[idx].data.ptr); - printf("Evt#%d: %x %d\n", idx, evt_mask, event_record->event_type); - if (event_record->event_type == EventType::SOCKET) { - socket = event_record->obj.socket; - printf("Socket-Handle: %d\n", socket->getHandle()); - if (evt_mask & EPOLLIN) { - read(socket->getHandle(), buffer, 128); - printf("R: %s\n", buffer); - } else if (evt_mask & EPOLLHUP) { - printf("HANG-UP\n"); - //SocketErrorExit("HANG-UP"); - } else if (evt_mask & EPOLLERR) { - SocketErrorExit("WorkerThread::epoll_wait()"); - } - } else if (event_record->event_type == EventType::TIMEOUT) { - timeout_timer = event_record->obj.timeout_timer; - printf("Timeout-Handle: %d\n", timeout_timer->getHandle()); - read(timeout_timer->getHandle(), &timeout_value, sizeof(uint64_t)); - printf("Timeout\n"); - } else { - printf("Invalid event type.\n"); - } - } - } - - return nullptr; -} diff --git a/pyxcp/cxx/linux/epoll.hpp b/pyxcp/cxx/linux/epoll.hpp deleted file mode 100644 index 3fbb242..0000000 --- a/pyxcp/cxx/linux/epoll.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#if !defined(__EPOLL_HPP) -#define __EPOLL_HPP - -#include -#include - -#include -#include -#include - -#include "socket.hpp" -#include "iasyncioservice.hpp" - -void * WorkerThread(void * param); - -enum class EventType { - SOCKET, - TIMEOUT -}; - -struct EventRecord { - EventType event_type; - union { - Socket const * socket; - TimeoutTimer const * timeout_timer; - } obj; -}; - -class Epoll : public IAsyncIoService { -public: - Epoll(size_t numProcessors = 1, size_t multiplier = 1) { - int ret; - - m_epoll_fd = ::epoll_create(42); - ret = pthread_create(&m_worker_thread, nullptr, &WorkerThread, reinterpret_cast(this)); - if (ret != 0) { - OsErrorExit("Epoll:Epoll() -- Create worker thread"); - } - } - - ~Epoll() { - ::close(m_epoll_fd); - } - - void registerSocket(Socket& socket) { - - registerHandle(socket.getHandle(), reinterpret_cast(&socket), EventType::SOCKET); - registerHandle(socket.getTimeout().getHandle(), reinterpret_cast(&socket.getTimeout()), EventType::TIMEOUT); - printf("S: %d T: %d\n", socket.getHandle(), socket.getTimeout().getHandle()); - } - - void postUserMessage(MessageCode messageCode, void * data = nullptr) const {} - void postQuitMessage() const {} - - HANDLE getHandle() const { - return m_epoll_fd; - } - -protected: - - void registerHandle(HANDLE handle, void const * data_ptr, EventType event_type) { - - struct epoll_event event; - auto event_record = std::make_shared(); - m_events.emplace_back(event_record); - - event_record->event_type = event_type; - if (event_type == EventType::SOCKET) { - event_record->obj.socket = reinterpret_cast(data_ptr); - } else if (event_type == EventType::TIMEOUT) { - event_record->obj.timeout_timer = static_cast(data_ptr); - } - event.data.ptr = event_record.get(); - event.events = EPOLLIN; - if (::epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, handle, &event) == -1) { - OsErrorExit("Epoll::registerHandle()"); - } - } - -private: - int m_epoll_fd; - pthread_t m_worker_thread; - std::vector> m_events; -}; - - -#endif // __EPOLL_HPP diff --git a/pyxcp/cxx/linux/lit_tester.cpp b/pyxcp/cxx/linux/lit_tester.cpp deleted file mode 100644 index d5a88a7..0000000 --- a/pyxcp/cxx/linux/lit_tester.cpp +++ /dev/null @@ -1,19 +0,0 @@ - -#include -#include - -#include - -using namespace std::literals; -using namespace std; - - -int main() -{ -// cout << static_cast(23ms) << endl; - auto d1 = 250ns; - - std::chrono::nanoseconds d2 = 1us; - std::cout << "250ns = " << d1.count() << " nanoseconds\n" << "1us = " << d2.count() << " nanoseconds\n"; - -} diff --git a/pyxcp/cxx/linux/socket.hpp b/pyxcp/cxx/linux/socket.hpp deleted file mode 100644 index e63cf43..0000000 --- a/pyxcp/cxx/linux/socket.hpp +++ /dev/null @@ -1,234 +0,0 @@ -#if !defined(__SOCKET_HPP) -#define __SOCKET_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "isocket.hpp" -#include "utils.hpp" -#include "timeout.hpp" - -#define SOCKET_ERROR (-1) -#define INVALID_SOCKET (-1) - -using HANDLE = int; -using SOCKET = int; - -class Socket : public ISocket { -public: - - Socket(int family = PF_INET, int socktype = SOCK_STREAM, int protocol = IPPROTO_TCP) : - m_family(family), m_socktype(socktype), m_protocol(protocol), m_connected(false), - m_addr(nullptr), m_timeout(150) { - m_socket = ::socket(m_family, m_socktype, m_protocol); - if (m_socket == INVALID_SOCKET) { - SocketErrorExit("Socket::Socket()"); - } - blocking(false); - ZeroOut(&m_peerAddress, sizeof(sockaddr_storage)); - } - - ~Socket() { - ::close(m_socket); - } - - void blocking(bool enabled) { - int flags = fcntl(m_socket, F_GETFL); - - if (flags == -1) { - SocketErrorExit("Socket::blocking()"); - } - flags = enabled ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); - if (fcntl(m_socket, F_SETFL, flags) == -1) { - SocketErrorExit("Socket::blocking()"); - } - } - - void option(int optname, int level, int * value) { - socklen_t len; - - len = sizeof(*value); - if (*value == 0) { - ::getsockopt(m_socket, level, optname, (char*) value, &len); - } else { - ::setsockopt(m_socket, level, optname, (const char*) value, len); - } - } - - bool getaddrinfo(int family, int socktype, int protocol, const char * hostname, int port, CAddress & address, int flags = AI_PASSIVE) { - int err; - addrinfo hints; - addrinfo * t_addr; - char port_str[16] = {0}; - - ZeroOut(&hints, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = socktype; - hints.ai_protocol = protocol; - hints.ai_flags = flags; - - ::sprintf(port_str, "%d", port); - err = ::getaddrinfo(hostname, port_str, &hints, &t_addr); - if (err != 0) { - printf("%s\n", gai_strerror(err)); - ::freeaddrinfo(t_addr); - SocketErrorExit("getaddrinfo()"); - return false; - } - - address.length = t_addr->ai_addrlen; - ::memcpy(&address.address, t_addr->ai_addr, sizeof(struct sockaddr)); - - ::freeaddrinfo(t_addr); - return true; - } - - void connect(CAddress & address) { - blocking(true); - if (::connect(m_socket, &address.address, address.length) == SOCKET_ERROR) { - if (errno != EINPROGRESS) { - SocketErrorExit("Socket::connect()"); - } - } - blocking(false); - } - - void bind(CAddress & address) { - if (::bind(m_socket, &address.address, address.length) == SOCKET_ERROR) { - SocketErrorExit("Socket::bind()"); - } - } - - void listen(int backlog = 5) { - if (::listen(m_socket, backlog) == SOCKET_ERROR) { - SocketErrorExit("Socket::listen()"); - } - } - - void accept(CAddress & peerAddress) { - int sock; - - peerAddress.length = sizeof peerAddress.address; - sock = ::accept(m_socket, (sockaddr *)&peerAddress.address, (socklen_t*)&peerAddress.length); - - if (sock == INVALID_SOCKET) { - SocketErrorExit("Socket::accept()"); - } - } - - template - void write(std::array& arr, bool alloc = true) { - size_t bytesWritten = 0; - int addrLen; - - m_timeout.arm(); - - if (m_socktype == SOCK_DGRAM) { -#if 0 - if (sendto(m_socket, (char const *)arr.data(), arr.size(), 0, (struct sockaddr const *)&XcpTl_Connection.connectionAddress, addrSize) == -1) { - SocketErrorExit("Socket::write() -- sendto()"); - } -#endif - } else if (m_socktype == SOCK_STREAM) { - if (send(m_socket, (char const *)arr.data(), arr.size(), 0) == -1) { - SocketErrorExit("Socket::write() -- send()"); - } - } -#if 0 - //PerIoData * iod = new PerIoData(128); - PerIoData * iod; - - if (alloc == true) { - iod = m_pool_mgr.get_iod().acquire(); - //iod = m_iod_pool.acquire(); - } - iod->reset(); - iod->set_buffer(arr); - iod->set_opcode(IoType::IO_WRITE); - iod->set_transfer_length(arr.size()); - if (m_socktype == SOCK_DGRAM) { - addrLen = sizeof(SOCKADDR_STORAGE); - if (::WSASendTo(m_socket, - iod->get_buffer(), - 1, - &bytesWritten, - 0, - (LPSOCKADDR)&m_peerAddress, - addrLen, - (LPWSAOVERLAPPED)iod, - nullptr - ) == SOCKET_ERROR) { - // WSA_IO_PENDING - SocketErrorExit("Socket::send()"); - } - } else if (m_socktype == SOCK_STREAM) { - if (::WSASend( - m_socket, - iod->get_buffer(), - 1, - &bytesWritten, - 0, - (LPWSAOVERLAPPED)iod, - nullptr) == SOCKET_ERROR) { - SocketErrorExit("Socket::send()"); - closesocket(m_socket); - } - } -#endif - printf("Status: %d bytes_written: %d\n", errno, bytesWritten); - } -#if 0 - void read(size_t count) { - if ( (n = read(sockfd, line, MAXLINE)) < 0) { - if (errno == ECONNRESET) { - close(sockfd); - events[i].data.fd = -1; - } else printf("readline error\n"); - } else if (n == 0) { - close(sockfd); - events[i].data.fd = -1; - } - - } -#endif - - void triggerRead(unsigned int len); - - HANDLE getHandle() const { - return m_socket; - } - - const TimeoutTimer& getTimeout() const { - return m_timeout; - } - -private: - int m_family; - int m_socktype; - int m_protocol; - bool m_connected; -// PoolManager m_pool_mgr; - addrinfo * m_addr; - TimeoutTimer m_timeout {150}; - int m_socket; - //CAddress ourAddress; - sockaddr_storage m_peerAddress; -}; - -#endif // __SOCKET_HPP diff --git a/pyxcp/cxx/linux/timeout.hpp b/pyxcp/cxx/linux/timeout.hpp deleted file mode 100644 index 4dfc2be..0000000 --- a/pyxcp/cxx/linux/timeout.hpp +++ /dev/null @@ -1,81 +0,0 @@ - -#if !defined(__TIMEOUT_HPP) -#define __TIMEOUT_HPP - -#include -#include -#include -#include -#include -#include - -#include "utils.hpp" - -#include - -using namespace std::literals; - -/* - * - * Implements a file descriptor based time-out. - * - * Resolution is milli-seconds. - * - * Could be used together with poll(), epoll(), or select(). - * - */ - -class TimeoutTimer { -public: - - explicit TimeoutTimer(uint64_t value) : m_millis(value), m_timer_fd(-1) { - m_timer_fd = ::timerfd_create(CLOCK_MONOTONIC, 0); - if (m_timer_fd == -1) - OsErrorExit("TimeoutTimer::TimeoutTimer() -- timerfd_create"); - } - - ~TimeoutTimer() { - ::close(m_timer_fd); - } - - void arm() { - struct itimerspec new_value {0}; - - new_value.it_interval = {0}; - new_value.it_value.tv_sec = m_millis / 1000; - new_value.it_value.tv_nsec = (m_millis % 1000) * (1000 * 1000); - - settime(new_value); - } - - void disarm() { - struct itimerspec new_value {0}; - - settime(new_value); - } - - int getHandle() const { - return m_timer_fd; - } - - uint64_t getValue() const { - return m_millis; - } - - void setValue(uint64_t new_millis) { - m_millis = new_millis; - } - -private: - - void settime(const itimerspec& new_value) { - if (::timerfd_settime(m_timer_fd, 0, &new_value, nullptr) == -1) { - OsErrorExit("TimeoutTimer::disarm() -- timerfd_settime"); - } - } - - uint64_t m_millis; - int m_timer_fd; -}; - -#endif // __TIMEOUT_HPP diff --git a/pyxcp/cxx/memoryblock.hpp b/pyxcp/cxx/memoryblock.hpp deleted file mode 100644 index 0fc36c3..0000000 --- a/pyxcp/cxx/memoryblock.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#if !defined(__MEMORYBLOCK_HPP) -#define __MEMORYBLOCK_HPP - -#include "iresource.hpp" - -/* - * - * Fixed size memory block. - * - */ -template class MemoryBlock : IResource { - -public: - - explicit MemoryBlock() : m_memory(nullptr) { - m_memory = new T[N]; - //printf("MemBlock-ctor: %p\n", m_memory); - } - - ~MemoryBlock() { - //printf("MemoryBlock-dtor: %p\n", m_memory); - if (m_memory) { - delete[] m_memory; - } - } - - T * data() { - return m_memory; - } - - void reset() { - #if !defined(NDEBUG) - - #endif - } - -private: - T * m_memory; - -}; - -#endif // __MEMORYBLOCK_HPP diff --git a/pyxcp/cxx/pool.hpp b/pyxcp/cxx/pool.hpp deleted file mode 100644 index 40a0e14..0000000 --- a/pyxcp/cxx/pool.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#if !defined(__POOL_H) -#define __POOL_H - -#include -#include -#include -#include -#include - -#include "exceptions.hpp" - - -/* - * - * Fixed-size generic resource pool. - * - */ - -template void dump(std::deque& list) { - - for (auto elem: list) { - printf("%p ", elem); - } - printf("\n"); -} - -template class Pool { -public: - - explicit Pool() : m_mtx(), m_high_water_mark(N), m_allocation_count(0) { - for (size_t i = 0; i < N; ++i) { - m_free_objs.push_back(new Obj()); - } - } - - ~Pool() noexcept { - for (auto elem: m_used_objs) { - delete elem; - } - for (auto elem: m_free_objs) { - delete elem; - } - } - - Obj * acquire() { - const std::lock_guard lock(m_mtx); - if (m_free_objs.empty()) { - throw CapacityExhaustedException(); - } - auto obj = m_free_objs.front(); - m_free_objs.pop_front(); - m_used_objs.push_back(obj); - //printf("ACQ %p\n", obj); - return obj; - } - - void release(Obj * obj) - { - const std::lock_guard lock(m_mtx); - //printf("REL: %p\n", obj); - auto iter = std::find(std::begin(m_used_objs), std::end(m_used_objs), obj); - auto found = iter != std::end(m_used_objs); - if (found) { - obj->reset(); - m_free_objs.push_front(obj); - m_used_objs.erase(iter); - } else { - throw InvalidObjectException(); - } - } - -private: - - std::mutex m_mtx; - size_t m_high_water_mark; - size_t m_allocation_count; - std::deque m_used_objs; - std::deque m_free_objs; -}; - -#endif // __POOL_H diff --git a/pyxcp/cxx/poolmgr.cpp b/pyxcp/cxx/poolmgr.cpp deleted file mode 100644 index ca5467b..0000000 --- a/pyxcp/cxx/poolmgr.cpp +++ /dev/null @@ -1,6 +0,0 @@ - -#include "poolmgr.hpp" - - - -PoolManager::IodPool_t PoolManager::m_iod_pool; // Initialization of static member. diff --git a/pyxcp/cxx/poolmgr.hpp b/pyxcp/cxx/poolmgr.hpp deleted file mode 100644 index a8c9373..0000000 --- a/pyxcp/cxx/poolmgr.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#if !defined(__POOLMGR_H) -#define __POOLMGR_H - -#include "pool.hpp" -#include "periodata.hpp" - -/* - * - * PoolManager holds various resource pools. - * - * - */ - -class PoolManager { -public: - using IodPool_t = Pool; - - PoolManager() = default; - ~PoolManager() = default; - - IodPool_t& get_iod() const { - return m_iod_pool; - } - -private: - - static IodPool_t m_iod_pool; -}; - - -#endif // __POOLMGR_H diff --git a/pyxcp/cxx/test_queue.cpp b/pyxcp/cxx/test_queue.cpp deleted file mode 100644 index 687b1d1..0000000 --- a/pyxcp/cxx/test_queue.cpp +++ /dev/null @@ -1,69 +0,0 @@ - -#include - -#include - -#include -#include -#include - -#include "concurrent_queue.hpp" - -namespace py = pybind11; - -auto queue = ConcurrentQueue {}; - -using tuple_t = std::tuple; - -auto frame_queue = ConcurrentQueue {}; - -void worker(int num) -{ - printf("Entering #%u\n", num); - for (int i = 0; i < 10; ++i) { - queue.enqueue(num + i); - } -} - - -int main(int ac, char const * av[]) -{ - - auto value = 0; - auto frame = std::make_tuple(20, 1, 1.0045, "hello world!!!"); - uint16_t length, counter; - double timestamp; - py::bytes payload {}; - - frame_queue.enqueue(frame); - - std::thread t0(worker, 10); - std::thread t1(worker, 20); - std::thread t2(worker, 30); - std::thread t3(worker, 40); - std::thread t4(worker, 50); - - for (auto i = 0; i < 100; ++i) { - if (queue.dequeue(value, 1000)) { - printf("%02u\n", value); - } else { - printf("TIME-OUT!!!\n"); - break; - } - } - - t4.join(); - t3.join(); - t2.join(); - t1.join(); - t0.join(); - - tuple_t flonz; - frame_queue.dequeue(flonz); - //printf("%u %u %g\n", std::get<0>(flonz), std::get<1>(flonz), std::get<2>(flonz)); - - std::tie(length, counter, timestamp, payload) = flonz; - printf("%u %u %g\n", length, counter, timestamp); - - return 0; -} diff --git a/pyxcp/cxx/timestamp.hpp b/pyxcp/cxx/timestamp.hpp deleted file mode 100644 index 4549598..0000000 --- a/pyxcp/cxx/timestamp.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#if !defined(__TIMESTAMP_HPP) -#define __TIMESTAMP_HPP - -#include - -#if defined(_WIN32) - #include -#else - #include -#endif - -class Timestamp { -public: - -#if defined(_WIN32) - Timestamp() { - LARGE_INTEGER tps; - - ::QueryPerformanceFrequency(&tps); - m_ticks_per_second = tps.QuadPart; - m_starting_time = static_cast(get_raw_value()); - } - - double get() const { - return get_raw_value() - m_starting_time; - } -#else - Timestamp() { - struct timespec resolution = {0}; - - if (::clock_getres(CLOCK_MONOTONIC_RAW, &resolution) == -1) { - } - m_starting_time = get_raw_value(); - } - - double get() const { - struct timespec dt = {0}; - - dt = diff(m_starting_time, get_raw_value()); - return static_cast(dt.tv_sec) + (static_cast(dt.tv_nsec) / (1000.0 * 1000.0 * 1000.0)); - } -#endif - -private: - -#if defined(_WIN32) - double get_raw_value() const { - LARGE_INTEGER now; - - ::QueryPerformanceCounter(&now); - - return static_cast(now.QuadPart) / static_cast(m_ticks_per_second); - } - - double m_starting_time; - uint64_t m_ticks_per_second; -#else - struct timespec get_raw_value() const { - struct timespec now; - - if (::clock_gettime(CLOCK_MONOTONIC_RAW, &now) == -1) { - } - return now; - } - - struct timespec diff(const struct timespec& start, const struct timespec& end) const { - struct timespec temp; - - if ((end.tv_nsec-start.tv_nsec) < 0) { - temp.tv_sec = end.tv_sec-start.tv_sec - 1; - temp.tv_nsec = 1000000000L + end.tv_nsec - start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - return temp; - } - - struct timespec m_starting_time; - -#endif -}; - -#endif // __TIMESTAMP_HPP diff --git a/pyxcp/cxx/utils.cpp b/pyxcp/cxx/utils.cpp deleted file mode 100644 index 91a3e80..0000000 --- a/pyxcp/cxx/utils.cpp +++ /dev/null @@ -1,38 +0,0 @@ - -#include - -#include "utils.hpp" - -#if defined(_WIN32) - #include -#else - -#endif - -void SocketErrorExit(const char * method) -{ - fprintf(stderr, "%s failed with: %d\n", method, GET_LAST_SOCKET_ERROR()); - exit(1); -} - -void OsErrorExit(const char * method) -{ - fprintf(stderr, "%s failed with: %d\n", method, GET_LAST_ERROR()); - exit(1); -} - -#if !defined(_WIN32) -/* - * - * Window-ish Sleep function for Linux. - * - */ -void Sleep(unsigned ms) -{ - struct timespec value = {0}, rem = {0}; - - value.tv_sec = ms / 1000; - value.tv_nsec = (ms % 1000) * 1000 * 1000; - nanosleep(&value, &rem); -} -#endif diff --git a/pyxcp/cxx/utils.hpp b/pyxcp/cxx/utils.hpp deleted file mode 100644 index 584a3c4..0000000 --- a/pyxcp/cxx/utils.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#if !defined(__UTILS_HPP) -#define __UTILS_HPP - -#if defined(_WIN32) - #define ZeroOut(p, s) ::SecureZeroMemory((p), (s)) - #define GET_LAST_SOCKET_ERROR() WSAGetLastError() - #define GET_LAST_ERROR() GetLastError() -#else - #include - #include - #include - - #define ZeroOut(p, s) ::memset((p), 0, (s)) - #define CopyMemory(d, s, l) ::memcpy((d), (s), (l)) - #define GET_LAST_SOCKET_ERROR() errno - #define GET_LAST_ERROR() errno - void Sleep(unsigned ms); -#endif - -#if defined(NDEBUG) - #define DBG_PRINT(...) -#else - #define DBG_PRINT(...) printf(VA_ARGS) -#endif - -void SocketErrorExit(const char * method); -void OsErrorExit(const char * method); - -#endif // __UTILS_HPP diff --git a/pyxcp/cxx/win/iocp.cpp b/pyxcp/cxx/win/iocp.cpp deleted file mode 100644 index a9856b7..0000000 --- a/pyxcp/cxx/win/iocp.cpp +++ /dev/null @@ -1,242 +0,0 @@ - -#include "iocp.hpp" -#include "socket.hpp" -#include "exceptions.hpp" -#include "timeout.hpp" - -#include -#include -#include - - -/* - * - * - * - */ - - -static DWORD WINAPI WorkerThread(LPVOID lpParameter); - -IOCP::IOCP(size_t numProcessors, size_t multiplier) -{ - m_numWorkerThreads = numProcessors * multiplier; - m_port.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, static_cast(0), m_numWorkerThreads); - if (m_port.handle == nullptr) { - OsErrorExit("IOCP::IOCP()"); - } - - m_threads.reserve(m_numWorkerThreads); - - HANDLE hThread; - - for (DWORD idx = 0; idx < m_numWorkerThreads; ++idx) { - hThread = ::CreateThread(nullptr, 0, WorkerThread, reinterpret_cast(this), 0, nullptr); - ::SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL); - m_threads.push_back(hThread); - } -} - -IOCP::~IOCP() -{ - DWORD numThreads = static_cast(m_threads.size()); - std::ldiv_t divres = std::ldiv(numThreads, MAXIMUM_WAIT_OBJECTS); - DWORD rounds = static_cast(divres.quot); - DWORD remaining = static_cast(divres.rem); - HANDLE * thrArray = nullptr; - DWORD offset = 0; - DWORD idx = 0; - - postQuitMessage(); - - thrArray = new HANDLE[MAXIMUM_WAIT_OBJECTS]; - for (DWORD r = 0; r < rounds; ++r) { - for (idx = 0; idx < MAXIMUM_WAIT_OBJECTS; ++idx) { - thrArray[idx] = m_threads.at(idx + offset); - } - ::WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, thrArray, TRUE, INFINITE); - for (idx = 0; idx < MAXIMUM_WAIT_OBJECTS; ++idx) { - ::CloseHandle(thrArray[idx]); - } - offset += MAXIMUM_WAIT_OBJECTS; - } - - if (remaining > 0) { - for (idx = 0; idx < remaining; ++idx) { - thrArray[idx] = m_threads.at(idx + offset); - } - ::WaitForMultipleObjects(remaining, thrArray, TRUE, INFINITE); - for (idx = 0; idx < remaining; ++idx) { - ::CloseHandle(thrArray[idx]); - } - } - delete[] thrArray; - ::CloseHandle(m_port.handle); -} - -void IOCP::registerHandle(const PerHandleData& object) -{ - HANDLE handle; - bool ok; - - handle = ::CreateIoCompletionPort(object.m_handle, m_port.handle, reinterpret_cast(&object), 0); - ok = (handle == m_port.handle); - if ((handle == nullptr) || (!ok)) { - OsErrorExit("IOCP::registerHandle()"); - } -} - - -void IOCP::registerSocket(Socket& socket) -{ - auto handleData = PerHandleData(HandleType::HANDLE_SOCKET, socket.getHandle()); - - socket.setIOCP(this); - registerHandle(handleData); - -} - -void IOCP::postUserMessage(MessageCode messageCode, void * data) const -{ - if (!::PostQueuedCompletionStatus(m_port.handle, 0, static_cast(messageCode), (OVERLAPPED*)data)) { - OsErrorExit("IOCP::postUserMessage()"); - } -} - -void IOCP::postQuitMessage() const -{ - postUserMessage(MessageCode::QUIT, nullptr); -} - -HANDLE IOCP::getHandle() const -{ - return m_port.handle; -} - -static DWORD WINAPI WorkerThread(LPVOID lpParameter) -{ - IOCP const * const iocp = reinterpret_cast(lpParameter); - DWORD bytesTransfered = 0; - ULONG_PTR completionKey; - PerIoData * iod = nullptr; - PerHandleData * phd = nullptr; - OVERLAPPED * olap = nullptr; - bool exitLoop = false; - MessageCode messageCode; - DWORD error; - - printf("Entering worker thread %d.\n", ::GetCurrentThreadId()); - while (!exitLoop) { - if (::GetQueuedCompletionStatus(iocp->getHandle(), &bytesTransfered, &completionKey, (LPOVERLAPPED*)&olap, INFINITE)) { - if (bytesTransfered == 0) { - messageCode = static_cast(completionKey); - if (messageCode == MessageCode::TIMEOUT) { - // TODO: Timeout handling. - } else if (messageCode == MessageCode::QUIT) { - iocp->postQuitMessage(); // "Broadcast" - exitLoop = true; - } - - } else { - phd = reinterpret_cast(completionKey); - iod = reinterpret_cast(olap); - printf("\tOPCODE: %d bytes: %d\n", iod->get_opcode(), bytesTransfered); - switch (iod->get_opcode()) { - case IoType::IO_WRITE: - iod->decr_bytes_to_xfer(bytesTransfered); -// phd->m_socket->triggerRead(1024); - if (iod->xfer_finished()) { - delete iod; - } else { - //iod->m_wsabuf.buf = iod->m_wsabuf.buf + (iod->get_bytes_to_xfer() - iod->m_bytesRemaining); - //iod->m_wsabuf.len = iod->m_bytesRemaining; - iod->reset(); - } - break; - case IoType::IO_READ: - printf("IO_READ() numBytes: %d\n", bytesTransfered); - break; - case IoType::IO_ACCEPT: - break; - } - } - } else { - error = ::GetLastError(); - if (olap == nullptr) { - - } else { - // Failed I/O operation. - // The function stores information in the variables pointed to by lpNumberOfBytes, lpCompletionKey. - } - //Win_ErrorMsg("IOWorkerThread::GetQueuedCompletionStatus()", error); - } - } - printf("Exiting worker thread %d\n", ::GetCurrentThreadId()); - ::ExitThread(0); -} - -void CALLBACK Timeout_CB(void * lpParam, unsigned char TimerOrWaitFired) -{ - IOCP const * const iocp = reinterpret_cast(lpParam); - - //printf("TIMEOUT\n"); - iocp->postUserMessage(MessageCode::TIMEOUT); -} - - -#if 0 -void Socket::triggerRead(unsigned int len) -{ - DWORD numReceived = (DWORD)0; - DWORD flags = (DWORD)0; - DWORD err = 0; - int addrLen; - static char buf[1024]; - - PerIoData * iod = new PerIoData(128); - - iod->m_wsabuf.buf = buf; - iod->m_wsabuf.len = len; - iod->m_opcode = IoType::IO_READ; - iod->m_bytesRemaining = iod->m_bytesToXfer = len; - iod->reset(); - - if (m_socktype == SOCK_STREAM) { - if (WSARecv(m_socket, - &iod->m_wsabuf, - 1, - &numReceived, - &flags, - (LPWSAOVERLAPPED)&iod->m_overlapped, - (LPWSAOVERLAPPED_COMPLETION_ROUTINE)nullptr) == SOCKET_ERROR) { - err = WSAGetLastError(); - if (err != WSA_IO_PENDING) { - - } - } - } else if (m_socktype == SOCK_DGRAM) { - addrLen = sizeof(SOCKADDR_STORAGE); - if (WSARecvFrom(m_socket, - &iod->m_wsabuf, - 1, - &numReceived, - &flags, - (LPSOCKADDR)&numReceived, - &addrLen, - (LPWSAOVERLAPPED)&iod->m_overlapped, - (LPWSAOVERLAPPED_COMPLETION_ROUTINE)nullptr)) { - err = WSAGetLastError(); - if (err != WSA_IO_PENDING) { - - } - } - } -} - -typedef std::function CompleteHandler_t; - -class AsyncIoServiceFactory { - -}; - -#endif diff --git a/pyxcp/cxx/win/iocp.hpp b/pyxcp/cxx/win/iocp.hpp deleted file mode 100644 index 2c50026..0000000 --- a/pyxcp/cxx/win/iocp.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#if !defined(__IOCP_HPP) -#define __IOCP_HPP - -#include "iasyncioservice.hpp" -#include "socket.hpp" -#include "perhandledata.hpp" -#include "periodata.hpp" -#include "poolmgr.hpp" -#include -#include -#include - -#if !defined(__GNUC__) -#pragma comment(lib,"ws2_32.lib") // MSVC only. -#endif - - -struct PerPortData { - HANDLE handle; -}; - - -class IOCP : public IAsyncIoService { -public: - IOCP(size_t numProcessors = 1, size_t multiplier = 1); - ~IOCP(); - void registerSocket(Socket& socket); - void postUserMessage(MessageCode messageCode, void * data = nullptr) const; - void postQuitMessage() const; - HANDLE getHandle() const; - -protected: - void registerHandle(const PerHandleData& object); - -private: - PerPortData m_port; - DWORD m_numWorkerThreads; - std::vector m_threads; - PoolManager m_pool_mgr; -}; - -#endif // __IOCP_HPP diff --git a/pyxcp/cxx/win/perhandledata.hpp b/pyxcp/cxx/win/perhandledata.hpp deleted file mode 100644 index 0655bfd..0000000 --- a/pyxcp/cxx/win/perhandledata.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#if !defined(__PERHANDLEDATA_HPP) -#define __PERHANDLEDATA_HPP - -#include - -enum class HandleType { - HANDLE_SOCKET, - HANDLE_FILE, - HANDLE_NAMED_PIPE, - HANDLE_USER, -}; - - -struct PerHandleData { - HandleType m_handleType; - HANDLE m_handle; - DWORD m_seqNoSend; - DWORD m_seqNoRecv; - - PerHandleData(HandleType handleType, const HANDLE& handle) : m_handleType(handleType), m_handle(handle), m_seqNoSend(0), m_seqNoRecv(0) {} -}; - - -#endif // __PERHANDLEDATA_HPP diff --git a/pyxcp/cxx/win/periodata.hpp b/pyxcp/cxx/win/periodata.hpp deleted file mode 100644 index 5c9cbb0..0000000 --- a/pyxcp/cxx/win/periodata.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#if !defined(__PERIODATA_HPP) -#define __PERIODATA_HPP - -#include -#include -#include "utils.hpp" -#include - -enum class IoType { - IO_ACCEPT, - IO_CONNECT, - IO_READ, - IO_WRITE -}; - -class PerIoData { - -public: - - explicit PerIoData(size_t bufferSize = 128) { - m_xferBuffer = nullptr; - m_xferBuffer = new char[bufferSize]; - m_wsabuf.buf = m_xferBuffer; - - m_wsabuf.len = bufferSize; - m_bytesRemaining = 0; - m_bytes_to_xfer = 0; - } - - ~PerIoData() { - if (m_xferBuffer) { - delete[] m_xferBuffer; - } - } - - void setup_write_request() { - - } - - void set_opcode(IoType opcode) { - m_opcode = opcode; - } - - template void set_buffer(std::array& arr) { - - m_wsabuf.buf = arr.data(); - m_wsabuf.len = arr.size(); - } - - WSABUF * get_buffer() { - return &m_wsabuf; - } - - IoType get_opcode() const { - return m_opcode; - } - - void set_transfer_length(size_t length) { - m_bytesRemaining = m_bytes_to_xfer = length; - } - - size_t get_bytes_to_xfer() const { - return m_bytes_to_xfer; - } - - void decr_bytes_to_xfer(size_t amount) { - printf("remaining: %d amount: %d\n",m_bytesRemaining, amount); - assert((static_cast(m_bytesRemaining) - static_cast(amount)) >= 0); - - m_bytesRemaining -= amount; - } - - bool xfer_finished() const { - return m_bytesRemaining == 0; - } - - OVERLAPPED * get_overlapped() { - return &m_overlapped; - } - - void reset() { - ZeroOut(&m_overlapped, sizeof(OVERLAPPED)); - m_wsabuf.len = 0; - m_bytesRemaining = 0; - m_bytes_to_xfer = 0; - } - -private: - OVERLAPPED m_overlapped; - IoType m_opcode; - WSABUF m_wsabuf; - char * m_xferBuffer; - size_t m_bytes_to_xfer; - size_t m_bytesRemaining; -}; - -#endif // __PERIODATA_HPP diff --git a/pyxcp/cxx/win/socket.hpp b/pyxcp/cxx/win/socket.hpp deleted file mode 100644 index 6e00ca8..0000000 --- a/pyxcp/cxx/win/socket.hpp +++ /dev/null @@ -1,185 +0,0 @@ - -#if !defined(__SOCKET_HPP) -#define __SOCKET_HPP - -#include - -#include -#include -#include -#include -#include - -#include "isocket.hpp" -#include "timeout.hpp" -#include "periodata.hpp" -#include "perhandledata.hpp" -#include "pool.hpp" -#include "poolmgr.hpp" - -class IOCP; - -class Socket : public ISocket { -public: - - using Pool_t = Pool; - - Socket(int family = PF_INET, int socktype = SOCK_STREAM, int protocol = IPPROTO_TCP) : - m_family(family), m_socktype(socktype), m_protocol(protocol), m_connected(false), - m_pool_mgr(PoolManager()), m_addr(nullptr) { - m_socket = ::WSASocket(m_family, m_socktype, m_protocol, NULL, 0, WSA_FLAG_OVERLAPPED); - if (m_socket == INVALID_SOCKET) { - SocketErrorExit("Socket::Socket()"); - } - ZeroOut(&m_peerAddress, sizeof(SOCKADDR_STORAGE)); - } - - ~Socket() { - ::closesocket(m_socket); - } - - void option(int optname, int level, int * value) { - int len; - - len = sizeof(*value); - if (*value == 0) { - ::getsockopt(m_socket, level, optname, (char*) value, &len); - } else { - ::setsockopt(m_socket, level, optname, (const char*) value, len); - } - } - - bool getaddrinfo(int family, int socktype, int protocol, const char * hostname, int port, CAddress & address, int flags = AI_PASSIVE) { - int err; - ADDRINFO hints; - ADDRINFO * t_addr; - char port_str[16] = {0}; - - ZeroOut(&hints, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = socktype; - hints.ai_protocol = protocol; - hints.ai_flags = flags; - - ::sprintf(port_str, "%d", port); - err = ::getaddrinfo(hostname, port_str, &hints, &t_addr); - if (err != 0) { - printf("%s\n", gai_strerror(err)); - ::freeaddrinfo(t_addr); - SocketErrorExit("getaddrinfo()"); - return false; - } - - address.length = t_addr->ai_addrlen; - ::CopyMemory(&address.address, t_addr->ai_addr, sizeof(struct sockaddr)); - - ::freeaddrinfo(t_addr); - return true; - } - - void connect(CAddress & address) { - if (::connect(m_socket, &address.address, address.length) == SOCKET_ERROR) { - SocketErrorExit("Socket::connect()"); - } - PerHandleData handleData(HandleType::HANDLE_SOCKET, getHandle()); - } - - void bind(CAddress & address) { - if (::bind(m_socket, &address.address, address.length) == SOCKET_ERROR) { - SocketErrorExit("Socket::bind()"); - } - } - - void listen(int backlog = 5) { - if (::listen(m_socket, backlog) == SOCKET_ERROR) { - SocketErrorExit("Socket::listen()"); - } - } - - void accept(CAddress & peerAddress) { - SOCKET sock; - - peerAddress.length = sizeof peerAddress.address; - sock = ::accept(m_socket, (sockaddr *)&peerAddress.address, &peerAddress.length); - - if (sock == INVALID_SOCKET) { - SocketErrorExit("Socket::accept()"); - } - } - - template - void write(std::array& arr, bool alloc = true) { - DWORD bytesWritten = 0; - int addrLen; - //PerIoData * iod = new PerIoData(128); - PerIoData * iod; - - if (alloc == true) { - iod = m_pool_mgr.get_iod().acquire(); - //iod = m_iod_pool.acquire(); - } - m_timeout.arm(); - iod->reset(); - iod->set_buffer(arr); - iod->set_opcode(IoType::IO_WRITE); - iod->set_transfer_length(arr.size()); - if (m_socktype == SOCK_DGRAM) { - addrLen = sizeof(SOCKADDR_STORAGE); - if (::WSASendTo(m_socket, - iod->get_buffer(), - 1, - &bytesWritten, - 0, - (LPSOCKADDR)&m_peerAddress, - addrLen, - (LPWSAOVERLAPPED)iod, - nullptr - ) == SOCKET_ERROR) { - // WSA_IO_PENDING - SocketErrorExit("Socket::send()"); - } - } else if (m_socktype == SOCK_STREAM) { - if (::WSASend( - m_socket, - iod->get_buffer(), - 1, - &bytesWritten, - 0, - (LPWSAOVERLAPPED)iod, - nullptr) == SOCKET_ERROR) { - SocketErrorExit("Socket::send()"); - closesocket(m_socket); - } - } - printf("Status: %d bytes_written: %d\n", WSAGetLastError(), bytesWritten); - } - void triggerRead(unsigned int len); - - HANDLE getHandle() const { - return reinterpret_cast(m_socket); - } - - const TimeoutTimer& getTimeoutTimer() const { - return m_timeout; - } - - void setIOCP(IOCP * iocp) { - m_iocp = iocp; - m_timeout.setIOCP(iocp); - } - -private: - int m_family; - int m_socktype; - int m_protocol; - bool m_connected; - PoolManager m_pool_mgr; - ADDRINFO * m_addr; - SOCKET m_socket; - //CAddress ourAddress; - SOCKADDR_STORAGE m_peerAddress; - TimeoutTimer m_timeout {150}; - IOCP * m_iocp = nullptr; -}; - -#endif // __SOCKET_HPP diff --git a/pyxcp/cxx/win/timeout.hpp b/pyxcp/cxx/win/timeout.hpp deleted file mode 100644 index 5c40816..0000000 --- a/pyxcp/cxx/win/timeout.hpp +++ /dev/null @@ -1,83 +0,0 @@ - -#if !defined(__TIMEOUT_HPP) -#define __TIMEOUT_HPP - -#include - -#include "utils.hpp" - - -/* - * - * Implements a timer queue based time-out. - * - * Resolution is milli-seconds. - * - * - */ - -class IOCP; - -void CALLBACK Timeout_CB(void * lpParam, unsigned char TimerOrWaitFired); - - -class TimeoutTimer { -public: - - explicit TimeoutTimer(uint64_t value) : m_millis(value) { - m_timer_queue = ::CreateTimerQueue(); - if (m_timer_queue == nullptr) - OsErrorExit("TimeoutTimer::TimeoutTimer() -- CreateTimerQueue"); - } - - TimeoutTimer(const TimeoutTimer&) = default; - TimeoutTimer(const TimeoutTimer&&) = delete; - - ~TimeoutTimer() { - if (!::DeleteTimerQueue(m_timer_queue)) { - OsErrorExit("TimeoutTimer::~TimeoutTimer() -- DeleteTimerQueueEx"); - } - } - - void arm() { - if (m_iocp != nullptr) { - if (!::CreateTimerQueueTimer(&m_timer, m_timer_queue, Timeout_CB, reinterpret_cast(m_iocp), m_millis, 0, 0)) { - OsErrorExit("TimeoutTimer::arm() -- CreateTimerQueueTimer"); - } - } - } - - void disarm() { - if (m_timer != INVALID_HANDLE_VALUE) { - if (!::DeleteTimerQueueTimer(m_timer_queue, m_timer, nullptr)) { - OsErrorExit("TimeoutTimer::disarm() -- DeleteTimerQueueTimer"); - } - m_timer = INVALID_HANDLE_VALUE; - } - } - - HANDLE getHandle() const { - return m_timer_queue; - } - - uint64_t getValue() const { - return m_millis; - } - - void setValue(uint64_t new_millis) { - m_millis = new_millis; - } - - void setIOCP(IOCP * iocp) { - m_iocp = iocp; - } - -private: - - uint64_t m_millis; - HANDLE m_timer_queue {INVALID_HANDLE_VALUE}; - HANDLE m_timer {INVALID_HANDLE_VALUE}; - IOCP * m_iocp = nullptr; -}; - -#endif // __TIMEOUT_HPP diff --git a/pyxcp/transport/cxx_ext/CMakeLists.txt b/pyxcp/transport/cxx_ext/CMakeLists.txt deleted file mode 100644 index 1872ed8..0000000 --- a/pyxcp/transport/cxx_ext/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -cmake_minimum_required(VERSION 3.12) -project(eth_booster VERSION 0.1.0 LANGUAGES CXX) - -set(BUILD_SHARED_LIBS false) - -set(BASE_DIR "../../cxx") - -if(WIN32) - set(_PS "win") -elseif(UNIX) - set(_PS "linux") -endif(WIN32) - - -add_library(eth - ${BASE_DIR}/utils.cpp -) -if(WIN32) - set(ADD_LIBS ws2_32) -elseif(UNIX) - set(ADD_LIBS pthread rt) -endif(WIN32) - -target_compile_features(eth PRIVATE cxx_std_14) -target_include_directories(eth PUBLIC ${BASE_DIR} ${BASE_DIR}/${_PS}) - -add_executable(test_timestamp tests/test_timestamp.cpp) -target_include_directories( - test_timestamp PUBLIC - ${eth_booster_SOURCE_DIR} - ${eth_booster_SOURCE_DIR}/${_PS} -) -target_link_libraries(test_timestamp eth) - - -add_executable(test_pool tests/test_pool.cpp) -target_include_directories( - test_pool PUBLIC - ${eth_booster_SOURCE_DIR} - ${eth_booster_SOURCE_DIR}/${_PS} -) -target_link_libraries(test_pool eth) - -add_executable(blocking_client ${BASE_DIR}/blocking_socket.cpp ${BASE_DIR}/blocking_client.cpp) -target_include_directories( - blocking_client PUBLIC - ${eth_booster_SOURCE_DIR} -# ${eth_booster_SOURCE_DIR}/${_PS} -) -target_link_libraries(blocking_client eth ${ADD_LIBS}) diff --git a/pyxcp/transport/cxx_ext/setup.py b/pyxcp/transport/cxx_ext/setup.py deleted file mode 100644 index 9142207..0000000 --- a/pyxcp/transport/cxx_ext/setup.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import subprocess # nosec -import sys -from distutils.core import setup - -from pybind11.setup_helpers import Pybind11Extension, build_ext - - -try: - INCLUDE_DIRS = subprocess.getoutput("pybind11-config --include") # nosec -except Exception as e: - print(f"Error while executing pybind11-config ({e!r}).\npybind11 probably not installed?") - sys.exit(1) - -pf = sys.platform -if pf.startswith("win32"): - LIBS = ["ws2_32"] -elif pf.startswith("linux"): - LIBS = ["pthread", "rt"] -else: - raise RuntimeError(f"Platform {pf!r} currently not supported.") - - -os.environ["CFLAGS"] = "" - -PKG_NAME = "eth_booster" -EXT_NAMES = ["eth_booster"] -__version__ = "0.0.1" - -ext_modules = [ - Pybind11Extension( - EXT_NAMES[0], - include_dirs=[INCLUDE_DIRS], - sources=["blocking_socket.cpp", "utils.cpp", "wrap.cpp"], - define_macros=[("EXTENSION_NAME", EXT_NAMES[0])], - extra_compile_args=["-O3", "-Wall", "-Weffc++", "-std=c++17"], - libraries=LIBS, - ), -] - -setup( - name=PKG_NAME, - version="0.0.1", - author="Christoph Schueler", - description="Example", - ext_modules=ext_modules, - cmdclass={"build_ext": build_ext}, -) diff --git a/pyxcp/transport/cxx_ext/tests/test_basic_socket.cpp b/pyxcp/transport/cxx_ext/tests/test_basic_socket.cpp deleted file mode 100644 index b40afe5..0000000 --- a/pyxcp/transport/cxx_ext/tests/test_basic_socket.cpp +++ /dev/null @@ -1,39 +0,0 @@ - - -#include "eth.hpp" -#include "socket.hpp" -#include "asynchiofactory.hpp" - -#include -#include - -using std::cout; -using std::endl; -using std::setw; -using std::internal; -using std::fixed; -using std::setfill; - -using namespace std; - -std::array hellomsg {"hello world!!!"}; - -Eth eth; - -int main(void) -{ - - CAddress address; - auto asio = createAsyncIoService(); - auto sock = Socket {PF_INET, SOCK_STREAM, IPPROTO_TCP}; - - //sock.getaddrinfo(PF_INET, SOCK_STREAM, IPPROTO_TCP, "localhost", 50007, address, 0); - sock.getaddrinfo(PF_INET, SOCK_STREAM, IPPROTO_TCP, "192.168.168.100", 50007, address, 0); - sock.connect(address); - asio->registerSocket(sock); - //sock.getaddrinfo(PF_INET, SOCK_STREAM, IPPROTO_TCP, "google.de", 80, address, 0); - //printf("addr: %x", address.address); - - sock.write(hellomsg); - Sleep(250); -} diff --git a/pyxcp/transport/cxx_ext/tests/test_pool.cpp b/pyxcp/transport/cxx_ext/tests/test_pool.cpp deleted file mode 100644 index 13ec4ed..0000000 --- a/pyxcp/transport/cxx_ext/tests/test_pool.cpp +++ /dev/null @@ -1,39 +0,0 @@ - -#include "memoryblock.hpp" -#include "pool.hpp" - -#include -#include - - -typedef Pool, 8> Pool_t; - -void acquire_memory_blocks(Pool_t& pool) -{ - for (int i = 0; i < 8; ++i) { - auto obj = pool.acquire(); - pool.release(obj); - } - // Blocks should be released. -} - -int main() -{ - Pool_t pool; - - acquire_memory_blocks(pool); - - auto p0 = pool.acquire(); - auto p1 = pool.acquire(); - auto p2 = pool.acquire(); - auto p3 = pool.acquire(); - auto p4 = pool.acquire(); - auto p5 = pool.acquire(); - auto p6 = pool.acquire(); - auto p7 = pool.acquire(); - try { - auto p8 = pool.acquire(); // should throw CapacityExhaustedException. - } catch(CapacityExhaustedException) { - std::cout << "OK, caught CapacityExhaustedException as expected." << std::endl; - } -} diff --git a/pyxcp/transport/cxx_ext/tests/test_timestamp.cpp b/pyxcp/transport/cxx_ext/tests/test_timestamp.cpp deleted file mode 100644 index 215cef8..0000000 --- a/pyxcp/transport/cxx_ext/tests/test_timestamp.cpp +++ /dev/null @@ -1,27 +0,0 @@ - -#include "timestamp.hpp" -#include "utils.hpp" - -#include -#include - -using std::cout; -using std::endl; -using namespace std; - - -int main(void) -{ - auto ts = Timestamp(); - double previous = 0.0; - double current = 0.0; - - cout << fixed; - - for (uint16_t idx = 0; idx < 100; ++idx) { - current = ts.get(); - cout << "#" << setw(3) << setfill('0') << idx + 1 << " " << current << " diff: " << current -previous << endl; - Sleep(100); - previous = current; - } -} diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index aa77c75..0000000 --- a/setup.cfg +++ /dev/null @@ -1,26 +0,0 @@ -[metadata] -name=pyxcp - -[aliases] -test=pytest - -[options] -python_requires = ">=3.7" - -[tool:pytest] -addopts = --verbose --tb=short --junitxml=result.xml -testpaths = pyxcp/tests -junit_family=legacy - -[flake8] -max-line-length = 132 -ignore = D203, E203, E266, E501, W503, F403, F401, BLK100 -select = B,C,E,F,W,T4,B9 -count = 1 -statistics = 1 -show-source = 1 -exclude=.git, __pycache__, .mypy_cache, .tox, .venv, .eggs, _build, build, docs, dist - -[black] -line-length = 132 -exclude = .git, .mypy_cache, .tox, .venv, _build, build, docs, __pypackages__, __pycache__, dist diff --git a/setup.py b/setup.py deleted file mode 100644 index 5e929db..0000000 --- a/setup.py +++ /dev/null @@ -1,187 +0,0 @@ -import os -import platform -import subprocess # nosec -import sys - -import setuptools.command.build_py -import setuptools.command.develop - - -if sys.platform == "darwin": - os.environ["MACOSX_DEPLOYMENT_TARGET"] = "11.0" - os.environ["CC"] = "clang++" - os.environ["CXX"] = "clang++" - -try: - from pybind11.setup_helpers import ( - ParallelCompile, - Pybind11Extension, - naive_recompile, - ) -except ImportError: - print("package 'pybind11' not installed, could not build recorder extension module.") - has_pybind11 = False -else: - has_pybind11 = True - ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() - -try: - PYB11_INCLUDE_DIRS = subprocess.check_output(["pybind11-config", "--includes"]) # nosec -except Exception as e: - print(str(e), end=" -- ") - has_pybind11 = False - print("'pybind11-config' not properly working, could not build recorder extension module.") - -with open(os.path.join("pyxcp", "__init__.py")) as f: - for line in f: - if line.startswith("__version__"): - version = line.split("=")[-1].strip().strip('"') - break - -with open("README.md") as fh: - long_description = fh.read() - -EXT_NAMES = ["pyxcp.recorder.rekorder", "pyxcp.cpp_ext.cpp_ext", "pyxcp.daq_stim.stim"] - -if has_pybind11: - ext_modules = [ - Pybind11Extension( - EXT_NAMES[0], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/recorder", "pyxcp/cpp_ext"], - sources=["pyxcp/recorder/lz4.c", "pyxcp/recorder/lz4hc.c", "pyxcp/recorder/wrap.cpp"], - define_macros=[("EXTENSION_NAME", EXT_NAMES[0]), ("NDEBUG", 1)], - optional=False, - cxx_std=20, - ), - Pybind11Extension( - EXT_NAMES[1], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/cpp_ext"], - sources=["pyxcp/cpp_ext/extension_wrapper.cpp"], - define_macros=[("EXTENSION_NAME", EXT_NAMES[1]), ("NDEBUG", 1)], - optional=False, - cxx_std=20, - ), - Pybind11Extension( - EXT_NAMES[2], - include_dirs=[PYB11_INCLUDE_DIRS, "pyxcp/daq_stim", "pyxcp/cpp_ext"], - sources=["pyxcp/daq_stim/stim.cpp", "pyxcp/daq_stim/stim_wrapper.cpp", "pyxcp/daq_stim/scheduler.cpp"], - define_macros=[("EXTENSION_NAME", EXT_NAMES[2]), ("NDEBUG", 1)], - optional=False, - cxx_std=20, # Extension will use C++20 generators/coroutines. - ), - ] -else: - ext_modules = [] - -install_reqs = [ - "pybind11", - "pyusb", - "construct >= 2.9.0", - "mako", - "pyserial", - "toml", - "python-can", - "uptime", - "chardet", - "traitlets", - "rich", -] - - -class AsamKeyDllAutogen(setuptools.Command): - """Custom command to compile `asamkeydll.exe`.""" - - description = "Compile `asamkeydll.exe`." - - def initialize_options(self): - pass - - def finalize_options(self): - """Post-process options.""" - asamkeydll = os.path.join("pyxcp", "asamkeydll.c") - target = os.path.join("pyxcp", "asamkeydll.exe") - self.arguments = [asamkeydll, f"-o{target}"] - - def run(self): - """Run gcc""" - word_width, _ = platform.architecture() - if sys.platform == "win32" and word_width == "64bit": - gccCmd = ["gcc", "-m32", "-O3", "-Wall"] - self.announce(" ".join(gccCmd + self.arguments)) - try: - subprocess.check_call(gccCmd + self.arguments) # nosec - except Exception as e: - print(f"Building pyxcp/asamkeydll.exe failed: {e!r}") - else: - print("Successfully build pyxcp/asamkeydll.exe") - - -class CustomBuildPy(setuptools.command.build_py.build_py): - def run(self): - self.run_command("asamkeydll") - super().run() - - -class CustomDevelop(setuptools.command.develop.develop): - def run(self): - self.run_command("asamkeydll") - super().run() - - -setuptools.setup( - name="pyxcp", - version=version, - provides=["pyxcp"], - description="Universal Calibration Protocol for Python", - long_description=long_description, - long_description_content_type="text/markdown", - author="Christoph Schueler", - author_email="cpu12.gems@googlemail.com", - url="https://github.com/christoph2/pyxcp", - packages=setuptools.find_packages(), - cmdclass={ - "asamkeydll": AsamKeyDllAutogen, - "build_py": CustomBuildPy, - "develop": CustomDevelop, - }, - python_requires=">=3.7", - include_package_data=True, - install_requires=install_reqs, - extras_require={"docs": ["sphinxcontrib-napoleon"], "develop": ["bumpversion"]}, - ext_modules=ext_modules, - package_dir={"tests": "pyxcp/tests"}, - zip_safe=False, - tests_require=["pytest", "pytest-runner"], - test_suite="pyxcp.tests", - license="LGPLv3+", - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - # How mature is this project? Common values are - # 3 - Alpha - # 4 - Beta - # 5 - Production/Stable - "Development Status :: 5 - Production/Stable", - # Indicate who your project is intended for - "Intended Audience :: Developers", - "Topic :: Software Development", - "Topic :: Scientific/Engineering", - # Pick your license as you wish (should match "license" above) - "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", - # Specify the Python versions you support here. In particular, ensure - # that you indicate whether you support Python 2, Python 3 or both. - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - ], - entry_points={ - "console_scripts": [ - "pyxcp-probe-can-drivers = pyxcp.scripts.pyxcp_probe_can_drivers:main", - "xcp-id-scanner = pyxcp.scripts.xcp_id_scanner:main", - "xcp-fetch-a2l = pyxcp.scripts.xcp_fetch_a2l:main", - "xcp-info = pyxcp.scripts.xcp_info:main", - ], - }, -) From 6eb4ae3fd864163f5f51e54bb39d8ea330d2d5a8 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 13 Aug 2024 14:11:54 +0300 Subject: [PATCH 97/99] today() --- .pre-commit-config.yaml | 1 + CONTRIBUTORS | 1 + build_ext.py | 14 +------------- docs/configuration.rst | 5 +++++ pyproject.toml | 4 ++-- pyxcp/config/__init__.py | 5 ----- pyxcp/cpp_ext/helper.hpp | 1 + pyxcp/examples/run_daq.py | 24 ++++++++++++------------ pyxcp/scripts/xcp_fetch_a2l.py | 2 ++ pyxcp/scripts/xcp_profile.py | 15 ++++++++++++--- pyxcp/transport/base.py | 3 +-- 11 files changed, 38 insertions(+), 37 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b6dba1a..47efc49 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,6 +19,7 @@ repos: entry: ruff language: system types: [python] + args: ["check"] require_serial: true - id: check-added-large-files name: Check for added large files diff --git a/CONTRIBUTORS b/CONTRIBUTORS index ac8e1b5..58bc554 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -6,6 +6,7 @@ danielhrisca Devendra Giramkar dkuschmierz <32884309+dkuschmierz@users.noreply.github.com> Felix Nieuwenhuizen +Jacob Schaer Paul Gee rui Sedov Aleksandr diff --git a/build_ext.py b/build_ext.py index 5c365e6..e5fec76 100644 --- a/build_ext.py +++ b/build_ext.py @@ -29,20 +29,11 @@ def build_extension(debug: bool = False) -> None: debug = bool(os.environ.get("DEBUG", 0)) or debug cfg = "Debug" if debug else "Release" print(f" BUILD-TYPE: {cfg!r}") - - # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON - # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code - # from Python. cmake_args = [ - # f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", - # "-G Ninja", f"-DPYTHON_EXECUTABLE={sys.executable}", f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm ] build_args = ["--config Release", "--verbose"] - # Adding CMake arguments set as environment variable - # (needed e.g. to build for ARM OSx on conda-forge) - # cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 /path/to/src if sys.platform.startswith("darwin"): @@ -53,20 +44,17 @@ def build_extension(debug: bool = False) -> None: build_temp = Path(TemporaryDirectory(suffix=".build-temp").name) / "extension_it_in" # build_temp = Path(".") / "build" - print("cwd:", os.getcwd(), "build-dir:", build_temp, "top:", str(TOP_DIR)) - # print("PHILEZ:", os.listdir(TOP_DIR)) + # print("cwd:", os.getcwd(), "build-dir:", build_temp, "top:", str(TOP_DIR)) if not build_temp.exists(): build_temp.mkdir(parents=True) banner("Step #1: Configure") # cmake_args += ["--debug-output"] - print("aufruf:", ["cmake", str(TOP_DIR), *cmake_args]) subprocess.run(["cmake", str(TOP_DIR), *cmake_args], cwd=build_temp, check=True) # nosec cmake_args += [f"--parallel {mp.cpu_count()}"] banner("Step #2: Build") - # subprocess.run(["cmake", "--build", ".", *build_args], cwd=build_temp, check=True) # nosec # build_args += ["-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON"] subprocess.run(["cmake", "--build", build_temp, *build_args], cwd=TOP_DIR, check=True) # nosec diff --git a/docs/configuration.rst b/docs/configuration.rst index e083834..3113dbb 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1,6 +1,11 @@ Configuration ============= +pyXCP has many configuration options... + +make shure to use `.py` as an extension. + + Parameters live in `JSON` or `TOML` :file:`pyxcp/examples` contains some example configurations. General pyXCP Parameters diff --git a/pyproject.toml b/pyproject.toml index c083a1e..4f1769a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.12" + "Programming Language :: Python :: 3.13" ] build = "build_ext.py" include = [ @@ -166,7 +166,7 @@ exclude = ''' build-verbosity = 3 #test-command = "pytest {package}/tests" #test-command = "pytest -svv pyxcp/tests" -build = "cp3{7,8,9,10,11,12}-*" +build = "cp3{7,8,9,10,11,12,13}-*" skip = ["*-manylinux_i686", "*-musllinux_x86_64", "*-musllinux_i686"] # Skip Linux 32bit and MUSL builds. build-frontend = "build" diff --git a/pyxcp/config/__init__.py b/pyxcp/config/__init__.py index f91e5d9..0e34aa4 100644 --- a/pyxcp/config/__init__.py +++ b/pyxcp/config/__init__.py @@ -799,11 +799,6 @@ class Transport(SingletonConfigurable): help="Choose one of the supported XCP transport layers.", ).tag(config=True) create_daq_timestamps = Bool(False, help="Record time of frame reception or set timestamp to 0.").tag(config=True) - timestamp_mode = Enum( - values=["ABSOLUTE", "RELATIVE"], - default_value="ABSOLUTE", - help="""Either absolute timestamps since some epoch or program start, both values are in nano-seconds.""", - ).tag(config=True) timeout = Float( 2.0, help="""raise `XcpTimeoutError` after `timeout` seconds diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index f99ffc0..a0ccf56 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -127,6 +127,7 @@ class TimestampInfo { TimestampInfo(TimestampInfo &&) = default; TimestampInfo &operator=(const TimestampInfo &) = default; TimestampInfo &operator=(TimestampInfo &&) = default; + virtual ~TimestampInfo() {} TimestampInfo() : m_timestamp_ns(0), m_timezone{}, m_utc_offset(0), m_dst_offset(0) { } diff --git a/pyxcp/examples/run_daq.py b/pyxcp/examples/run_daq.py index d1a1868..d582928 100644 --- a/pyxcp/examples/run_daq.py +++ b/pyxcp/examples/run_daq.py @@ -10,8 +10,8 @@ ap = ArgumentParser(description="DAQ test") -# XCP_LITE = True -XCP_LITE = False +XCP_LITE = True +# XCP_LITE = False # Completly random configurations, only for illustrative purposes. # @@ -26,10 +26,10 @@ False, False, [ - ("byteCounter", 0x31698, 0, "U8"), - ("wordCounter", 0x3169A, 0, "U16"), - ("dwordCounter", 0x3169C, 0, "U32"), - ("sbyteCounter", 0x316A0, 0, "I8"), + ("byteCounter", 0x203EA, 0, "U8"), + ("wordCounter", 0x203EC, 0, "U16"), + ("dwordCounter", 0x20410, 0, "U32"), + ("sbyteCounter", 0x203EB, 0, "I8"), ], ), DaqList( @@ -38,11 +38,11 @@ False, False, [ - ("swordCounter", 0x316A2, 0, "I16"), - ("sdwordCounter", 0x316A4, 0, "I32"), - ("channel1", 0x30280, 0, "F64"), - ("channel2", 0x30288, 0, "F64"), - ("channel3", 0x30290, 0, "F64"), + ("swordCounter", 0x20414, 0, "I16"), + ("sdwordCounter", 0x20418, 0, "I32"), + ("channel1", 0x203F8, 0, "F64"), + ("channel2", 0x20400, 0, "F64"), + ("channel3", 0x20408, 0, "F64"), ], ), ] @@ -156,7 +156,7 @@ print("start DAQ lists.") daq_parser.start() # Start DAQ lists. - time.sleep(3 * 15.0 * 60.0) + time.sleep(1 * 15.0 * 60.0) # time.sleep(1 * 60.0 * 60.0) # Arbitrary termination condition.ls *. # time.sleep(6 * 60.0 * 60.0) # Arbitrary termination condition.ls *. print("Stop DAQ....") diff --git a/pyxcp/scripts/xcp_fetch_a2l.py b/pyxcp/scripts/xcp_fetch_a2l.py index 3aa196a..b54f700 100644 --- a/pyxcp/scripts/xcp_fetch_a2l.py +++ b/pyxcp/scripts/xcp_fetch_a2l.py @@ -17,6 +17,8 @@ def main(): # TODO: error-handling. file_name = x.identifier(XcpGetIdType.FILENAME) content = x.identifier(XcpGetIdType.FILE_TO_UPLOAD) + if not content: + sys.exit(f"Empty response from ID '{XcpGetIdType.FILE_TO_UPLOAD!r}'.") x.disconnect() if not file_name.lower().endswith(".a2l"): file_name += ".a2l" diff --git a/pyxcp/scripts/xcp_profile.py b/pyxcp/scripts/xcp_profile.py index 5ec8640..f45a8a6 100644 --- a/pyxcp/scripts/xcp_profile.py +++ b/pyxcp/scripts/xcp_profile.py @@ -7,8 +7,17 @@ from pyxcp.cmdline import ArgumentParser -sys.argv.append("profile") +if len(sys.argv) == 1: + sys.argv.append("profile") +elif len(sys.argv) >= 2 and sys.argv[1] != "profile": + sys.argv.insert(1, "profile") + + ap = ArgumentParser(description="Create / convert pyxcp profiles (configurations).") -with ap.run() as x: - pass +try: + with ap.run() as x: + pass +except FileNotFoundError as e: + print(f"Error: {e}") + sys.exit(1) diff --git a/pyxcp/transport/base.py b/pyxcp/transport/base.py index 4dad903..5a19c56 100644 --- a/pyxcp/transport/base.py +++ b/pyxcp/transport/base.py @@ -154,8 +154,7 @@ def __init__(self, config, policy: Optional[FrameAcquisitionPolicy] = None, tran self.counter_send: int = 0 self.counter_received: int = -1 self.create_daq_timestamps: bool = config.create_daq_timestamps - timestamp_mode = TimestampType.ABSOLUTE_TS if config.timestamp_mode == "ABSOLUTE" else TimestampType.RELATIVE_TS - self.timestamp = Timestamp(timestamp_mode) + self.timestamp = Timestamp(TimestampType.ABSOLUTE_TS) self._start_datetime: CurrentDatetime = CurrentDatetime(self.timestamp.initial_value) self.alignment: int = config.alignment self.timeout: int = seconds_to_nanoseconds(config.timeout) From 4c6c623d4e5bde2190fe9a88110819fe3b120b57 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 13 Aug 2024 18:48:32 +0300 Subject: [PATCH 98/99] today() --- pyxcp/cpp_ext/helper.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index a0ccf56..58ca5cb 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -3,13 +3,14 @@ #define __HELPER_HPP #if defined(_WIN32) || defined(_WIN64) - #include + #else #include #include #endif #include + #include #include #include #include From 9c9d51d1bda5de7c78f3f789a793af8a51a113d2 Mon Sep 17 00:00:00 2001 From: Christoph Schueler Date: Tue, 13 Aug 2024 19:53:15 +0300 Subject: [PATCH 99/99] today() --- pyxcp/cpp_ext/__init__.py | 2 -- pyxcp/cpp_ext/extension_wrapper.cpp | 4 ++-- pyxcp/cpp_ext/helper.hpp | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyxcp/cpp_ext/__init__.py b/pyxcp/cpp_ext/__init__.py index 2026297..4a07cae 100644 --- a/pyxcp/cpp_ext/__init__.py +++ b/pyxcp/cpp_ext/__init__.py @@ -5,6 +5,4 @@ Timestamp, TimestampInfo, TimestampType, - sleep_ms, - sleep_ns, ) diff --git a/pyxcp/cpp_ext/extension_wrapper.cpp b/pyxcp/cpp_ext/extension_wrapper.cpp index 4dd7c78..5f92807 100644 --- a/pyxcp/cpp_ext/extension_wrapper.cpp +++ b/pyxcp/cpp_ext/extension_wrapper.cpp @@ -23,8 +23,8 @@ class PyTimestampInfo : public TimestampInfo { PYBIND11_MODULE(cpp_ext, m) { m.doc() = "C++ extensions for pyXCP."; - m.def("sleep_ms", &sleep_ms, "milliseconds"_a); - m.def("sleep_ns", &sleep_ns, "nanoseconds"_a); + //m.def("sleep_ms", &sleep_ms, "milliseconds"_a); + //m.def("sleep_ns", &sleep_ns, "nanoseconds"_a); py::class_(m, "McObject") .def( diff --git a/pyxcp/cpp_ext/helper.hpp b/pyxcp/cpp_ext/helper.hpp index 58ca5cb..9f24ca3 100644 --- a/pyxcp/cpp_ext/helper.hpp +++ b/pyxcp/cpp_ext/helper.hpp @@ -247,6 +247,7 @@ class Timestamp { std::uint64_t m_initial; }; +#if 0 inline void sleep_ms(std::uint64_t milliseconds) { std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); } @@ -254,5 +255,6 @@ inline void sleep_ms(std::uint64_t milliseconds) { inline void sleep_ns(std::uint64_t nanoseconds) { std::this_thread::sleep_for(std::chrono::nanoseconds(nanoseconds)); } +#endif #endif // __HELPER_HPP