mirror of
https://github.com/platformio/platformio-core.git
synced 2025-07-30 18:17:13 +02:00
Control device monitor output with filters and text transformations
This commit is contained in:
@ -9,6 +9,7 @@ PlatformIO Core 4
|
||||
4.2.2 (2020-??-??)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Control device monitor output with `filters and text transformations <https://docs.platformio.org/page/userguide/device/cmd_monitor.html#cmd-device-monitor-filters>`__
|
||||
* Added support for Arm Mbed "module.json" ``dependencies`` field (`issue #3400 <https://github.com/platformio/platformio-core/issues/3400>`_)
|
||||
* Improved support for Arduino "library.properties" ``depends`` field
|
||||
* Fixed an issue when quitting from PlatformIO IDE does not shutdown PIO Home server
|
||||
|
2
docs
2
docs
Submodule docs updated: 1506c1bcbf...d6a2968edc
15
platformio/commands/device/__init__.py
Normal file
15
platformio/commands/device/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from platformio.commands.device.filters.base import DeviceMonitorFilter
|
@ -20,9 +20,9 @@ import click
|
||||
from serial.tools import miniterm
|
||||
|
||||
from platformio import exception, fs, util
|
||||
from platformio.compat import dump_json_to_unicode, load_python_module
|
||||
from platformio.commands.device import helpers as device_helpers
|
||||
from platformio.compat import dump_json_to_unicode
|
||||
from platformio.managers.platform import PlatformFactory
|
||||
from platformio.project.config import ProjectConfig
|
||||
from platformio.project.exception import NotPlatformIOProjectError
|
||||
|
||||
|
||||
@ -135,7 +135,7 @@ def device_list( # pylint: disable=too-many-branches
|
||||
help="Set the encoding for the serial port (e.g. hexlify, "
|
||||
"Latin1, UTF-8), default: UTF-8",
|
||||
)
|
||||
@click.option("--filter", "-f", multiple=True, help="Add text transformation")
|
||||
@click.option("--filter", "-f", multiple=True, help="Add filters/text transformations")
|
||||
@click.option(
|
||||
"--eol",
|
||||
default="CRLF",
|
||||
@ -174,15 +174,11 @@ def device_list( # pylint: disable=too-many-branches
|
||||
help="Load configuration from `platformio.ini` and specified environment",
|
||||
)
|
||||
def device_monitor(**kwargs): # pylint: disable=too-many-branches
|
||||
click.echo(
|
||||
"Looking for advanced Serial Monitor with UI? "
|
||||
"Check http://bit.ly/pio-advanced-monitor"
|
||||
)
|
||||
project_options = {}
|
||||
try:
|
||||
with fs.cd(kwargs["project_dir"]):
|
||||
project_options = get_project_options(kwargs["environment"])
|
||||
kwargs = apply_project_monitor_options(kwargs, project_options)
|
||||
project_options = device_helpers.get_project_options(kwargs["environment"])
|
||||
kwargs = device_helpers.apply_project_monitor_options(kwargs, project_options)
|
||||
except NotPlatformIOProjectError:
|
||||
pass
|
||||
|
||||
@ -190,17 +186,17 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches
|
||||
if "platform" in project_options:
|
||||
with fs.cd(kwargs["project_dir"]):
|
||||
platform = PlatformFactory.newPlatform(project_options["platform"])
|
||||
register_platform_filters(platform, kwargs["project_dir"], kwargs["environment"])
|
||||
device_helpers.register_platform_filters(
|
||||
platform, kwargs["project_dir"], kwargs["environment"]
|
||||
)
|
||||
|
||||
if not kwargs["port"]:
|
||||
ports = util.get_serial_ports(filter_hwid=True)
|
||||
if len(ports) == 1:
|
||||
kwargs["port"] = ports[0]["port"]
|
||||
elif "platform" in project_options and "board" in project_options:
|
||||
board_hwids = get_board_hwids(
|
||||
kwargs["project_dir"],
|
||||
platform,
|
||||
project_options["board"],
|
||||
board_hwids = device_helpers.get_board_hwids(
|
||||
kwargs["project_dir"], platform, project_options["board"],
|
||||
)
|
||||
for item in ports:
|
||||
for hwid in board_hwids:
|
||||
@ -217,12 +213,18 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches
|
||||
break
|
||||
|
||||
# override system argv with patched options
|
||||
sys.argv = ["monitor"] + options_to_argv(
|
||||
sys.argv = ["monitor"] + device_helpers.options_to_argv(
|
||||
kwargs,
|
||||
project_options,
|
||||
ignore=("port", "baud", "rts", "dtr", "environment", "project_dir"),
|
||||
)
|
||||
|
||||
if not kwargs["quiet"]:
|
||||
click.echo(
|
||||
"Available filters and text transformations: %s"
|
||||
% ", ".join(sorted(miniterm.TRANSFORMATIONS.keys()))
|
||||
)
|
||||
click.echo("More details at http://bit.ly/pio-monitor-filters")
|
||||
try:
|
||||
miniterm.main(
|
||||
default_port=kwargs["port"],
|
||||
@ -232,98 +234,3 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches
|
||||
)
|
||||
except Exception as e:
|
||||
raise exception.MinitermException(e)
|
||||
|
||||
def apply_project_monitor_options(cli_options, project_options):
|
||||
for k in ("port", "speed", "rts", "dtr"):
|
||||
k2 = "monitor_%s" % k
|
||||
if k == "speed":
|
||||
k = "baud"
|
||||
if cli_options[k] is None and k2 in project_options:
|
||||
cli_options[k] = project_options[k2]
|
||||
if k != "port":
|
||||
cli_options[k] = int(cli_options[k])
|
||||
return cli_options
|
||||
|
||||
|
||||
def options_to_argv(cli_options, project_options, ignore=None):
|
||||
result = project_options.get("monitor_flags", [])
|
||||
for k, v in cli_options.items():
|
||||
if v is None or (ignore and k in ignore):
|
||||
continue
|
||||
k = "--" + k.replace("_", "-")
|
||||
if k in project_options.get("monitor_flags", []):
|
||||
continue
|
||||
if isinstance(v, bool):
|
||||
if v:
|
||||
result.append(k)
|
||||
elif isinstance(v, tuple):
|
||||
for i in v:
|
||||
result.extend([k, i])
|
||||
else:
|
||||
result.extend([k, str(v)])
|
||||
return result
|
||||
|
||||
|
||||
def get_project_options(environment=None):
|
||||
config = ProjectConfig.get_instance()
|
||||
config.validate(envs=[environment] if environment else None)
|
||||
if not environment:
|
||||
default_envs = config.default_envs()
|
||||
if default_envs:
|
||||
environment = default_envs[0]
|
||||
else:
|
||||
environment = config.envs()[0]
|
||||
return config.items(env=environment, as_dict=True)
|
||||
|
||||
|
||||
def get_board_hwids(project_dir, platform, board):
|
||||
with fs.cd(project_dir):
|
||||
return (
|
||||
platform
|
||||
.board_config(board)
|
||||
.get("build.hwids", [])
|
||||
)
|
||||
|
||||
class DeviceMonitorFilter(miniterm.Transform):
|
||||
# NAME = "esp_exception_decoder" - all filters must have one
|
||||
|
||||
# Called by PlatformIO to pass context
|
||||
def __init__(self, project_dir, environment):
|
||||
super(DeviceMonitorFilter, self).__init__()
|
||||
|
||||
self.project_dir = project_dir
|
||||
self.environment = environment
|
||||
|
||||
self.config = ProjectConfig.get_instance()
|
||||
if not self.environment:
|
||||
default_envs = self.config.default_envs()
|
||||
if default_envs:
|
||||
self.environment = default_envs[0]
|
||||
else:
|
||||
self.environment = self.config.envs()[0]
|
||||
|
||||
# Called by the miniterm library when the filter is acutally used
|
||||
def __call__(self):
|
||||
return self
|
||||
|
||||
def register_platform_filters(platform, project_dir, environment):
|
||||
monitor_dir = os.path.join(platform.get_dir(), "monitor")
|
||||
if not os.path.isdir(monitor_dir):
|
||||
return
|
||||
|
||||
for fn in os.listdir(monitor_dir):
|
||||
if not fn.startswith("filter_") or not fn.endswith(".py"):
|
||||
continue
|
||||
path = os.path.join(monitor_dir, fn)
|
||||
if not os.path.isfile(path):
|
||||
continue
|
||||
|
||||
dot = fn.find(".")
|
||||
module = load_python_module("platformio.commands.device.%s" % fn[:dot], path)
|
||||
for key in dir(module):
|
||||
member = getattr(module, key)
|
||||
try:
|
||||
if issubclass(member, DeviceMonitorFilter) and hasattr(member, "NAME"):
|
||||
miniterm.TRANSFORMATIONS[member.NAME] = member(project_dir, environment)
|
||||
except TypeError:
|
||||
pass
|
13
platformio/commands/device/filters/__init__.py
Normal file
13
platformio/commands/device/filters/__init__.py
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
42
platformio/commands/device/filters/base.py
Normal file
42
platformio/commands/device/filters/base.py
Normal file
@ -0,0 +1,42 @@
|
||||
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from serial.tools import miniterm
|
||||
|
||||
from platformio.project.config import ProjectConfig
|
||||
|
||||
|
||||
class DeviceMonitorFilter(miniterm.Transform):
|
||||
def __init__(self, project_dir, environment):
|
||||
""" Called by PlatformIO to pass context """
|
||||
super(DeviceMonitorFilter, self).__init__()
|
||||
|
||||
self.project_dir = project_dir
|
||||
self.environment = environment
|
||||
|
||||
self.config = ProjectConfig.get_instance()
|
||||
if not self.environment:
|
||||
default_envs = self.config.default_envs()
|
||||
if default_envs:
|
||||
self.environment = default_envs[0]
|
||||
else:
|
||||
self.environment = self.config.envs()[0]
|
||||
|
||||
def __call__(self):
|
||||
""" Called by the miniterm library when the filter is actually used """
|
||||
return self
|
||||
|
||||
@property
|
||||
def NAME(self):
|
||||
raise NotImplementedError("Please declare NAME attribute for the filter class")
|
102
platformio/commands/device/helpers.py
Normal file
102
platformio/commands/device/helpers.py
Normal file
@ -0,0 +1,102 @@
|
||||
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import inspect
|
||||
import os
|
||||
|
||||
from serial.tools import miniterm
|
||||
|
||||
from platformio import fs
|
||||
from platformio.commands.device import DeviceMonitorFilter
|
||||
from platformio.compat import get_object_members, load_python_module
|
||||
from platformio.project.config import ProjectConfig
|
||||
|
||||
|
||||
def apply_project_monitor_options(cli_options, project_options):
|
||||
for k in ("port", "speed", "rts", "dtr"):
|
||||
k2 = "monitor_%s" % k
|
||||
if k == "speed":
|
||||
k = "baud"
|
||||
if cli_options[k] is None and k2 in project_options:
|
||||
cli_options[k] = project_options[k2]
|
||||
if k != "port":
|
||||
cli_options[k] = int(cli_options[k])
|
||||
return cli_options
|
||||
|
||||
|
||||
def options_to_argv(cli_options, project_options, ignore=None):
|
||||
confmon_flags = project_options.get("monitor_flags", [])
|
||||
result = confmon_flags[::]
|
||||
|
||||
for f in project_options.get("monitor_filters", []):
|
||||
result.extend(["--filter", f])
|
||||
|
||||
for k, v in cli_options.items():
|
||||
if v is None or (ignore and k in ignore):
|
||||
continue
|
||||
k = "--" + k.replace("_", "-")
|
||||
if k in confmon_flags:
|
||||
continue
|
||||
if isinstance(v, bool):
|
||||
if v:
|
||||
result.append(k)
|
||||
elif isinstance(v, tuple):
|
||||
for i in v:
|
||||
result.extend([k, i])
|
||||
else:
|
||||
result.extend([k, str(v)])
|
||||
return result
|
||||
|
||||
|
||||
def get_project_options(environment=None):
|
||||
config = ProjectConfig.get_instance()
|
||||
config.validate(envs=[environment] if environment else None)
|
||||
if not environment:
|
||||
default_envs = config.default_envs()
|
||||
if default_envs:
|
||||
environment = default_envs[0]
|
||||
else:
|
||||
environment = config.envs()[0]
|
||||
return config.items(env=environment, as_dict=True)
|
||||
|
||||
|
||||
def get_board_hwids(project_dir, platform, board):
|
||||
with fs.cd(project_dir):
|
||||
return platform.board_config(board).get("build.hwids", [])
|
||||
|
||||
|
||||
def register_platform_filters(platform, project_dir, environment):
|
||||
monitor_dir = os.path.join(platform.get_dir(), "monitor")
|
||||
if not os.path.isdir(monitor_dir):
|
||||
return
|
||||
|
||||
for fn in os.listdir(monitor_dir):
|
||||
if not fn.startswith("filter_") or not fn.endswith(".py"):
|
||||
continue
|
||||
path = os.path.join(monitor_dir, fn)
|
||||
if not os.path.isfile(path):
|
||||
continue
|
||||
|
||||
module = load_python_module(
|
||||
"platformio.commands.device.filters.%s" % fn[: fn.find(".")], path
|
||||
)
|
||||
for cls in get_object_members(module).values():
|
||||
if (
|
||||
not inspect.isclass(cls)
|
||||
or not issubclass(cls, DeviceMonitorFilter)
|
||||
or cls == DeviceMonitorFilter
|
||||
):
|
||||
continue
|
||||
obj = cls(project_dir, environment)
|
||||
miniterm.TRANSFORMATIONS[obj.NAME] = obj
|
@ -21,7 +21,8 @@ from time import sleep
|
||||
import click
|
||||
|
||||
from platformio import exception, fs
|
||||
from platformio.commands import device
|
||||
from platformio.commands.device import helpers as device_helpers
|
||||
from platformio.commands.device.command import device_monitor as cmd_device_monitor
|
||||
from platformio.managers.core import pioplus_call
|
||||
from platformio.project.exception import NotPlatformIOProjectError
|
||||
|
||||
@ -197,8 +198,8 @@ def device_monitor(ctx, **kwargs):
|
||||
project_options = {}
|
||||
try:
|
||||
with fs.cd(kwargs["project_dir"]):
|
||||
project_options = device.get_project_options(kwargs["environment"])
|
||||
kwargs = device.apply_project_monitor_options(kwargs, project_options)
|
||||
project_options = device_helpers.get_project_options(kwargs["environment"])
|
||||
kwargs = device_helpers.apply_project_monitor_options(kwargs, project_options)
|
||||
except NotPlatformIOProjectError:
|
||||
pass
|
||||
|
||||
@ -206,7 +207,7 @@ def device_monitor(ctx, **kwargs):
|
||||
|
||||
def _tx_target(sock_dir):
|
||||
pioplus_argv = ["remote", "device", "monitor"]
|
||||
pioplus_argv.extend(device.options_to_argv(kwargs, project_options))
|
||||
pioplus_argv.extend(device_helpers.options_to_argv(kwargs, project_options))
|
||||
pioplus_argv.extend(["--sock", sock_dir])
|
||||
try:
|
||||
pioplus_call(pioplus_argv)
|
||||
@ -224,7 +225,7 @@ def device_monitor(ctx, **kwargs):
|
||||
return
|
||||
with open(sock_file) as fp:
|
||||
kwargs["port"] = fp.read()
|
||||
ctx.invoke(device.device_monitor, **kwargs)
|
||||
ctx.invoke(cmd_device_monitor, **kwargs)
|
||||
t.join(2)
|
||||
finally:
|
||||
fs.rmtree(sock_dir)
|
||||
|
@ -21,7 +21,7 @@ import click
|
||||
from tabulate import tabulate
|
||||
|
||||
from platformio import app, exception, fs, util
|
||||
from platformio.commands.device import device_monitor as cmd_device_monitor
|
||||
from platformio.commands.device.command import device_monitor as cmd_device_monitor
|
||||
from platformio.commands.run.helpers import clean_build_dir, handle_legacy_libdeps
|
||||
from platformio.commands.run.processor import EnvironmentProcessor
|
||||
from platformio.commands.test.processor import CTX_META_TEST_IS_RUNNING
|
||||
|
@ -445,6 +445,14 @@ ProjectOptions = OrderedDict(
|
||||
oldnames=["monitor_baud"],
|
||||
default=9600,
|
||||
),
|
||||
ConfigEnvOption(
|
||||
group="monitor",
|
||||
name="monitor_filters",
|
||||
description=(
|
||||
"Apply the filters and text transformations to monitor output"
|
||||
),
|
||||
multiple=True,
|
||||
),
|
||||
ConfigEnvOption(
|
||||
group="monitor",
|
||||
name="monitor_rts",
|
||||
|
Reference in New Issue
Block a user