Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions subprojects/robotpy-wpilib/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ subdir('semiwrap')

wpilib_sources += files(
'wpilib/src/main.cpp',
'wpilib/src/rpy/AddressableLEDBuffer.cpp',
'wpilib/src/rpy/ControlWord.cpp',
'wpilib/src/rpy/Notifier.cpp',
'wpilib/src/rpy/SmartDashboardData.cpp',
Expand Down
2 changes: 2 additions & 0 deletions subprojects/robotpy-wpilib/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ SysIdRoutineLog = "frc/sysid/SysIdRoutineLog.h"
Color = "frc/util/Color.h"
Color8Bit = "frc/util/Color8Bit.h"

# rpy only
AddressableLEDBuffer = "rpy/AddressableLEDBuffer.h"

[tool.semiwrap.extension_modules."wpilib.counter._counter"]
name = "wpilib_counter"
Expand Down
57 changes: 57 additions & 0 deletions subprojects/robotpy-wpilib/semiwrap/AddressableLEDBuffer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
classes:
frc::AddressableLEDBuffer:
methods:
AddressableLEDBuffer:
SetRGB:
SetHSV:
SetLED:
overloads:
size_t, const frc::Color&:
size_t, const frc::Color8Bit&:
size:
rename: __len__
GetRed:
GetGreen:
GetBlue:
GetLED:
GetLED8Bit:
at:
rename: __getitem__
begin:
ignore: true
end:
ignore: true
CreateView:
rename: __getitem__
keepalive:
- [0, 1]
inline_code: |
.def("__iter__", [](frc::AddressableLEDBuffer& self) {
return py::make_iterator(self.begin(), self.end());
}, py::keep_alive<0, 1>())
frc::AddressableLEDBuffer::View:
methods:
size:
rename: __len__
SetRGB:
SetHSV:
SetLED:
overloads:
size_t, const frc::Color&:
size_t, const frc::Color8Bit&:
at:
rename: __getitem__
overloads:
size_t:
size_t [const]:
ignore: true
begin:
ignore: true
end:
ignore: true
GetLED:
GetLED8Bit:
inline_code: |
.def("__iter__", [](frc::AddressableLEDBuffer::View& self) {
return py::make_iterator(self.begin(), self.end());
}, py::keep_alive<0, 1>())
18 changes: 18 additions & 0 deletions subprojects/robotpy-wpilib/semiwrap/LEDPattern.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
extra_includes:
- rpy/AddressableLEDBuffer.h

classes:
frc::LEDPattern:
enums:
Expand Down Expand Up @@ -38,6 +41,21 @@ classes:
ignore: true
Rainbow:
MapIndex:
inline_code: |
.def("applyTo", [](const frc::LEDPattern& self, frc::AddressableLEDBuffer& data) {
self.ApplyTo(static_cast<std::span<frc::AddressableLED::LEDData>>(data));
}, py::arg("data"), release_gil(), py::prepend())
.def("applyTo", [](const frc::LEDPattern& self, frc::AddressableLEDBuffer& data,
std::function<void (int, frc::Color)> writer) {
self.ApplyTo(static_cast<std::span<frc::AddressableLED::LEDData>>(data), std::move(writer));
}, py::arg("data"), py::arg("writer").none(false), release_gil(), py::prepend())
.def("applyTo", [](const frc::LEDPattern& self, frc::AddressableLEDBuffer::View& data) {
self.ApplyTo(static_cast<std::span<frc::AddressableLED::LEDData>>(data));
}, py::arg("data"), release_gil(), py::prepend())
.def("applyTo", [](const frc::LEDPattern& self, frc::AddressableLEDBuffer::View& data,
std::function<void (int, frc::Color)> writer) {
self.ApplyTo(static_cast<std::span<frc::AddressableLED::LEDData>>(data), std::move(writer));
}, py::arg("data"), py::arg("writer").none(false), release_gil(), py::prepend())
frc::LEDPattern::LEDReader:
methods:
LEDReader:
Expand Down
163 changes: 163 additions & 0 deletions subprojects/robotpy-wpilib/tests/test_addressable_led_buffer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Copyright (c) FIRST and other WPILib contributors.
# Open Source Software; you can modify and/or share it under the terms of
# the WPILib BSD license file in the root directory of this project.

import pytest

from wpilib import AddressableLEDBuffer, Color, Color8Bit

AddressableLEDBufferView = AddressableLEDBuffer.View


class TestAddressableLEDBuffer:
"""Tests for AddressableLEDBuffer"""

@pytest.mark.parametrize(
"h,s,v,r,g,b",
[
(0, 0, 0, 0, 0, 0), # Black
(0, 0, 255, 255, 255, 255), # White
(0, 255, 255, 255, 0, 0), # Red
(60, 255, 255, 0, 255, 0), # Lime
(120, 255, 255, 0, 0, 255), # Blue
(30, 255, 255, 255, 255, 0), # Yellow
(90, 255, 255, 0, 255, 255), # Cyan
(150, 255, 255, 255, 0, 255), # Magenta
(0, 0, 191, 191, 191, 191), # Silver
(0, 0, 128, 128, 128, 128), # Gray
(0, 255, 128, 128, 0, 0), # Maroon
(30, 255, 128, 128, 128, 0), # Olive
(60, 255, 128, 0, 128, 0), # Green
(150, 255, 128, 128, 0, 128), # Purple
(90, 255, 128, 0, 128, 128), # Teal
(120, 255, 128, 0, 0, 128), # Navy
],
)
def test_hsv_convert(self, h, s, v, r, g, b):
"""Test HSV to RGB conversion"""
buffer = AddressableLEDBuffer(length=1)
buffer.setHSV(0, h, s, v)
color = buffer.getLED8Bit(0)
assert color.red == r, "R value didn't match"
assert color.green == g, "G value didn't match"
assert color.blue == b, "B value didn't match"

def test_get_color(self):
"""Test getting colors from buffer"""
buffer = AddressableLEDBuffer(4)
denim_color_8bit = Color8Bit(Color.kDenim)
first_blue_color_8bit = Color8Bit(Color.kFirstBlue)
first_red_color_8bit = Color8Bit(Color.kFirstRed)

buffer.setLED(0, Color.kFirstBlue)
buffer.setLED(1, denim_color_8bit)
buffer.setLED(2, Color.kFirstRed)
buffer.setLED(3, Color.kFirstBlue)

assert buffer.getLED(0) == Color.kFirstBlue
assert buffer.getLED(1) == Color.kDenim
assert buffer.getLED(2) == Color.kFirstRed
assert buffer.getLED(3) == Color.kFirstBlue
assert buffer.getLED8Bit(0) == first_blue_color_8bit
assert buffer.getLED8Bit(1) == denim_color_8bit
assert buffer.getLED8Bit(2) == first_red_color_8bit
assert buffer.getLED8Bit(3) == first_blue_color_8bit

def test_get_red(self):
"""Test getting red component"""
buffer = AddressableLEDBuffer(1)
buffer.setRGB(0, 127, 128, 129)
assert buffer.getRed(0) == 127

def test_get_green(self):
"""Test getting green component"""
buffer = AddressableLEDBuffer(1)
buffer.setRGB(0, 127, 128, 129)
assert buffer.getGreen(0) == 128

def test_get_blue(self):
"""Test getting blue component"""
buffer = AddressableLEDBuffer(1)
buffer.setRGB(0, 127, 128, 129)
assert buffer.getBlue(0) == 129

def test_iteration(self):
buffer = AddressableLEDBuffer(3)
buffer.setRGB(0, 1, 2, 3)
buffer.setRGB(1, 4, 5, 6)
buffer.setRGB(2, 7, 8, 9)

results = []

for led in buffer:
results.append((led.r, led.g, led.b))

assert len(results) == 3
assert results[0] == (1, 2, 3)
assert results[1] == (4, 5, 6)
assert results[2] == (7, 8, 9)

def test_iteration_on_empty_buffer(self):
buffer = AddressableLEDBuffer(0)

for led in buffer:
assert False, "Iterator should not return items on an empty buffer"


class TestAddressableLEDBufferView:
"""Tests for AddressableLEDBufferView"""

def test_single_led(self):
"""Test setting a single LED through a view"""
buffer = AddressableLEDBuffer(10)
view = buffer[5:6]
color = Color.kAqua
view.setLED(0, color)
assert buffer.getLED(5) == color
assert view.getLED(0) == color

def test_segment(self):
"""Test segment view"""
buffer = AddressableLEDBuffer(10)
view = buffer[2:9]
view.setLED(0, Color.kAqua)
assert buffer.getLED(2) == Color.kAqua

view.setLED(6, Color.kAzure)
assert buffer.getLED(8) == Color.kAzure

@pytest.mark.skip("reversed views are not implemented")
def test_manual_reversed(self):
"""Test manually reversed view"""
buffer = AddressableLEDBuffer(10)
view = buffer[8:1:-1]

# LED 0 in the view should write to LED 8 on the real buffer
view.setLED(0, Color.kAqua)
assert buffer.getLED(8) == Color.kAqua

# LED 6 in the view should write to LED 2 on the real buffer
view.setLED(6, Color.kAzure)
assert buffer.getLED(2) == Color.kAzure

@pytest.mark.skip("reversed views are not implemented")
def test_full_manual_reversed(self):
"""Test full manual reversed view"""
buffer = AddressableLEDBuffer(10)
view = buffer[9::-1]
view.setLED(0, Color.kWhite)
assert buffer.getLED(9) == Color.kWhite

buffer.setLED(8, Color.kRed)
assert view.getLED(1) == Color.kRed

@pytest.mark.skip("reversed views are not implemented")
def test_reversed(self):
"""Test reversed view"""
buffer = AddressableLEDBuffer(10)
view = buffer[:].reversed()
view.setLED(0, Color.kWhite)
assert buffer.getLED(9) == Color.kWhite

view.setLED(9, Color.kRed)
assert buffer.getLED(0) == Color.kRed
Loading
Loading