Skip to content

Commit

Permalink
Adds an option to pass devices with yaml (antmicro#47)
Browse files Browse the repository at this point in the history
Fixes an error with changing i2c parameters
Updates documentation
  • Loading branch information
WiktorOgrodnik authored Jun 16, 2023
1 parent 7738caf commit 94b1ec8
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 70 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,12 @@ jobs:
python pyrav4l2/.github/save_examples.py pyrav4l2/README.md
python examples/controls-enumeration.py
devices: |
vivid
gpio 0 16
i2c 0x1C
vivid:
gpio:
left-bound: 0
right-bound: 16
i2c:
chip-addr: 0x1C
python-packages: |
git+https://github.com/antmicro/pyrav4l2.git@3c071a7494b6b67263c4dddb87b47025338fd960
git+https://github.com/antmicro/tuttest.git@c44309e0365c54759fb36864fb77bf8b347bd647
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,12 @@ jobs:
python pyrav4l2/.github/save_examples.py pyrav4l2/README.md
python examples/controls-enumeration.py
devices: |
vivid
gpio 0 16
i2c 0x1C
vivid:
gpio:
left-bound: 0
right-bound: 16
i2c:
chip-addr: 0x1C
python-packages: |
git+https://github.com/antmicro/pyrav4l2.git@3c071a7494b6b67263c4dddb87b47025338fd960
git+https://github.com/antmicro/tuttest.git@c44309e0365c54759fb36864fb77bf8b347bd647
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/run-locally.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ jobs:
python pyrav4l2/.github/save_examples.py pyrav4l2/README.md
python examples/controls-enumeration.py
devices: |
vivid
gpio 0 16
i2c 0x1C
vivid:
gpio:
left-bound: 0
right-bound: 16
i2c:
chip-addr: 0x1C
python-packages: |
git+https://github.com/antmicro/pyrav4l2.git@3c071a7494b6b67263c4dddb87b47025338fd960
git+https://github.com/antmicro/tuttest.git@c44309e0365c54759fb36864fb77bf8b347bd647
Expand Down
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
renode-linux-runner-action
199 changes: 148 additions & 51 deletions action/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@

from common import error

from typing import Protocol, Any, Dict
from typing import Protocol, Any, Dict, Tuple, Iterator
from dataclasses import dataclass
from string import hexdigits

import yaml


class Action(Protocol):
"""
Expand Down Expand Up @@ -50,8 +52,8 @@ def __init__(self) -> None:

def __call__(self, args: list[str]) -> list[str]:

assert len(args) >= 2, "not enough parameters passed"
assert args[0].isdecimal() and args[1].isdecimal()
assert len(args) == 2, "not enough parameters passed"
assert all(type(arg) is int for arg in args) or all(arg.isdecimal() for arg in args)

l, r = int(args[0]), int(args[1])
gpio_ranges_params = []
Expand All @@ -65,11 +67,19 @@ def __call__(self, args: list[str]) -> list[str]:

return [','.join([str(i) for i in gpio_ranges_params])]

def check_args(self, args: list[str]) -> bool:
return len(args) >= 2 and \
args[0].isdecimal() and \
args[1].isdecimal() and \
int(args[0]) < int(args[1])
def check_args(self, args: list[int | str]) -> bool:

if len(args) != 2:
return False

if type(args[0]) is int and type(args[1]) is int:
return args[0] < args[1]
elif type(args[0]) is str and type(args[1]) is str:
return args[0].isdecimal() and \
args[1].isdecimal() and \
int(args[0]) < int(args[1])
else:
return False


class I2C_SetDeviceAddress:
Expand All @@ -84,47 +94,147 @@ def __call__(self, args: list[str]) -> list[str]:

assert len(args) >= 1, "not enough parameters passed"

return [args[0]]
if type(args[0]) is int:
return [format(args[0], "#x")]
else:
return [args[0]]

def check_args(self, args: list[str]) -> bool:
return len(args) == 1 and \
len(args[0]) >= 3 and \
args[0][0:2] == '0x' and \
all(c in hexdigits for c in args[0][2:]) and \
3 <= int(args[0], 16) <= 119

if len(args) != 1:
return False

if type(args[0]) is int:
return 3 <= args[0] <= 119
elif type(args[0]) is str:
return len(args[0]) >= 3 and \
args[0][0:2] == '0x' and \
all(c in hexdigits for c in args[0][2:]) and \
3 <= int(args[0], 16) <= 119
else:
return False


@dataclass
class Device:
class DevicePrototype:
"""
Device Prototype: it stores available devices that can be added.
Fields:
----------
params_list: list[str]
command_action: list[tuple[Action, int]]
command_action: list[Tuple[Action, list[str]]]
defines number of parameters needed and the
Action itself
Action itself and their names (for yaml style list)
"""
params_list: list[str]
command_action: list[tuple[Action, int]]
command_action: list[Tuple[Action, list[str]]]


@dataclass
class Device:
"""
Device with parameters selected by the user
available_devices = {
"vivid": Device(
Fields:
----------
name: name of the device
prototype: DevicePrototype for this device
args: dict with parameters (for yaml style)
or list with paramaters (for old multiline string style)
"""
name: str
prototype: DevicePrototype
args: Dict[str, str]


available_devices: Dict[str, DevicePrototype] = {
"vivid": DevicePrototype(
[],
[(None, 0)],
[(None, [])],
),
"gpio": Device(
["RANGES"],
[(GPIO_SplitDevice, 2)],
"gpio": DevicePrototype(
["ranges"],
[(GPIO_SplitDevice, ["left-bound", "right-bound"])],
),
"i2c": Device(
"i2c": DevicePrototype(
["chip_addr"],
[(I2C_SetDeviceAddress, 1)],
[(I2C_SetDeviceAddress, ["chip-addr"])],
)
}


def get_device(devices: str) -> Iterator[Device]:
"""
Returns the list of devices need to be added. It undersatds both yaml style list
and multiline string style list.
Parameters
----------
devices: raw string from github action, syntax defined in README.md
"""

def none_to_empty_dict(suspect: Dict[str, str] | None) -> Dict[str, str]:
return suspect if suspect is not None else {}

def add_colon_if_no_params(line: str) -> str:
return line if ":" in line or len(line.split()) > 1 else f"{line}:"

def device_available(device: str) -> bool:
if device not in available_devices:
print(f"WARNING: Device {device} not found")

return device in available_devices

devices_dict: Dict[str, Dict[str, str]] = {}

try:
devices_dict = {
device: none_to_empty_dict(args) for device, args in yaml.load(
str.join('\n', [add_colon_if_no_params(line) for line in devices.splitlines()]),
Loader=yaml.FullLoader
).items() if device_available(device)
}

if any(type(args) is not dict for args in devices_dict.values()):
raise yaml.YAMLError

except Exception:

for device in devices.splitlines():
device = device.split()
device_name = device[0]
device_args = device[1:]

if not device_available(device_name):
continue

device_prototype = available_devices[device_name]

if len(device_args) != sum([len(i[1]) for i in device_prototype.command_action]):
print(f"WARNING: for device {device_name}, wrong number "
"of parameters, replaced with the default ones.")

devices_dict[device_name] = {}
continue

devices_dict[device_name] = {
param: value for param, value in zip(
sum([args[1] for args in available_devices[device_name].command_action], start=[]),
device_args
)
}

finally:

for device_name, device_args in devices_dict.items():

yield Device(
device_name,
available_devices[device_name],
device_args,
)


def add_devices(devices: str) -> Dict[str, Dict[str, str]]:
"""
Parses arguments and commands, and adds devices to the
Expand All @@ -136,41 +246,29 @@ def add_devices(devices: str) -> Dict[str, Dict[str, str]]:

added_devices: Dict[str, Dict[str, str]] = {}

for device in devices.splitlines():
device = device.split()
device_name = device[0]

if device_name not in available_devices:
print(f"WARNING: Device {device_name} not found")
continue

device_proto = available_devices[device_name]
for device in get_device(devices):

new_device = {}

args = device[1:]
args_pointer = 0
vars_pointer = 0

if len(device[1:]) != sum([i[1] for i in device_proto.command_action]):
print(f"WARNING: for device {device_name}, wrong number "
"of parameters, replaced with the default ones.")
for params_hook in device.prototype.command_action:

added_devices[f"device-{device_name}"] = new_device
continue
params_action = params_hook[0]
params_list_len = len(params_hook[1])
params = [device.args[arg] for arg in params_hook[1]]

for params_hook in device_proto.command_action:
if not all([arg in device.args.keys() for arg in params_hook[1]]):
print(f"WARNING: for device {device.name}, wrong number "
"of parameters. Some parameters replaced with the default ones.")

params_action = params_hook[0]
params_list_len = params_hook[1]
params = args[args_pointer:args_pointer + params_list_len]
continue

if params_list_len > 0 and params_action:

params_action: Action = params_action()

if not params_action.check_args(params):
error(f"ERROR: for device {device_name} {params_action.error}.")
error(f"ERROR: for device {device.name} {params_action.error}.")

variable_list = params_action(params)
variable_list_len = len(variable_list)
Expand All @@ -179,10 +277,9 @@ def add_devices(devices: str) -> Dict[str, Dict[str, str]]:
variable_list = params
variable_list_len = params_list_len

new_device |= {device_proto.params_list[i + vars_pointer]: var for i, var in enumerate(variable_list)}
new_device |= {device.prototype.params_list[i + vars_pointer].upper(): var for i, var in enumerate(variable_list)}
vars_pointer += variable_list_len
args_pointer += params_list_len

added_devices[f"device-{device_name}"] = new_device
added_devices[f"device-{device.name}"] = new_device

return added_devices
53 changes: 43 additions & 10 deletions docs/Devices.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,49 @@ This action offers several additional devices in the default images, which are n
> **Warnings**
> Adding devices may not work properly with your own kernel. If you want to use your own kernel, check that you have added the correct drivers.
## Devices syntax
## YAML style passing devices

You can pass device configurations for inclusion with a simple YAML file. If you do not want to pass any parameters to the device, simply put its name on the next line.

### Available devices

- [`vivid`](https://www.kernel.org/doc/html/latest/admin-guide/media/vivid.html) - virtual device emulating a Video4Linux device
- [`gpio`](https://docs.kernel.org/admin-guide/gpio/gpio-mockup.html) - virtual device emulating GPIO lines. Optional parameters:
- left-bound: GPIO line numbers will start from this number
- right-bound: GPIO line numbers will end 1 before this number (for example, `gpio 0 64` will add 64 lines from 0 to 63)
- [`i2c`](https://www.kernel.org/doc/html/v5.10/i2c/i2c-stub.html) - virtual device emulating `I2C` bus. Optional parameter:
- chip-addr: 7 bit address 0x03 to 0x77 of the chip that simulates the EEPROM device and provides read and write commands to it.

### Example

```yaml
- uses: antmicro/renode-linux-runner-action@v0
with:
renode-run: ls /dev
devices: |
gpio:
left-bound: 16
right-bound: 32
vivid
```
## Passing devices by multiline string
Devices can also be written out simply as individual lines in a multiline string. Then all the parameters have to be listed one by one (without their names).
For example:
```yaml
- uses: antmicro/renode-linux-runner-action@v0
with:
renode-run: ls /dev
devices: |
gpio 16 32
i2c 0x1C
vivid
```
### Old devices syntax
```yaml
- uses: antmicro/renode-linux-runner-action@v0
Expand All @@ -15,12 +57,3 @@ This action offers several additional devices in the default images, which are n
device2 param1 param2 param3 ...
...
```
## Available devices
- [`vivid`](https://www.kernel.org/doc/html/latest/admin-guide/media/vivid.html) - virtual device emulating a Video4Linux device
- [`gpio`](https://docs.kernel.org/admin-guide/gpio/gpio-mockup.html) - virtual device emulating GPIO lines. Optional parameters:
- left bound: GPIO line numbers will start from this number
- right bound: GPIO line numbers will end 1 before this number (for example, `gpio 0 64` will add 64 lines from 0 to 63)
- [`i2c`](https://www.kernel.org/doc/html/v5.10/i2c/i2c-stub.html) - virtual device emulating `I2C` bus. Optional parameter:
- chip_addr: 7 bit address 0x03 to 0x77 of the chip that simulates the EEPROM device and provides read and write commands to it.

0 comments on commit 94b1ec8

Please sign in to comment.