diff --git a/BUILD b/BUILD index 05d92a6..400229b 100644 --- a/BUILD +++ b/BUILD @@ -59,11 +59,11 @@ copyright_checker( srcs = [ ".github", "bazel", - "deps", "examples", "itf", "scripts", "test", + "third_party", "tools", "//:BUILD", "//:MODULE.bazel", diff --git a/MODULE.bazel b/MODULE.bazel index b918de4..a2ab28c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -96,18 +96,6 @@ git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "g git_repository( name = "dlt_daemon", branch = "master", - build_file = "//deps:dlt_daemon.BUILD", + build_file = "//third_party/dlt:dlt_daemon.BUILD", remote = "https://github.com/draganbjedov/dlt-daemon.git", ) - -PYTHON_DLT_VERSION = "2.18.10.1" - -http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "python_dlt", - build_file = "//deps:python_dlt.BUILD", - sha256 = "cae256ac791d06c4e6e4665dc938e497a7de5784bf6a1244667572953990f324", - strip_prefix = "python-dlt-%s" % PYTHON_DLT_VERSION, - url = "https://github.com/bmwcarit/python-dlt/archive/refs/tags/v%s.tar.gz" % PYTHON_DLT_VERSION, -) diff --git a/bazel/rules/build_as_host.bzl b/bazel/rules/build_as_host.bzl deleted file mode 100644 index 6a71bc5..0000000 --- a/bazel/rules/build_as_host.bzl +++ /dev/null @@ -1,45 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* - -""" - Implements a rule to run generators in host cfg - With this host configuration, generators need to be run only once even if the corresponding results - are built with different bazel configs -""" - -def _as_host_impl(ctx): - providers = [ - OutputGroupInfo, - CcInfo, - ] - - output = [ - ctx.attr.src[provider] - for provider in providers - if provider in ctx.attr.src - ] - - if DefaultInfo in ctx.attr.src: - output = output + [DefaultInfo(files = ctx.attr.src[DefaultInfo].files, runfiles = ctx.attr.src[DefaultInfo].data_runfiles)] - - return output - -as_host = rule( - implementation = _as_host_impl, - attrs = { - "src": attr.label( - allow_files = True, - cfg = "exec", - ), - }, -) diff --git a/config/target_config.json b/config/target_config.json index ae26af4..4a49147 100644 --- a/config/target_config.json +++ b/config/target_config.json @@ -10,12 +10,6 @@ "serial_device": "", "network_interfaces": [], "ecu_name": "s_core_ecu_qemu_bridge_network_pp", - "data_router_config": { - "vlan_address": "192.168.122.1", - "multicast_addresses": [ - "239.255.42.99" - ] - }, "qemu_num_cores": 2, "qemu_ram_size": "1G" }, @@ -40,10 +34,6 @@ "serial_device": "", "network_interfaces": [], "ecu_name": "s_core_ecu_qemu_port_forwarding_pp", - "data_router_config": { - "vlan_address": "127.0.0.1", - "multicast_addresses": [] - }, "qemu_num_cores": 2, "qemu_ram_size": "1G" }, diff --git a/examples/examples/itf/BUILD b/examples/examples/itf/BUILD index 7889bd8..c0a4838 100644 --- a/examples/examples/itf/BUILD +++ b/examples/examples/itf/BUILD @@ -11,7 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* load("@score_itf//:defs.bzl", "py_itf_test") -load("@score_itf//itf/plugins:plugins.bzl", "base", "docker") +load("@score_itf//itf/plugins:plugins.bzl", "base", "dlt", "docker") py_itf_test( name = "test_docker", @@ -43,6 +43,7 @@ py_itf_test( ], plugins = [ base, + dlt, ], ) @@ -61,5 +62,6 @@ py_itf_test( ], plugins = [ base, + dlt, ], ) diff --git a/itf/core/base/base_plugin.py b/itf/core/base/base_plugin.py index 5fdcded..fc304e5 100644 --- a/itf/core/base/base_plugin.py +++ b/itf/core/base/base_plugin.py @@ -24,6 +24,8 @@ from itf.core.utils import padder from itf.core.utils.bunch import Bunch +from itf.plugins.dlt.dlt_receive import DltReceive, Protocol + logger = logging.getLogger(__name__) @@ -35,12 +37,6 @@ def pytest_addoption(parser): default="config/target_config.json", help="Path to json file with target configurations.", ) - # Internally provided in py_itf_test macro - parser.addoption( - "--dlt_receive_path", - action="store", - help="Path to dlt-receive binary", - ) parser.addoption( "--ecu", action="store", @@ -86,35 +82,41 @@ def target_config_fixture(request): @pytest.fixture(scope="session") -def target_fixture(target_config_fixture, test_config_fixture, request): +def target_fixture(target_config_fixture, test_config_fixture, request, dlt_config): logger.info("Starting target_fixture in base_plugin.py ...") logger.info(f"Starting tests on host: {socket.gethostname()}") - if test_config_fixture.qemu: - with qemu_target(target_config_fixture, test_config_fixture) as qemu: - try: - pre_tests_phase(qemu, target_config_fixture.ip_address, test_config_fixture, request) - yield qemu - finally: - post_tests_phase(qemu, test_config_fixture) - - elif test_config_fixture.qvp: - with qvp_target(target_config_fixture, test_config_fixture) as qvp: - try: - pre_tests_phase(qvp, target_config_fixture.ip_address, test_config_fixture, request) - yield qvp - finally: - post_tests_phase(qvp, test_config_fixture) - - elif test_config_fixture.hw: - with hw_target(target_config_fixture, test_config_fixture) as hardware: - try: - pre_tests_phase(hardware, target_config_fixture.ip_address, test_config_fixture, request) - yield hardware - finally: - post_tests_phase(hardware, test_config_fixture) - else: - raise RuntimeError("QEMU, QVP or HW not specified to use") + with DltReceive( + protocol=Protocol.UDP, + host_ip=dlt_config.host_ip, + multicast_ips=dlt_config.multicast_ips, + binary_path=dlt_config.dlt_receive_path, + ): + if test_config_fixture.qemu: + with qemu_target(target_config_fixture, test_config_fixture) as qemu: + try: + pre_tests_phase(qemu, target_config_fixture.ip_address, test_config_fixture, request) + yield qemu + finally: + post_tests_phase(qemu, test_config_fixture) + + elif test_config_fixture.qvp: + with qvp_target(target_config_fixture, test_config_fixture) as qvp: + try: + pre_tests_phase(qvp, target_config_fixture.ip_address, test_config_fixture, request) + yield qvp + finally: + post_tests_phase(qvp, test_config_fixture) + + elif test_config_fixture.hw: + with hw_target(target_config_fixture, test_config_fixture) as hardware: + try: + pre_tests_phase(hardware, target_config_fixture.ip_address, test_config_fixture, request) + yield hardware + finally: + post_tests_phase(hardware, test_config_fixture) + else: + raise RuntimeError("QEMU, QVP or HW not specified to use") def __make_test_config(config): @@ -126,7 +128,6 @@ def __make_test_config(config): qemu_image=config.getoption("qemu_image"), qvp=config.getoption("qvp"), hw=config.getoption("hw"), - dlt_receive_path=config.getoption("dlt_receive_path"), ) diff --git a/itf/core/base/target/BUILD b/itf/core/base/target/BUILD index 8f8e6d9..87f0032 100644 --- a/itf/core/base/target/BUILD +++ b/itf/core/base/target/BUILD @@ -27,7 +27,7 @@ py_library( "//itf/core/base/os", "//itf/core/base/target/config", "//itf/core/base/target/processors", - "//itf/core/dlt", "//itf/core/qemu", + "//itf/plugins/dlt", ], ) diff --git a/itf/core/base/target/config/config.py b/itf/core/base/target/config/config.py index 39fef5a..6daba3f 100644 --- a/itf/core/base/target/config/config.py +++ b/itf/core/base/target/config/config.py @@ -49,7 +49,6 @@ def load_configuration(config_file: str): serial_device=perf_config["serial_device"], network_interfaces=perf_config["network_interfaces"], ecu_name=perf_config["ecu_name"], - data_router_config=perf_config["data_router_config"], qemu_num_cores=perf_config["qemu_num_cores"], qemu_ram_size=perf_config["qemu_ram_size"], params=perf_config.get("params", {}), diff --git a/itf/core/base/target/hw_target.py b/itf/core/base/target/hw_target.py index 0940b3c..00e6eac 100644 --- a/itf/core/base/target/hw_target.py +++ b/itf/core/base/target/hw_target.py @@ -10,14 +10,8 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -import logging - from contextlib import contextmanager, nullcontext from itf.core.base.target.base_target import Target -from itf.core.dlt.dlt_receive import DltReceive, Protocol - - -logger = logging.getLogger(__name__) @contextmanager @@ -29,13 +23,7 @@ def hw_target(target_config, test_config): diagnostic_ip = None with nullcontext(): - with DltReceive( - target_ip=target_config.ip_address, - protocol=Protocol.UDP, - data_router_config=target_config.data_router_config, - binary_path=test_config.dlt_receive_path, - ): - target = Target(test_config.ecu, test_config.os, diagnostic_ip) - target.register_processors() - yield target - target.teardown() + target = Target(test_config.ecu, test_config.os, diagnostic_ip) + target.register_processors() + yield target + target.teardown() diff --git a/itf/core/base/target/qemu_target.py b/itf/core/base/target/qemu_target.py index e598473..86853d5 100644 --- a/itf/core/base/target/qemu_target.py +++ b/itf/core/base/target/qemu_target.py @@ -16,7 +16,6 @@ from itf.core.base.target.base_target import Target from itf.core.base.target.config.ecu import Ecu from itf.core.base.target.processors.qemu_processor import TargetProcessorQemu -from itf.core.dlt.dlt_receive import DltReceive, Protocol from itf.core.qemu.qemu_process import QemuProcess as Qemu @@ -41,13 +40,7 @@ def qemu_target(target_config, test_config): with Qemu( target_config.qemu_image_path, None, target_config.qemu_ram_size, target_config.qemu_num_cores ) if target_config.qemu_image_path else nullcontext() as qemu_process: - with DltReceive( - target_ip=target_config.ip_address, - protocol=Protocol.UDP, - data_router_config=target_config.data_router_config, - binary_path=test_config.dlt_receive_path, - ): - target = TargetQemu(test_config.ecu, test_config.os) - target.register_processors(qemu_process) - yield target - target.teardown() + target = TargetQemu(test_config.ecu, test_config.os) + target.register_processors(qemu_process) + yield target + target.teardown() diff --git a/itf/core/base/target/qvp_target.py b/itf/core/base/target/qvp_target.py index f7d9358..5a42a55 100644 --- a/itf/core/base/target/qvp_target.py +++ b/itf/core/base/target/qvp_target.py @@ -17,7 +17,7 @@ from itf.core.base.target.base_target import Target from itf.core.base.target.config.ecu import Ecu from itf.core.base.target.processors.qvp_processor import TargetProcessorQVP -from itf.core.dlt.dlt_receive import DltReceive, Protocol +from itf.plugins.dlt.dlt_receive import DltReceive, Protocol logger = logging.getLogger(__name__) @@ -43,10 +43,10 @@ def qvp_target(target_config, test_config): """ with nullcontext() as qvp_process: with DltReceive( + host_ip=dlt_config.host_ip, + multicast_ips=dlt_config.multicast_ips, + binary_path=dlt_config.dlt_receive_path, target_ip=target_config.ip_address, - protocol=Protocol.UDP, - data_router_config=target_config.data_router_config, - binary_path=test_config.dlt_receive_path, ): target = TargetQvp(test_config.ecu, test_config.os) target.register_processors(qvp_process) diff --git a/itf/core/dlt/BUILD b/itf/core/dlt/BUILD deleted file mode 100644 index 5ad0c63..0000000 --- a/itf/core/dlt/BUILD +++ /dev/null @@ -1,62 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* - -load("@rules_python//python:defs.bzl", "py_library") -load("//bazel/rules:build_as_host.bzl", "as_host") - -as_host( - name = "dlt-receive_as_host", - src = "@dlt_daemon//:dlt-receive", - visibility = ["//visibility:public"], -) - -as_host( - name = "libdlt_as_host.so", - src = "@dlt_daemon//:libdlt.so", - visibility = ["//visibility:public"], -) - -genrule( - name = "dlt-artifacts", - outs = [ - "dlt-receive", - "libdlt.so", - ], - cmd = """ - cp $(location :dlt-receive_as_host) $(@D) - cp $(location :libdlt_as_host.so) $(@D) - """, - output_to_bindir = True, - tags = ["aas"], - tools = [ - ":dlt-receive_as_host", - ":libdlt_as_host.so", - ], - visibility = ["//visibility:public"], -) - -py_library( - name = "dlt", - srcs = [ - "__init__.py", - "dlt_receive.py", - "dlt_window.py", - ], - data = [":dlt-artifacts"], - imports = ["."], - visibility = ["//visibility:public"], - deps = [ - "//itf/core/utils", - "@python_dlt", - ], -) diff --git a/itf/core/dlt/dlt_window.py b/itf/core/dlt/dlt_window.py deleted file mode 100644 index d8071f3..0000000 --- a/itf/core/dlt/dlt_window.py +++ /dev/null @@ -1,264 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2025 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* -import logging -import os -import time -import dlt - -from itf.core.utils.bunch import Bunch -from itf.core.dlt.dlt_receive import DltReceive, Protocol -from itf.core.utils.process.process_wrapper import ProcessWrapper - - -logger = logging.getLogger(__name__) - - -class DltWindow(ProcessWrapper): - """ - Save, filter and query DLT logs on demand from the provided target - Logs by default are saved in the "/tmp" folder and thus will not be uploaded - "protocol" can be either Protocol.TCP or Protocol.UDP - - dlt_filter -> create './filter.txt' file and add flag -f filter.txt to dlt-receive. - This enabled filtering dlt messages. Example of usages: - dlt = DltWindow(filter='EPTP LTCE') - Where: - EPTP -> Application ID - LTCE -> Contex ID - """ - - # pylint: disable=too-many-instance-attributes - def __init__( - self, - target_ip: str = "127.0.0.1", - protocol: Protocol = Protocol.UDP, - dlt_file: str = None, - print_to_stdout: bool = False, - logger_name: str = None, - sctf: bool = False, - clear_dlt: bool = True, - dlt_filter: str = None, - data_router_config: dict = None, - binary_path: str = None, - ): - """Initialize DltWindow with target IP, protocol, and optional parameters. - - :param str target_ip: IP address of the target device. - :param Protocol protocol: Communication protocol (TCP or UDP). - :param str dlt_file: Path to the DLT file. Defaults to '/tmp/dlt_window.dlt'. - :param bool print_to_stdout: If True, prints DLT messages to stdout. - :param str logger_name: Name of the logger to use. - :param bool sctf: If True, uses SCTF configuration. - :param bool clear_dlt: If True, clears the DLT file at initialization. - :param str dlt_filter: Filter string for DLT messages. - :param dict data_router_config: Configuration for the data router - with keys "vlan_address" and "multicast_addresses". - :param str binary_path: Path to the dlt-receive binary. - """ - self._target_ip = target_ip - self._protocol = protocol - self._dlt_file = f"{dlt_file or '/tmp/dlt_window.dlt'}" - self._captured_logs = [] - - self._protocol_opts = DltReceive.protocol_arguments(self._target_ip, self._protocol, sctf, data_router_config) - - self._dlt_content = None - self._queried_counter = 0 - - self.logger = logging.getLogger(logger_name) if logger_name else None - self._log_handler = None - if self.logger: - self._log_handler = logging.Handler() - - # Dynamically assign emit method - def emit(record): - log_entry = self._log_handler.format(record) - self._captured_logs.append(log_entry) - - self._log_handler.emit = emit - - formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") - self._log_handler.setFormatter(formatter) - self.logger.addHandler(self._log_handler) - self.logger.setLevel(logging.DEBUG) - - if clear_dlt: - self.clear() - - print_opts = ["-a"] if print_to_stdout else [] - dlt_receive_args = ["-o", self._dlt_file] + self._protocol_opts + print_opts - - if dlt_filter: - with open("./filter.txt", "w") as file: - file.write(dlt_filter) - dlt_receive_args += ["-f", "filter.txt"] - - super().__init__( - binary_path, - dlt_receive_args, - logger_name=logger_name, - ) - - @staticmethod - def live_logs(): - return DltWindow(logger_name="dlt_test", print_to_stdout=True) - - def __enter__(self): - self._start() - return self - - def _start(self): - super().__enter__() - - def start(self): - self._start() - - def __exit__(self, exc_type, exc_val, exc_tb): - self._stop(exc_type, exc_val, exc_tb) - - def _stop(self, exc_type, exc_val, exc_tb): - super().__exit__(exc_type, exc_val, exc_tb) - if os.path.exists("./filter.txt"): - os.remove("./filter.txt") - if self.logger and self._log_handler: - self.logger.removeHandler(self._log_handler) - self._captured_logs.clear() - - def stop(self): - self._stop(None, None, None) - - def record(self): - return self - - def load(self, filters=None): - """Load and filter DLT messages from the recorded file - - :param list filters: List of filters to apply [("APPID", "CTID"), ...] - [("", "")] : loads everything, same as filters=None - [("Fasi", "")] : loads all messages with APPID="Fasi" and non extended ones - [("Fasi", "mata")] : loads messages with APPID="Fasi" and CTID="mata", also all non extended ones - """ - self._dlt_content = dlt.load(self._dlt_file, filters=filters) - - def find(self, query=None, include_ext=True, include_non_ext=False, full_match=True, timeout=None): - """Find DLT messages matching the input query - - :param dict query: Dictionary with selected keys to compare with - dict(apid=re.compile(r"^A.*"), ctid=b'ACOM') : messages which apid starting with "A" and CTID="ACOM" - dict(payload_decoded=re.compile(r".connected.*")) : messages which payload contains "connected" - :param bool include_ext: Include extended DLT messages during search. Set False to exclude them - :param bool include_non_ext: Include non extended DLT messages during search. Set False to exclude them - :param bool full_match: Find all DLT messages matching the query. Set False to return immediatly after first match - :param bool timeout: If set, the check will be stopped if timeout exceeded - :returns list: List of DLT messages matching the query. Each message is a Bunch object: - time_stamp float - apid, ctid, payload string - raw_msg DLTMessage object - epoch_time float - """ - if not include_ext and not include_non_ext: - logger.warning("Both 'include_ext' and 'include_non_ext' flags are set to False: empty search space!") - return [] - - self._queried_counter = 0 - result = [] - start_time = time.time() - - for msg in self._dlt_content: - if not include_ext and msg.use_extended_header: - continue - - if not include_non_ext and not msg.use_extended_header: - continue - - self._queried_counter += 1 - - if not query or msg.compare(query): - payload = msg.payload_decoded - if isinstance(payload, bytes): - payload = payload.decode(errors="ignore") - - normalized_time = DltWindow.normalize_timestamp_precision(msg.storage_timestamp) - result.append( - Bunch( - time_stamp=msg.tmsp, - apid=msg.apid.decode("ascii"), - ctid=msg.ctid.decode("ascii"), - payload=payload, - raw_msg=msg, - epoch_time=normalized_time, - ) - ) - - if not full_match: - break - - if timeout is not None and time.time() - start_time >= timeout: - logger.debug("[DLT Window]: find function exceeded timeout set!") - break - - return result - - def total_count(self): - """ - Total number of DLT messages recorded - """ - return self._dlt_content.counter_total - - def filtered_count(self): - """ - Number of relevant DLT messages according to the filters provided - Includes all the non extended DLT messages - """ - return self._dlt_content.counter - - def queried_count(self): - """ - Number of relevant DLT messages according to the filters provided - and after (optionally) discarding non extended or extended DLT messages - """ - return self._queried_counter - - def clear(self): - DltReceive.remove_dlt_file(self._dlt_file) - - def get_dlt_file_path(self): - return self._dlt_file - - def get_logged_output(self, clear_after_read=False): - """ - Returns captured DLT logs as a single string. - - :param clear_after_read: If True, clears logs after reading. Defaults to True. - :return: String containing all log lines. - """ - logs = "\n".join(self._captured_logs) - if clear_after_read: - self._captured_logs.clear() - return logs - - def getdlt_live_buffer(self): - return self._captured_logs - - @staticmethod - def normalize_timestamp_precision(epoch_time): - try: - time_str = str(epoch_time) - seconds, microseconds = time_str.split(".") - - if len(microseconds) < 6: - microseconds = microseconds.rjust(6, "0") - - except Exception as error: - logger.error(f"Error normalizing timestamp precision: {error}") - return f"{seconds}.{microseconds}" diff --git a/deps/python_dlt.BUILD b/itf/plugins/dlt/BUILD similarity index 83% rename from deps/python_dlt.BUILD rename to itf/plugins/dlt/BUILD index 88cd612..333af39 100644 --- a/deps/python_dlt.BUILD +++ b/itf/plugins/dlt/BUILD @@ -10,12 +10,17 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* + load("@rules_python//python:defs.bzl", "py_library") py_library( - name = "python_dlt", - srcs = glob(["dlt/**/*.py"]), - imports = ["./dlt"], - tags = ["manual"], + name = "dlt", + srcs = [ + "__init__.py", + "dlt_receive.py", + ], visibility = ["//visibility:public"], + deps = [ + "//itf/core/utils", + ], ) diff --git a/itf/plugins/dlt/__init__.py b/itf/plugins/dlt/__init__.py new file mode 100644 index 0000000..c7009dd --- /dev/null +++ b/itf/plugins/dlt/__init__.py @@ -0,0 +1,56 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +import json +import pytest + +from itf.core.utils.bunch import Bunch +from itf.plugins.core import determine_target_scope + + +def pytest_addoption(parser): + parser.addoption( + "--dlt-config", + action="store", + required=False, + help="Path to json file with dlt configurations.", + ) + parser.addoption( + "--dlt-receive-path", + action="store", + required=True, + help="Path to dlt-receive binary.", + ) + + +@pytest.fixture(scope="session") +def dlt_config(request): + b = Bunch( + host_ip="127.0.0.1", + target_ip="127.0.0.1", + multicast_ips=[], + ) + + dlt_config_path = request.config.getoption("dlt_config") + if dlt_config_path: + with open(dlt_config_path) as f: + json_config = json.load(f) + if "host_ip" in json_config: + b.host_ip = json_config["host_ip"] + if "target_ip" in json_config: + b.target_ip = json_config["target_ip"] + if "multicast_ips" in json_config: + b.multicast_ips = json_config["multicast_ips"] + + b.dlt_receive_path = request.config.getoption("dlt_receive_path") + + return b diff --git a/itf/core/dlt/dlt_receive.py b/itf/plugins/dlt/dlt_receive.py similarity index 56% rename from itf/core/dlt/dlt_receive.py rename to itf/plugins/dlt/dlt_receive.py index 9a8d60b..5967bde 100644 --- a/itf/core/dlt/dlt_receive.py +++ b/itf/plugins/dlt/dlt_receive.py @@ -36,44 +36,37 @@ class DltReceive(ProcessWrapper): def __init__( self, - target_ip: str, protocol: Protocol = Protocol.UDP, + host_ip: str = None, + multicast_ips: list[str] = None, + target_ip: str = None, file_name: str = None, enable_file_output: bool = True, print_to_stdout: bool = False, logger_name: str = None, - sctf: bool = False, - data_router_config: dict = None, binary_path: str = None, ): """Initialize DltReceive instance. - :param str target_ip: IP address of the target to receive DLT logs from. :param Protocol protocol: Protocol to use for receiving DLT logs (TCP or UDP). + :param str host_ip: IP address to bind to in case of UDP. + :param list[str] multicast_ips: Multicast IPs to join to in case of UDP. + :param str target_ip: IP address to connect to in case of TCP. :param str file_name: Optional name for the output DLT file. If not provided, defaults to "dlt_receive.dlt" in the output directory. :param bool enable_file_output: If True, DLT logs will be saved to a file. :param bool print_to_stdout: If True, DLT logs will be printed to stdout. :param str logger_name: Optional name for the logger. If not provided, defaults to the basename of the binary path. - :param bool sctf: If True, uses SCTF-specific configurations. - :param dict data_router_config: Configuration for the data router - with keys "vlan_address" and "multicast_addresses". :param str binary_path: Path to the DLT receive binary. """ - self._target_ip = target_ip - self._protocol = protocol self._dlt_file_name = file_name or f"{get_output_dir()}/dlt_receive.dlt" - self._data_router_config = data_router_config - self._protocol_opts = DltReceive.protocol_arguments( - self._target_ip, self._protocol, sctf, self._data_router_config - ) - dlt_receive_args = ["-o", self._dlt_file_name] if enable_file_output else [] - dlt_receive_args += self._protocol_opts + dlt_receive_args += _protocol_arguments(protocol, host_ip, target_ip, multicast_ips) dlt_receive_args += ["-a"] if print_to_stdout else [] - if file_name and enable_file_output: - DltReceive.remove_dlt_file(self._dlt_file_name) + if self._dlt_file_name and enable_file_output: + if os.path.exists(self._dlt_file_name): + os.remove(self._dlt_file_name) super().__init__( binary_path, @@ -81,33 +74,26 @@ def __init__( logger_name=logger_name, ) - @staticmethod - def remove_dlt_file(target_file): - if os.path.exists(target_file): - os.remove(target_file) - - @staticmethod - def protocol_arguments(target_ip, protocol, sctf, data_router_config): - dlt_port = "3490" - proto_specific_opts = [] - - if protocol == Protocol.TCP: - proto_specific_opts = ["--tcp", target_ip] - elif protocol == Protocol.UDP: - net_if = target_ip if sctf else data_router_config["vlan_address"] - mcasts = data_router_config["multicast_addresses"] - mcast_ip = [val for pair in zip(["--mcast-ip"] * len(mcasts), mcasts) for val in pair] - proto_specific_opts = ["--udp"] + mcast_ip + ["--net-if", net_if, "--port", dlt_port] - else: - raise RuntimeError( - f"Unsupported Transport Layer Protocol provided: {protocol}. " - + "Supported are: " - + "[" - + ", ".join([str(name[1]) for name in Protocol.__members__.items()]) - + "]" - ) - - return proto_specific_opts - def file_name(self): return self._dlt_file_name + + +def _protocol_arguments(protocol, host_ip, target_ip, multicast_ips): + dlt_port = "3490" + proto_specific_opts = [] + + if protocol == Protocol.TCP: + proto_specific_opts = ["--tcp", target_ip] + elif protocol == Protocol.UDP: + mcast_ip = [val for pair in zip(["--mcast-ip"] * len(multicast_ips), multicast_ips) for val in pair] + proto_specific_opts = ["--udp"] + ["--net-if", host_ip, "--port", dlt_port] + mcast_ip + else: + raise RuntimeError( + f"Unsupported Transport Layer Protocol provided: {protocol}. " + + "Supported are: " + + "[" + + ", ".join([str(name[1]) for name in Protocol.__members__.items()]) + + "]" + ) + + return proto_specific_opts diff --git a/itf/plugins/plugins.bzl b/itf/plugins/plugins.bzl index e36fc8a..ce226b4 100644 --- a/itf/plugins/plugins.bzl +++ b/itf/plugins/plugins.bzl @@ -34,13 +34,29 @@ base = py_itf_plugin( "itf.core.base.base_plugin", ], args = [ - "--dlt_receive_path=$(location @score_itf//itf/core/dlt:dlt-receive_as_host)", ], data = [ ], data_as_exec = [ - "@score_itf//itf/core/dlt:dlt-receive_as_host", - "@score_itf//itf/core/dlt:libdlt_as_host.so", + ], + tags = [ + "local", + "manual", + ], +) + +dlt = py_itf_plugin( + py_library = "@score_itf//itf/plugins/dlt", + enabled_plugins = [ + "itf.plugins.dlt", + ], + args = [ + "--dlt-receive-path=$(location @score_itf//third_party/dlt:dlt-receive)", + ], + data = [ + ], + data_as_exec = [ + "@score_itf//third_party/dlt:dlt-receive", ], tags = [ "local", diff --git a/test/BUILD b/test/BUILD index e25705d..92b6e5c 100644 --- a/test/BUILD +++ b/test/BUILD @@ -11,7 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* load("//:defs.bzl", "py_itf_test") -load("//itf/plugins:plugins.bzl", "base", "docker") +load("//itf/plugins:plugins.bzl", "base", "dlt", "docker") py_itf_test( name = "test_rules_are_working_correctly", @@ -39,8 +39,8 @@ py_itf_test( srcs = [ "test_dlt.py", ], - deps = [ - "//itf/core/dlt", + plugins = [ + dlt, ], ) @@ -51,14 +51,17 @@ py_itf_test( ], args = [ "--target_config=$(location //config)", + "--dlt-config=$(location dlt_config.json)", "--ecu=s_core_ecu_qemu_bridge_network", "--qemu", ], data = [ + "dlt_config.json", "//config", ], plugins = [ base, + dlt, ], ) @@ -77,5 +80,6 @@ py_itf_test( ], plugins = [ base, + dlt, ], ) diff --git a/test/dlt_config.json b/test/dlt_config.json new file mode 100644 index 0000000..87fcaa2 --- /dev/null +++ b/test/dlt_config.json @@ -0,0 +1,7 @@ +{ + "target_ip": "192.168.122.76", + "host_ip": "192.168.122.1", + "multicast_ips": [ + "239.255.42.99" + ] +} diff --git a/test/test_dlt.py b/test/test_dlt.py index 3970f52..b08778a 100644 --- a/test/test_dlt.py +++ b/test/test_dlt.py @@ -10,24 +10,33 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -from itf.core.dlt.dlt_receive import DltReceive, Protocol import time +from itf.plugins.dlt.dlt_receive import DltReceive, Protocol -def test_dlt(): + +def test_dlt(dlt_config): + with DltReceive( + protocol=Protocol.UDP, + host_ip=dlt_config.host_ip, + target_ip=dlt_config.target_ip, + multicast_ips=dlt_config.multicast_ips, + binary_path=dlt_config.dlt_receive_path, + ): + time.sleep(1) + + +def test_dlt_custom_config(dlt_config): with DltReceive( - target_ip="127.0.0.1", protocol=Protocol.UDP, - binary_path="./itf/core/dlt/dlt-receive", - data_router_config={ - "vlan_address": "127.0.0.1", - "multicast_addresses": [ - "239.255.42.99", - "231.255.42.99", - "234.255.42.99", - "237.255.42.99", - ], - }, + host_ip="127.0.0.1", + target_ip="127.0.0.1", + multicast_ips=[ + "239.255.42.99", + "231.255.42.99", + "234.255.42.99", + "237.255.42.99", + ], + binary_path=dlt_config.dlt_receive_path, ): - time.sleep(5) - pass + time.sleep(1) diff --git a/deps/BUILD b/third_party/BUILD similarity index 100% rename from deps/BUILD rename to third_party/BUILD diff --git a/itf/core/dlt/__init__.py b/third_party/dlt/BUILD similarity index 80% rename from itf/core/dlt/__init__.py rename to third_party/dlt/BUILD index 6bdeed2..142c745 100644 --- a/itf/core/dlt/__init__.py +++ b/third_party/dlt/BUILD @@ -10,3 +10,10 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +alias( + name = "dlt-receive", + actual = "@dlt_daemon//:dlt-receive", + visibility = [ + "//visibility:public", + ], +) diff --git a/deps/dlt_daemon.BUILD b/third_party/dlt/dlt_daemon.BUILD similarity index 100% rename from deps/dlt_daemon.BUILD rename to third_party/dlt/dlt_daemon.BUILD