Skip to content

Commit

Permalink
Device Monitor Filter API, implement "time" and "log2file" filters // R…
Browse files Browse the repository at this point in the history
…esolve #981 Resolve #670
  • Loading branch information
ivankravets committed Mar 17, 2020
1 parent 5a72033 commit 0df7241
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 23 deletions.
10 changes: 8 additions & 2 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ PlatformIO Core 4
4.3.0 (2020-??-??)
~~~~~~~~~~~~~~~~~~

* Added initial support for an official `PlatformIO for CLion IDE <https://docs.platformio.org/page/integration/ide/clion.html>`__ plugin:
* Initial support for an official `PlatformIO for CLion IDE <https://docs.platformio.org/page/integration/ide/clion.html>`__ plugin:

- Smart C and C++ editor
- Code refactoring
Expand All @@ -18,7 +18,13 @@ PlatformIO Core 4
- Building, Uploading, Testing
- Integrated debugger (inline variable view, conditional breakpoints, expressions, watchpoints, peripheral registers, multi-thread support, etc.)

* Control device monitor output with `filters and text transformations <https://docs.platformio.org/page/userguide/device/cmd_monitor.html#cmd-device-monitor-filters>`__ (`pull #3383 <https://github.com/platformio/platformio-core/pull/3383>`_)
* `Device Monitor 2.0 <https://docs.platformio.org/page/core/userguide/device/cmd_monitor.html>`__

- Added **PlatformIO Device Monitor Filter API** (dev-platforms can extend base device monitor with a custom functionality, such as exception decoding) (`pull #3383 <https://github.com/platformio/platformio-core/pull/3383>`_)
- Configure project device monitor with `monitor_filters <https://docs.platformio.org/page/projectconf/section_env_monitor.html#monitor-filters>`__ option
- Show a timestamp for each new line with ``time`` filter (`issue #981 <https://github.com/platformio/platformio-core/issues/981>`_)
- `Capture device monitor output to a file <https://docs.platformio.org/page/core/userguide/device/cmd_monitor.html#capture-output-to-a-file>`__ with ``log2file`` filter (`issue #670 <https://github.com/platformio/platformio-core/issues/670>`_)

* 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
Expand Down
2 changes: 1 addition & 1 deletion docs
11 changes: 9 additions & 2 deletions platformio/commands/device/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ 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
# load default monitor filters
filters_dir = os.path.join(fs.get_source_dir(), "commands", "device", "filters")
for name in os.listdir(filters_dir):
if not name.endswith(".py"):
continue
device_helpers.load_monitor_filter(os.path.join(filters_dir, name))

project_options = {}
try:
with fs.cd(kwargs["project_dir"]):
Expand Down Expand Up @@ -221,10 +228,10 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches

if not kwargs["quiet"]:
click.echo(
"Available filters and text transformations: %s"
"--- Available filters and text transformations: %s"
% ", ".join(sorted(miniterm.TRANSFORMATIONS.keys()))
)
click.echo("More details at http://bit.ly/pio-monitor-filters")
click.echo("--- More details at http://bit.ly/pio-monitor-filters")
try:
miniterm.main(
default_port=kwargs["port"],
Expand Down
4 changes: 2 additions & 2 deletions platformio/commands/device/filters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


class DeviceMonitorFilter(miniterm.Transform):
def __init__(self, project_dir, environment):
def __init__(self, project_dir=None, environment=None):
""" Called by PlatformIO to pass context """
super(DeviceMonitorFilter, self).__init__()

Expand All @@ -30,7 +30,7 @@ def __init__(self, project_dir, environment):
default_envs = self.config.default_envs()
if default_envs:
self.environment = default_envs[0]
else:
elif self.config.envs():
self.environment = self.config.envs()[0]

def __call__(self):
Expand Down
44 changes: 44 additions & 0 deletions platformio/commands/device/filters/log2file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright (c) 2014-present PlatformIO <[email protected]>
#
# 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 io
import os.path
from datetime import datetime

from platformio.commands.device import DeviceMonitorFilter


class LogToFile(DeviceMonitorFilter):
NAME = "log2file"

def __init__(self, *args, **kwargs):
super(LogToFile, self).__init__(*args, **kwargs)
self._log_fp = None

def __call__(self):
log_file_name = "platformio-device-monitor-%s.log" % datetime.now().strftime(
"%y%m%d-%H%M%S"
)
print("--- Logging an output to %s" % os.path.abspath(log_file_name))
self._log_fp = io.open(log_file_name, "w", encoding="utf-8")
return self

def __del__(self):
if self._log_fp:
self._log_fp.close()

def rx(self, text):
self._log_fp.write(text)
self._log_fp.flush()
return text
34 changes: 34 additions & 0 deletions platformio/commands/device/filters/time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (c) 2014-present PlatformIO <[email protected]>
#
# 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 datetime import datetime

from platformio.commands.device import DeviceMonitorFilter


class Timestamp(DeviceMonitorFilter):
NAME = "time"

def __init__(self, *args, **kwargs):
super(Timestamp, self).__init__(*args, **kwargs)
self._first_text_received = False

def rx(self, text):
if self._first_text_received and "\n" not in text:
return text
timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
if not self._first_text_received:
self._first_text_received = True
return "%s > %s" % (timestamp, text)
return text.replace("\n", "\n%s > " % timestamp)
36 changes: 20 additions & 16 deletions platformio/commands/device/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,27 +76,31 @@ def get_board_hwids(project_dir, platform, board):
return platform.board_config(board).get("build.hwids", [])


def load_monitor_filter(path, project_dir=None, environment=None):
name = os.path.basename(path)
name = name[: name.find(".")]
module = load_python_module("platformio.commands.device.filters.%s" % name, 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
return True


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"):
for name in os.listdir(monitor_dir):
if not name.startswith("filter_") or not name.endswith(".py"):
continue
path = os.path.join(monitor_dir, fn)
path = os.path.join(monitor_dir, name)
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
load_monitor_filter(path, project_dir, environment)

0 comments on commit 0df7241

Please sign in to comment.