Skip to content

Commit

Permalink
refactor(hwil): refactors the hwil command implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
pallabpain committed Jun 13, 2024
1 parent adc91e9 commit fa6a38c
Show file tree
Hide file tree
Showing 13 changed files with 367 additions and 853 deletions.
10 changes: 10 additions & 0 deletions docs/source/hwil.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Hardware-in-Loop
================

.. toctree::
:maxdepth: 3
:caption: Contents:

.. click:: riocli.hwil:hwildevice
:prog: rio hwil
:nested: full
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Rapyuta CLI has commands for all rapyuta.io resources. You can read more about t
Deployment <deployment>
Device <device>
Disk <disk>
Hardware-in-Loop <hwil>
ManagedService <managedservice>
Network <network>
Organization <organization>
Expand Down
6 changes: 3 additions & 3 deletions riocli/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from riocli.exceptions import LoggedOut, NoOrganizationSelected, NoProjectSelected
from riocli.v2client import Client as v2Client
from riocli.hwilclient import Client as hwilClient
from riocli.hwilclient import Client as HwilClient


class Configuration(object):
Expand Down Expand Up @@ -110,7 +110,7 @@ def new_v2_client(self: Configuration, with_project: bool = True) -> v2Client:

return v2Client(self, auth_token=token, project=project)

def new_hwil_client(self: Configuration) -> hwilClient:
def new_hwil_client(self: Configuration) -> HwilClient:
if 'hwil_auth_token' not in self.data:
raise LoggedOut

Expand All @@ -119,7 +119,7 @@ def new_hwil_client(self: Configuration) -> hwilClient:

token = self.data.get('hwil_auth_token', None)

return hwilClient(auth_token=token)
return HwilClient(auth_token=token)

def get_auth_header(self: Configuration) -> dict:
if not ('auth_token' in self.data and 'project_id' in self.data):
Expand Down
16 changes: 9 additions & 7 deletions riocli/hwil/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2021 Rapyuta Robotics
# Copyright 2024 Rapyuta Robotics
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -14,23 +14,24 @@
import click
from click_help_colors import HelpColorsGroup

from riocli.constants import Colors
from riocli.hwil.create import create_device
from riocli.hwil.list import list_devices
from riocli.hwil.delete import delete_device
from riocli.hwil.execute import execute
from riocli.hwil.inspect import inspect_device
from riocli.hwil.list import list_devices
from riocli.hwil.login import login


@click.group(
name="hwil",
invoke_without_command=False,
cls=HelpColorsGroup,
help_headers_color='yellow',
help_options_color='green',
help_headers_color=Colors.YELLOW,
help_options_color=Colors.GREEN,
)
def hwildevice():
"""
HWIL Devices on Rapyuta.io
"""
"""Manage Hardware-in-the-Loop (HWIL) devices"""
pass


Expand All @@ -39,3 +40,4 @@ def hwildevice():
hwildevice.add_command(list_devices)
hwildevice.add_command(delete_device)
hwildevice.add_command(inspect_device)
hwildevice.add_command(execute)
114 changes: 42 additions & 72 deletions riocli/hwil/create.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2021 Rapyuta Robotics
# Copyright 2024 Rapyuta Robotics
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -11,94 +11,64 @@
# 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 time
import typing

import click
from click_spinner import spinner
from click_help_colors import HelpColorsCommand
from riocli.constants import Colors
from rapyuta_io.utils import ConflictError
from rapyuta_io.clients.device import DevicePythonVersion, Device, DeviceStatus
from riocli.device.util import find_device_guid
from riocli.hwil.util import name_to_id
from yaspin.api import Yaspin

from riocli.config import new_client, new_hwil_client
from riocli.hwilclient.client import Client
from rapyuta_io import Client as v1Client
from riocli.config import new_hwil_client
from riocli.constants import Colors, Symbols
from riocli.utils.spinner import with_spinner


