1.. _module-pw_console-embedding: 2 3=============== 4Embedding Guide 5=============== 6.. pigweed-module-subpage:: 7 :name: pw_console 8 9------------- 10Using embed() 11------------- 12``pw console`` is invoked by calling ``PwConsoleEmbed().embed()`` in your 13own Python script. For a complete example of an embedded device console script see 14:bdg-link-primary-line:`pw_system/py/pw_system/console.py <https://cs.pigweed.dev/pigweed/+/main:pw_system/py/pw_system/console.py>`. 15 16.. automodule:: pw_console.embed 17 :members: PwConsoleEmbed 18 :undoc-members: 19 :show-inheritance: 20 21.. _module-pw_console-embedding-logstore: 22 23.. autoclass:: pw_console.log_store.LogStore 24 :members: __init__ 25 :undoc-members: 26 :show-inheritance: 27 28.. _module-pw_console-embedding-plugins: 29 30Adding Plugins 31============== 32User plugin instances are created before starting-up and passed to the Pigweed 33Console embed instance. Typically, a console is started by creating a 34``PwConsoleEmbed()`` instance, calling customization functions, then calling 35``.embed()`` as shown in `Using embed()`_. Adding plugins functions similarly by 36calling ``add_top_toolbar``, ``add_bottom_toolbar``, 37``add_floating_window_plugin`` or ``add_window_plugin``. For example: 38 39.. code-block:: python 40 41 # Create plugin instances 42 user_toolbar1 = DeviceStatusToolbar(device=client.client.channel(1)) 43 user_toolbar2 = BandwithToolbar() 44 user_device_window = CustomWindowPlugin() 45 46 console = PwConsoleEmbed( 47 global_vars=local_variables, 48 loggers={ 49 'Device Logs': [logging.getLogger('rpc_device')], 50 'Host Logs': [logging.getLogger()], 51 }, 52 ... 53 ) 54 55 # Add toolbar plugins 56 console.add_top_toolbar(user_toolbar1) 57 console.add_bottom_toolbar(user_toolbar2) 58 59 # Add Window plugins 60 console.add_window_plugin(user_device_window) 61 62 # Start the console 63 console.embed() 64 65------------------- 66Adding Log Metadata 67------------------- 68``pw_console`` can display log messages in a table with justified columns for 69metadata fields provided by :ref:`module-pw_log_tokenized`. 70 71It is also possible to manually add values that should be displayed in columns 72using the ``extra`` keyword argument when logging from Python. See the `Python's 73logging documentation`_ for how ``extra`` works. A dict of name, value pairs can 74be passed in as the ``extra_metadata_fields`` variable. For example, the 75following code will create a log message with two custom columns titled 76``module`` and ``timestamp``. 77 78.. code-block:: python 79 80 import logging 81 82 LOG = logging.getLogger('log_source_1') 83 84 LOG.info( 85 'Hello there!', 86 extra={ 87 'extra_metadata_fields': { 88 'module': 'cool', 89 'timestamp': 1.2345, 90 } 91 } 92 ) 93 94--------------------- 95Debugging Serial Data 96--------------------- 97``pw_console`` is often used to communicate with devices using `pySerial 98<https://pythonhosted.org/pyserial/>`_ or 99``pw_console.socket_client.SocketClient``. To monitor the raw data flowing over 100the wire, ``pw_console`` provides simple wrappers for pySerial and socket client 101instances that log data for each read and write call. 102 103Logging data with PySerial 104========================== 105.. code-block:: python 106 107 # Instead of 'import serial' use this import: 108 from pw_console.pyserial_wrapper import SerialWithLogging 109 110 serial_device = SerialWithLogging('/dev/ttyUSB0', 115200, timeout=1) 111 112Logging data with sockets 113========================= 114.. code-block:: python 115 116 from pw_console.socket_client import SocketClientWithLogging 117 118 # Name resolution with explicit port 119 serial_device = SocketClientWithLogging('localhost:1234') 120 # Name resolution with default port. 121 serial_device = SocketClientWithLogging('pigweed.dev') 122 # Link-local IPv6 address with explicit port. 123 serial_device = SocketClientWithLogging('[fe80::100%enp1s0]:1234') 124 # Link-local IPv6 address with default port. 125 serial_device = SocketClientWithLogging('[fe80::100%enp1s0]') 126 # IPv4 address with port. 127 serial_device = SocketClientWithLogging('1.2.3.4:5678') 128 129.. tip:: 130 The ``SocketClient`` takes an optional callback called when a disconnect is 131 detected. The ``pw_system`` console provides an example reconnect routine. 132 133With the above examples each ``serial_device.read`` and ``write`` call will 134create a log message to the ``pw_console.serial_debug_logger`` Python 135logger. This logger can then be included as a log window pane in the 136``PwConsoleEmbed()`` call. 137 138.. code-block:: python 139 140 import logging 141 from pw_console import PwConsoleEmbed 142 143 console = PwConsoleEmbed( 144 global_vars=globals(), 145 local_vars=locals(), 146 loggers={ 147 'Host Logs': [ 148 # Root Python logger 149 logging.getLogger(''), 150 # Your current Python package logger. 151 logging.getLogger(__package__) 152 ], 153 'Device Logs': [ 154 logging.getLogger('usb_gadget') 155 ], 156 'Serial Debug': [ 157 # New log window to display serial read and writes 158 logging.getLogger('pw_console.serial_debug_logger') 159 ], 160 }, 161 app_title='CoolConsole', 162 ) 163 # Then run the console with: 164 console.embed() 165 166.. figure:: images/serial_debug.svg 167 :alt: Serial debug pw_console screenshot. 168 169 Screenshot of issuing an Echo RPC with serial debug logging. 170 171 172.. _Python's logging documentation: https://docs.python.org/3/library/logging.html#logging.Logger.debug 173 174.. _module-pw_console-embedding-ipython: 175 176------------------------------ 177Embeddeding other interpreters 178------------------------------ 179The Pigweed console is optimized for use with Pigweed, but other embedded Python 180interpreters may be used to interact with Pigweed devices. Popular options 181include `IPython <https://ipython.org/>`_ and `bpython 182<https://bpython-interpreter.org/>`_. 183 184Embedding IPython is similar to embedding ``pw_console``. After ``import 185IPython``, call ``IPython.start_ipython()`` with the set of variables to expose 186in the console. See `Embedding IPython 187<https://ipython.readthedocs.io/en/stable/interactive/reference.html#embedding>`_ 188for details. 189 190.. code-block:: python 191 192 IPython.start_ipython( 193 argv=[], 194 display_banner=False, 195 user_ns=local_variables, 196 ) 197 return 198