Skip to content

Commit

Permalink
Replace lighting_console with chip_rpc.console (#8302)
Browse files Browse the repository at this point in the history
Create a single rpc console which has all chip RPC protos to talk
to any chip RPC enabled device, this avoids needing to create a
new console for every app.

Also update Pigweed, and integrate the new console changes and UI.

Move the protos from the ipv6only and lock-app examples into common
folders.

Update all the example docs to use the new console.
  • Loading branch information
rgoliver authored and pull[bot] committed Aug 4, 2021
1 parent 3f647e4 commit 5871570
Show file tree
Hide file tree
Showing 30 changed files with 302 additions and 98 deletions.
22 changes: 22 additions & 0 deletions examples/common/pigweed/rpc_console/.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright (c) 2021 Project CHIP Authors
#
# 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("//build_overrides/build.gni")

# The location of the build configuration file.
buildconfig = "${build_root}/config/BUILDCONFIG.gn"

default_args = {
chip_enable_pw_rpc = true
}
22 changes: 22 additions & 0 deletions examples/common/pigweed/rpc_console/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright (c) 2021 Project CHIP Authors
#
# 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("//build_overrides/chip.gni")
import("//build_overrides/pigweed.gni")

group("default") {
deps = [
"py:chip_rpc.install",
"py:chip_rpc_wheel",
]
}
46 changes: 46 additions & 0 deletions examples/common/pigweed/rpc_console/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# CHIP RPC CONSOLE

This python application provides a console for interacting with rpc-enabled chip
devices.

The console uses the [pigweed pw_console](https://pigweed.dev/pw_console/), but
with customizations to work better with CHIP, including containing all rpc proto
files required for CHIP.

- [CHIP RPC CONSOLE](#chip-rpc-console)
- [Building](#building)
- [Running](#running)

---

## Building

If this is the first time using the checkout the environment must first be
bootstrapped to install all dependencies.

$ source ./scripts/bootstrap.sh

If bootstrap has previously be run simply activate.

$ source ./scripts/activate.sh

The python console is built and installed in the venv using gn:

$ gn gen out/debug
$ ninja -C out/debug

After building the output directory also contains a folder
(chip_rpc_console_wheels), with all the wheels required for the tool. These
can be used to install the console without needing the sdk. Simply install
all the wheels in the folder:
$ pip install chip_rpc_console_wheels/*.whl

## Running

To start the console provide the path to the device, for example:

$ python -m chip_rpc.console --device /dev/ttyUSB0

An example RPC command:

$ rpcs.chip.rpc.DeviceCommon.GetDeviceInfo()
1 change: 1 addition & 0 deletions examples/common/pigweed/rpc_console/build_overrides
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,29 @@ import("$dir_pw_build/mirror_tree.gni")
import("$dir_pw_build/python.gni")
import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni")

pw_python_package("lighting_app") {
pw_python_package("chip_rpc") {
generate_setup = {
name = "lighting_app"
name = "chip_rpc"
version = "0.0.1"
install_requires = [ "ipython" ]
}
sources = [ "lighting_app/rpc_console.py" ]
sources = [ "chip_rpc/console.py" ]
python_deps = [
"$dir_pw_console/py",
"$dir_pw_hdlc/py",
"$dir_pw_log_tokenized/py",
"$dir_pw_protobuf_compiler/py",
"$dir_pw_rpc/py",
"${chip_root}/examples/common/pigweed:button_service.python",
"${chip_root}/examples/common/pigweed:device_service.python",
"${chip_root}/examples/ipv6only-app/common:wifi_service.python",
"${chip_root}/examples/lighting-app/lighting-common:lighting_service.python",
"${chip_root}/examples/lock-app/lock-common:locking_service.python",
]
}

pw_mirror_tree("lighting_app_wheel") {
pw_mirror_tree("chip_rpc_wheel") {
path_data_keys = [ "pw_python_package_wheels" ]
deps = [ ":lighting_app.wheel" ]
directory = "$root_out_dir/lighting_app_wheels"
deps = [ ":chip_rpc.wheel" ]
directory = "$root_out_dir/chip_rpc_console_wheels"
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
To start the console, provide a serial port as the --device argument
python -m lighting_app.rpc_console --device /dev/ttyUSB0
python -m chip_rpc.console --device /dev/ttyUSB0
Alternatively to connect to a linux CHIP device provide the port.
python -m lighting_app.rpc_console -s localhost:33000
python -m chip_rpc.console -s localhost:33000
This starts an IPython console for communicating with the connected device. A
few variables are predefined in the interactive console. These include:
Expand All @@ -37,30 +37,38 @@
"""

import argparse
import glob
import logging
from pathlib import Path
import sys
from typing import Any, Collection, Iterable, Iterator
from typing import Any, BinaryIO
import socket

import IPython # type: ignore
from inspect import cleandoc
import serial # type: ignore

from pw_hdlc.rpc import HdlcRpcClient, default_channels, write_to_file
import pw_cli.log
from pw_console.console_app import embed
from pw_console.__main__ import create_temp_log_file
from pw_hdlc.rpc import HdlcRpcClient, default_channels

# Protos for lighting app
# Protos
from button_service import button_service_pb2
from lighting_service import lighting_service_pb2
from device_service import device_service_pb2
from lighting_service import lighting_service_pb2
from locking_service import locking_service_pb2
from wifi_service import wifi_service_pb2

PW_LOG = logging.getLogger(__name__)
_LOG = logging.getLogger(__name__)
_DEVICE_LOG = logging.getLogger('rpc_device')

PW_RPC_MAX_PACKET_SIZE = 256
SOCKET_SERVER = 'localhost'
SOCKET_PORT = 33000

PROTOS = [button_service_pb2, lighting_service_pb2, device_service_pb2]
PROTOS = [button_service_pb2,
lighting_service_pb2,
locking_service_pb2,
wifi_service_pb2,
device_service_pb2]


def _parse_args():
"""Parses and returns the command line arguments."""
Expand Down Expand Up @@ -94,11 +102,27 @@ def _start_ipython_terminal(client: HdlcRpcClient) -> None:
channel_client=client.client.channel(1),
rpcs=client.client.channel(1).rpcs,
protos=client.protos.packages,
# Include the active pane logger for creating logs in the repl.
LOG=_DEVICE_LOG,
)

print(__doc__) # Print the banner
IPython.terminal.embed.InteractiveShellEmbed().mainloop(
local_ns=local_variables, module=argparse.Namespace())
welcome_message = cleandoc("""
Welcome to the CHIP RPC Console!
Press F1 for help.
Example commands:
rpcs.chip.rpc.DeviceCommon.GetDeviceInfo()
LOG.warning('Message appears console log window.')
""")

embed(global_vars=local_variables,
local_vars=None,
loggers=[_DEVICE_LOG],
repl_startup_message=welcome_message,
help_text=__doc__,
app_title="CHIP Console")


class SocketClientImpl:
Expand All @@ -122,17 +146,27 @@ def read(self, num_bytes: int = PW_RPC_MAX_PACKET_SIZE):
return self.socket.recv(num_bytes)


def write_to_output(data: bytes,
unused_output: BinaryIO = sys.stdout.buffer,):
log_line = data

for line in log_line.decode(errors="surrogateescape").splitlines():
_DEVICE_LOG.info(line)


def console(device: str, baudrate: int,
socket_addr: str, output: Any) -> int:
"""Starts an interactive RPC console for HDLC."""
# argparse.FileType doesn't correctly handle '-' for binary files.
if output is sys.stdout:
output = sys.stdout.buffer

logfile = create_temp_log_file()
pw_cli.log.install(logging.INFO, True, False, logfile)

if socket_addr is None:
serial_device = serial.Serial(device, baudrate, timeout=1)
read = lambda: serial_device.read(8192)
def read(): return serial_device.read(8192)
write = serial_device.write
else:
try:
Expand All @@ -145,7 +179,7 @@ def console(device: str, baudrate: int,

_start_ipython_terminal(
HdlcRpcClient(read, PROTOS, default_channels(write),
lambda data: write_to_file(data, output)))
lambda data: write_to_output(data, output)))
return 0


Expand Down
27 changes: 27 additions & 0 deletions examples/ipv6only-app/common/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright (c) 2021 Project CHIP Authors
#
# 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("//build_overrides/chip.gni")
import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni")

if (chip_enable_pw_rpc) {
import("//build_overrides/pigweed.gni")
import("$dir_pw_protobuf_compiler/proto.gni")

pw_proto_library("wifi_service") {
sources = [ "wifi_service/wifi_service.proto" ]
inputs = [ "wifi_service/wifi_service.options" ]
deps = [ "$dir_pw_protobuf:common_protos" ]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ syntax = "proto3";

package chip.rpc;

// TODO: Use pw.protobuf.Empty; currently not using it so that this file has no
// dependencies and can be loaded into the hdlc console python tool.
message Empty {
}
import 'pw_protobuf_protos/common.proto';

message Channel {
uint32 channel = 1;
Expand Down Expand Up @@ -150,22 +147,22 @@ message ConnectionResult {
CONNECTION_ERROR error = 1;
}

// The GDMWifiBase service provides the common RPC interface for interacting
// The Wifi service provides the common RPC interface for interacting
// with a WIFI capable CHIP device.
// The current state can be retrieved using the various 'Get' RPCs.
// A device can be connected to an AP using the StartScan, and Connect RPCs.
service GDMWifiBase {
rpc GetChannel(Empty) returns (Channel) {}
rpc GetSsid(Empty) returns (Ssid) {}
rpc GetState(Empty) returns (State) {}
rpc GetMacAddress(Empty) returns (MacAddress) {}
// A device can be connected to an AP using the StartScan, and Connect RPCs.
service Wifi {
rpc GetChannel(pw.protobuf.Empty) returns (Channel) {}
rpc GetSsid(pw.protobuf.Empty) returns (Ssid) {}
rpc GetState(pw.protobuf.Empty) returns (State) {}
rpc GetMacAddress(pw.protobuf.Empty) returns (MacAddress) {}

rpc GetWiFiInterface(Empty) returns (WiFiInterface) {}
rpc GetIP4Address(Empty) returns (IP4Address) {}
rpc GetIP6Address(Empty) returns (IP6Address) {}
rpc GetWiFiInterface(pw.protobuf.Empty) returns (WiFiInterface) {}
rpc GetIP4Address(pw.protobuf.Empty) returns (IP4Address) {}
rpc GetIP6Address(pw.protobuf.Empty) returns (IP6Address) {}

rpc StartScan(ScanConfig) returns (stream ScanResults) {}
rpc StopScan(Empty) returns (Empty) {}
rpc StopScan(pw.protobuf.Empty) returns (pw.protobuf.Empty) {}
rpc Connect(ConnectionData) returns (ConnectionResult) {}
rpc Disconnect(Empty) returns (Empty) {}
rpc Disconnect(pw.protobuf.Empty) returns (pw.protobuf.Empty) {}
}
13 changes: 7 additions & 6 deletions examples/ipv6only-app/esp32/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,18 @@ make sure the IDF_PATH has been exported(See the manual setup steps above).

## Testing the Example Application

Run the following command to start an interactive Python shell, where the Wifi
RPC commands can be invoked:
Build or install the [rpc console](../../common/pigweed/rpc_console/README.md)

python -m pw_hdlc.rpc_console --device /dev/ttyUSB0 -b 115200 ../common/gdm_wifi_base_rpc.proto
Start the console:

$ python -m chip_rpc.console --device /dev/ttyUSB0 -b 115200

An example flow of performing a scan, connecting, and getting the IPv6 address:

scan = rpcs.chip.rpc.GDMWifiBase.StartScan(pw_rpc_timeout_s=5)
scan = rpcs.chip.rpc.Wifi.StartScan(pw_rpc_timeout_s=5)
ap = next(filter(lambda a: b"SSID\000" in a.ssid, next(scan.responses()).aps))

connect = protos.chip.rpc.ConnectionData(ssid=ap.ssid,security_type=ap.security_type,secret=b"PASSWORD")
rpcs.chip.rpc.GDMWifiBase.Connect(connect, pw_rpc_timeout_s=10)
rpcs.chip.rpc.Wifi.Connect(connect, pw_rpc_timeout_s=10)

rpcs.chip.rpc.GDMWifiBase.GetIP6Address()
rpcs.chip.rpc.Wifi.GetIP6Address()
Loading

0 comments on commit 5871570

Please sign in to comment.