@click.command('create',
cls=HelpColorsCommand,
help_headers_color=Colors.YELLOW,
help_options_color=Colors.GREEN,)
@click.option('--arch', 'arch', help='device family type',
@click.command(
'create',
cls=HelpColorsCommand,
help_headers_color=Colors.YELLOW,
help_options_color=Colors.GREEN,
)
@click.option('--arch', 'arch', help='Device architecture',
type=click.Choice(['amd64', 'arm64']), default='amd64')
@click.option('--os', 'os', help='type of os',
@click.option('--os', 'os', help='Type of the OS',
type=click.Choice(['debian', 'ubuntu']), default='ubuntu')
@click.option('--codename', 'codename', help='code name of os',
@click.option('--codename', 'codename', help='Code name of the OS',
type=click.Choice(['bionic', 'focal', 'jammy', 'bullseye']), default='focal')
@click.option('--onboard', 'onboard', is_flag=True, type=bool, default=False)
@click.argument('device-name', type=str)
@with_spinner(text='Creating device...')
@click.pass_context
def create_device(
ctx: click.Context,
device_name: str,
arch: str,
os: str,
codename: str,
onboard: bool,
spinner: Yaspin = None,
) -> None:
"""
Create a new virtual device on the cloud
"""
client = new_client()
hwil_client = new_hwil_client()
try:
with spinner():
try:
hwil_client.create_device(device_name, arch, os, codename)
click.secho('HWIL Device created successfully!', fg='green')
except ConflictError:
click.secho('HWIL Device {} already exists in cluster!'.format(device_name), fg='green')

if onboard:
try:
device = Device(name=device_name, description='onboarded using hwil', ros_distro='melodic',
runtime_docker=True, runtime_preinstalled=False,
python_version=DevicePythonVersion.PYTHON3)
device = client.create_device(device)
click.secho('Device created successfully in rapyuta.io!', fg='green')
onboard_command = device.onboard_script().full_command()
_onboard_hwil_device(hwil_client=hwil_client, client=client, device_name=device_name,
onboard_command=onboard_command,
device_uuid=device.uuid)
except ConflictError:
click.secho('Device {} already exists in rapyuta.io!'.format(device_name), fg='green')
device = client.get_device(device_id=find_device_guid(client, device_name))
if device.is_online() or device.status == DeviceStatus.INITIALIZING:
click.secho('Device {} already {} in rapyuta.io!'.format(device.status,
device_name), fg='green')
raise SystemExit(0)
_onboard_hwil_device(hwil_client=hwil_client, client=client, device_name=device_name,
onboard_command=device.onboard_script().full_command(),
device_uuid=device.uuid)
"""Create a new hardware-in-the-loop device."""
info = click.style(f'{Symbols.INFO} Device configuration = {os}:{codename}:{arch}',
fg=Colors.CYAN, bold=True)
spinner.write(info)
client = new_hwil_client()
labels = prepare_device_labels_from_context(ctx)

try:
client.create_device(device_name, arch, os, codename, labels)
spinner.text = click.style(f'Device {device_name} created successfully.', fg=Colors.GREEN)
spinner.green.ok(Symbols.SUCCESS)
except Exception as e:
click.secho(str(e), fg='red')
spinner.text = click.style(f'Failed to create device: {str(e)}', fg=Colors.RED)
spinner.red.fail(Symbols.ERROR)
raise SystemExit(1)


@name_to_id
def _onboard_hwil_device(hwil_client: Client, client: v1Client, device_name: str, onboard_command: str,
device_id: int, device_uuid: str):
try:
hwil_client.poll_till_device_ready(device_id, sleep_interval=5, retry_limit=3)
hwil_client.execute_cmd(device_id, onboard_command)
for _ in range(10):
device = client.get_device(device_uuid)
if device.is_online():
click.secho('Device {} came online in rapyuta.io!'.format(device_name), fg='green')
return
click.secho('Device {} state {} in rapyuta.io!'.format(device_name, device.status), fg='green')
time.sleep(20)
click.secho('Device {} state {} in rapyuta.io!'.format(device_name, device.status), fg='red')
except Exception as e:
click.secho(str(e), fg='red')
raise SystemExit(1)
def prepare_device_labels_from_context(ctx: click.Context) -> typing.Dict:
user_email = ctx.obj.data.get('email_id', '')
if user_email:
user_email = user_email.split('@')[0]

return {
"user": user_email,
"organization": ctx.obj.data.get('organization_id', ''),
"project": ctx.obj.data.get('project_id', ''),
}
84 changes: 54 additions & 30 deletions riocli/hwil/delete.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2021 Rapyuta Robotics
# Copyright 2024 Rapyuta Robotics
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -11,45 +11,69 @@
# 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 typing

import click
from click_spinner import spinner
from click_help_colors import HelpColorsCommand
from riocli.config import new_client, new_hwil_client
from riocli.hwil.util import find_device_id, DeviceNotFound
from riocli.device.util import find_device_guid
from yaspin.api import Yaspin

from riocli.config import new_hwil_client
from riocli.constants import Colors, Symbols
from riocli.utils.spinner import with_spinner


@click.command(
'delete',
cls=HelpColorsCommand,
help_headers_color=Colors.YELLOW,
help_options_color=Colors.GREEN,
)
# @click.argument('device-name', type=str, default="")
@click.argument('device-names', type=str, nargs=-1)
@click.option('--offboard', 'offboard', is_flag=True, type=bool, default=False)
@click.argument('devices', type=str, nargs=-1)
@click.option('--force', '-f', '--silent', 'force', is_flag=True,
default=False, help='Skip confirmation')
@with_spinner(text='Deleting device(s)...')
def delete_device(
device_names: tuple,
offboard: bool,
devices: typing.List,
force: bool,
spinner: Yaspin = None,
) -> None:
"""
delete a virtual device on the cloud
"""
client = new_client()
hwil_client = new_hwil_client()
"""Delete one or more devices"""

if not devices:
spinner.text = click.style('No device names provided', fg=Colors.RED)
spinner.red.fail(Symbols.ERROR)
raise SystemExit(1)

client = new_hwil_client()
fetched = []

try:
fetched = client.list_devices()
except Exception as e:
spinner.text = click.style(f'Error fetching device(s): {str(e)}', fg=Colors.RED)
spinner.red.fail(Symbols.ERROR)

device_name_map = {name: None for name in devices}

final = {d['id']: d['name'] for d in fetched
if d['name'] in device_name_map}

if not final:
spinner.text = click.style(f'No devices found with name(s): {", ".join(devices)}', fg=Colors.RED)
spinner.red.fail(Symbols.ERROR)
raise SystemExit(1)

with spinner.hidden():
if not force:
click.confirm(f'Do you want to delete {", ".join(final.values())}?', abort=True)

try:
with spinner():
for device_name in device_names:
try:
hwil_client.delete_device(find_device_id(hwil_client, device_name))
click.secho('HWIL Device {device_name} deleted successfully!', fg='green')
except DeviceNotFound:
click.secho('HWIL Device {device_name} already deleted!', fg='green')

if offboard:
try:
client.delete_device(device_id=find_device_guid(client, device_name))
click.secho('Rapyuta.io Device {device_name} deleted successfully in rapyuta.io!', fg='green')
except DeviceNotFound:
click.secho('Rapyuta.io Device {device_name} already deleted!', fg='green')
for device_id, device_name in final.items():
spinner.text = f'Deleting device {device_name}...'
client.delete_device(device_id)
spinner.text = click.style(f'Device(s) deleted successfully!', fg=Colors.GREEN)
spinner.green.ok(Symbols.SUCCESS)
except Exception as e:
click.secho(str(e), fg='red')
spinner.text = click.style(f'Error deleting device(s): {str(e)}', fg=Colors.RED)
spinner.red.fail(Symbols.ERROR)
raise SystemExit(1)
42 changes: 42 additions & 0 deletions riocli/hwil/execute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2024 Rapyuta Robotics
#
# 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 sys

import click
from click_help_colors import HelpColorsCommand

from riocli.config import new_hwil_client
from riocli.constants import Colors
from riocli.hwil.util import name_to_id, execute_command


@click.command(
'execute',
cls=HelpColorsCommand,
help_headers_color=Colors.YELLOW,
help_options_color=Colors.GREEN,
)
@click.argument('device-name', required=True, type=str)
@click.argument('command', required=True, type=str)
@name_to_id
def execute(device_name: str, device_id: str, command: str) -> None:
"""Execute"""
try:
code, stdout, stderr = execute_command(new_hwil_client(), device_id, command)
sys.stdout.write(stdout)
sys.stderr.write(stderr)
sys.exit(code)
except Exception as e:
click.secho(str(e), fg=Colors.RED)
raise SystemExit(1)
Loading

0 comments on commit fa6a38c

Please sign in to comment.