Skip to content
Snippets Groups Projects
Commit 72d5a0e9 authored by Shawn S's avatar Shawn S
Browse files

Updated ledflasher sample to use trait/component model

 - light schema onOff trait
 - dynamically add components based on available LEDs from lights HAL
 - custom trait _ledInfo to save lights HAL LED names

BUG:26798554
Change-Id: I4c1dd0cdb2f3479703e9082fea152a713178e65c
parent a64e4e0a
No related branches found
No related tags found
No related merge requests found
...@@ -17,7 +17,10 @@ ...@@ -17,7 +17,10 @@
package brillo.examples.ledflasher; package brillo.examples.ledflasher;
interface ILEDService { interface ILEDService {
int getLEDCount();
void setLED(int ledIndex, boolean on); void setLED(int ledIndex, boolean on);
boolean getLED(int ledIndex); boolean getLED(int ledIndex);
boolean[] getAllLEDs(); boolean[] getAllLEDs();
String[] getAllLEDNames();
void setAllLEDs(boolean on);
} }
...@@ -25,6 +25,10 @@ Animation::Animation( ...@@ -25,6 +25,10 @@ Animation::Animation(
android::sp<brillo::examples::ledflasher::ILEDService> led_service, android::sp<brillo::examples::ledflasher::ILEDService> led_service,
const base::TimeDelta& step_duration) const base::TimeDelta& step_duration)
: led_service_{led_service}, step_duration_{step_duration} { : led_service_{led_service}, step_duration_{step_duration} {
int led_count;
led_service_->getLEDCount(&led_count);
num_leds = static_cast<size_t>(led_count);
step_duration_ /= num_leds;
} }
Animation::~Animation() { Animation::~Animation() {
......
...@@ -39,9 +39,9 @@ class Animation { ...@@ -39,9 +39,9 @@ class Animation {
const std::string& type, const std::string& type,
const base::TimeDelta& duration); const base::TimeDelta& duration);
static const size_t num_leds = 4;
protected: protected:
size_t num_leds;
virtual void DoAnimationStep() = 0; virtual void DoAnimationStep() = 0;
bool GetLED(size_t index) const; bool GetLED(size_t index) const;
......
...@@ -20,8 +20,7 @@ AnimationMarquee::AnimationMarquee( ...@@ -20,8 +20,7 @@ AnimationMarquee::AnimationMarquee(
android::sp<brillo::examples::ledflasher::ILEDService> led_service, android::sp<brillo::examples::ledflasher::ILEDService> led_service,
const base::TimeDelta& duration, const base::TimeDelta& duration,
Direction direction) Direction direction)
: Animation{led_service, duration / num_leds}, direction_{direction} { : Animation{led_service, duration}, direction_{direction} {}
}
void AnimationMarquee::DoAnimationStep() { void AnimationMarquee::DoAnimationStep() {
SetAllLEDs(false); SetAllLEDs(false);
......
{ {
"_ledflasher": { "_ledflasher": {
"commands": { "commands": {
"set": {
"minimalRole": "user",
"parameters": {
"led": {
"type": "integer",
"minimum": 1,
"maximum": 4
},
"on": { "type": "boolean" }
}
},
"toggle": {
"minimalRole": "user",
"parameters": {
"led": {
"type": "integer",
"minimum": 1,
"maximum": 4
}
}
},
"animate": { "animate": {
"minimalRole": "user", "minimalRole": "user",
"parameters": { "parameters": {
...@@ -41,10 +20,34 @@ ...@@ -41,10 +20,34 @@
"status": { "status": {
"type": "string", "type": "string",
"enum": [ "idle", "animating" ] "enum": [ "idle", "animating" ]
}, }
"leds": { }
"type": "array", },
"items": { "type": "boolean" } "onOff": {
"commands": {
"setConfig": {
"minimalRole": "user",
"parameters": {
"state": {
"type": "string",
"enum": [ "on", "off" ]
}
}
}
},
"state": {
"state": {
"isRequired": true,
"type": "string",
"enum": [ "on", "off" ]
}
}
},
"_ledInfo": {
"state": {
"name": {
"isRequired": true,
"type": "string"
} }
} }
} }
......
...@@ -31,11 +31,16 @@ ...@@ -31,11 +31,16 @@
#include "binder_constants.h" #include "binder_constants.h"
#include "brillo/examples/ledflasher/ILEDService.h" #include "brillo/examples/ledflasher/ILEDService.h"
using android::String16;
namespace { namespace {
const char kWeaveComponent[] = "ledflasher"; const char kLedFlasherComponent[] = "ledflasher";
const char kWeaveTrait[] = "_ledflasher"; const char kLedFlasherTrait[] = "_ledflasher";
const char kBaseComponent[] = "base"; const char kBaseComponent[] = "base";
const char kBaseTrait[] = "base"; const char kBaseTrait[] = "base";
const char kLedComponentPrefix[] = "led";
const char kOnOffTrait[] = "onOff";
const char kLedInfoTrait[] = "_ledInfo";
} // anonymous namespace } // anonymous namespace
using brillo::examples::ledflasher::ILEDService; using brillo::examples::ledflasher::ILEDService;
...@@ -52,10 +57,10 @@ class Daemon final : public brillo::Daemon { ...@@ -52,10 +57,10 @@ class Daemon final : public brillo::Daemon {
void ConnectToLEDService(); void ConnectToLEDService();
void OnLEDServiceDisconnected(); void OnLEDServiceDisconnected();
void OnPairingInfoChanged(const weaved::Service::PairingInfo* pairing_info); void OnPairingInfoChanged(const weaved::Service::PairingInfo* pairing_info);
void CreateLedComponentsIfNeeded();
// Particular command handlers for various commands. // Particular command handlers for various commands.
void OnSet(std::unique_ptr<weaved::Command> command); void OnSetConfig(size_t led_index, std::unique_ptr<weaved::Command> command);
void OnToggle(std::unique_ptr<weaved::Command> command);
void OnAnimate(std::unique_ptr<weaved::Command> command); void OnAnimate(std::unique_ptr<weaved::Command> command);
void OnIdentify(std::unique_ptr<weaved::Command> command); void OnIdentify(std::unique_ptr<weaved::Command> command);
...@@ -80,6 +85,8 @@ class Daemon final : public brillo::Daemon { ...@@ -80,6 +85,8 @@ class Daemon final : public brillo::Daemon {
brillo::BinderWatcher binder_watcher_; brillo::BinderWatcher binder_watcher_;
std::unique_ptr<weaved::Service::Subscription> weave_service_subscription_; std::unique_ptr<weaved::Service::Subscription> weave_service_subscription_;
bool led_components_added_{false};
base::WeakPtrFactory<Daemon> weak_ptr_factory_{this}; base::WeakPtrFactory<Daemon> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(Daemon); DISALLOW_COPY_AND_ASSIGN(Daemon);
}; };
...@@ -111,15 +118,12 @@ void Daemon::OnWeaveServiceConnected( ...@@ -111,15 +118,12 @@ void Daemon::OnWeaveServiceConnected(
if (!weave_service) if (!weave_service)
return; return;
weave_service->AddComponent(kWeaveComponent, {kWeaveTrait}, nullptr); weave_service->AddComponent(
weave_service->AddCommandHandler( kLedFlasherComponent, {kLedFlasherTrait}, nullptr);
kWeaveComponent, kWeaveTrait, "set",
base::Bind(&Daemon::OnSet, weak_ptr_factory_.GetWeakPtr()));
weave_service->AddCommandHandler( weave_service->AddCommandHandler(
kWeaveComponent, kWeaveTrait, "toggle", kLedFlasherComponent,
base::Bind(&Daemon::OnToggle, weak_ptr_factory_.GetWeakPtr())); kLedFlasherTrait,
weave_service->AddCommandHandler( "animate",
kWeaveComponent, kWeaveTrait, "animate",
base::Bind(&Daemon::OnAnimate, weak_ptr_factory_.GetWeakPtr())); base::Bind(&Daemon::OnAnimate, weak_ptr_factory_.GetWeakPtr()));
weave_service->AddCommandHandler( weave_service->AddCommandHandler(
kBaseComponent, kBaseTrait, "identify", kBaseComponent, kBaseTrait, "identify",
...@@ -129,6 +133,8 @@ void Daemon::OnWeaveServiceConnected( ...@@ -129,6 +133,8 @@ void Daemon::OnWeaveServiceConnected(
base::Bind(&Daemon::OnPairingInfoChanged, base::Bind(&Daemon::OnPairingInfoChanged,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
led_components_added_ = false;
CreateLedComponentsIfNeeded();
UpdateDeviceState(); UpdateDeviceState();
} }
...@@ -147,61 +153,92 @@ void Daemon::ConnectToLEDService() { ...@@ -147,61 +153,92 @@ void Daemon::ConnectToLEDService() {
base::Bind(&Daemon::OnLEDServiceDisconnected, base::Bind(&Daemon::OnLEDServiceDisconnected,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
led_service_ = android::interface_cast<ILEDService>(binder); led_service_ = android::interface_cast<ILEDService>(binder);
CreateLedComponentsIfNeeded();
UpdateDeviceState(); UpdateDeviceState();
} }
void Daemon::CreateLedComponentsIfNeeded() {
if (led_components_added_ || !led_service_.get())
return;
auto weave_service = weave_service_.lock();
if (!weave_service)
return;
std::vector<bool> leds;
std::vector<String16> ledNames;
if (!led_service_->getAllLEDs(&leds).isOk())
return;
if (!led_service_->getAllLEDNames(&ledNames).isOk())
return;
for (size_t led_index = 0; led_index < leds.size(); led_index++) {
std::string led_name = android::String8{ledNames[led_index]}.string();
std::string component_name =
kLedComponentPrefix + std::to_string(led_index + 1);
if (weave_service->AddComponent(
component_name, {kOnOffTrait, kLedInfoTrait}, nullptr)) {
weave_service->AddCommandHandler(
component_name,
kOnOffTrait,
"setConfig",
base::Bind(
&Daemon::OnSetConfig, weak_ptr_factory_.GetWeakPtr(), led_index));
weave_service->SetStateProperty(
component_name,
kOnOffTrait,
"state",
*brillo::ToValue(leds[led_index] ? "on" : "off"),
nullptr);
weave_service->SetStateProperty(component_name,
kLedInfoTrait,
"name",
*brillo::ToValue(led_name),
nullptr);
}
}
led_components_added_ = true;
}
void Daemon::OnLEDServiceDisconnected() { void Daemon::OnLEDServiceDisconnected() {
animation_.reset(); animation_.reset();
led_service_ = nullptr; led_service_ = nullptr;
ConnectToLEDService(); ConnectToLEDService();
} }
void Daemon::OnSet(std::unique_ptr<weaved::Command> command) { void Daemon::OnSetConfig(size_t led_index,
std::unique_ptr<weaved::Command> command) {
if (!led_service_.get()) { if (!led_service_.get()) {
command->Abort("_system_error", "ledservice unavailable", nullptr); command->Abort("_system_error", "ledservice unavailable", nullptr);
return; return;
} }
int index = command->GetParameter<int>("led"); auto state = command->GetParameter<std::string>("state");
if(index < 1 || index > 4) { bool on = (state == "on");
command->Abort("_invalid_parameter", "Invalid parameter value", nullptr); android::binder::Status status = led_service_->setLED(led_index, on);
return;
}
bool on = command->GetParameter<bool>("on");
android::binder::Status status = led_service_->setLED(index - 1, on);
if (!status.isOk()) { if (!status.isOk()) {
command->AbortWithCustomError(status, nullptr); command->AbortWithCustomError(status, nullptr);
return; return;
} }
animation_.reset(); if (animation_) {
status_ = "idle"; animation_.reset();
UpdateDeviceState(); status_ = "idle";
command->Complete({}, nullptr); UpdateDeviceState();
}
void Daemon::OnToggle(std::unique_ptr<weaved::Command> command) {
if (!led_service_.get()) {
command->Abort("_system_error", "ledservice unavailable", nullptr);
return;
} }
int index = command->GetParameter<int>("led"); auto weave_service = weave_service_.lock();
if(index < 1 || index > 4) { if (weave_service) {
command->Abort("_invalid_parameter", "Invalid parameter value", nullptr); std::string component_name =
return; kLedComponentPrefix + std::to_string(led_index + 1);
} weave_service->SetStateProperty(component_name,
index--; kOnOffTrait,
bool on = false; "state",
android::binder::Status status = led_service_->getLED(index, &on); *brillo::ToValue(on ? "on" : "off"),
if (status.isOk()) nullptr);
status = led_service_->setLED(index, !on);
if(!status.isOk()) {
command->AbortWithCustomError(status, nullptr);
return;
} }
animation_.reset();
status_ = "idle";
UpdateDeviceState();
command->Complete({}, nullptr); command->Complete({}, nullptr);
} }
...@@ -263,21 +300,15 @@ void Daemon::StopAnimation() { ...@@ -263,21 +300,15 @@ void Daemon::StopAnimation() {
} }
void Daemon::UpdateDeviceState() { void Daemon::UpdateDeviceState() {
if (!led_service_.get())
return;
std::vector<bool> leds;
if (!led_service_->getAllLEDs(&leds).isOk())
return;
auto weave_service = weave_service_.lock(); auto weave_service = weave_service_.lock();
if (!weave_service) if (!weave_service)
return; return;
base::DictionaryValue state_change; weave_service->SetStateProperty(kLedFlasherComponent,
state_change.SetString("_ledflasher.status", status_); kLedFlasherTrait,
state_change.Set("_ledflasher.leds", brillo::ToValue(leds).release()); "status",
weave_service->SetStateProperties(kWeaveComponent, state_change, nullptr); *brillo::ToValue(status_),
nullptr);
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
......
...@@ -29,8 +29,15 @@ ...@@ -29,8 +29,15 @@
#include "brillo/examples/ledflasher/BnLEDService.h" #include "brillo/examples/ledflasher/BnLEDService.h"
#include "ledstatus.h" #include "ledstatus.h"
using android::String16;
class LEDService : public brillo::examples::ledflasher::BnLEDService { class LEDService : public brillo::examples::ledflasher::BnLEDService {
public: public:
android::binder::Status getLEDCount(int32_t* count) override {
*count = leds_.GetLedCount();
return android::binder::Status::ok();
}
android::binder::Status setLED(int32_t ledIndex, bool on) override { android::binder::Status setLED(int32_t ledIndex, bool on) override {
leds_.SetLedStatus(ledIndex, on); leds_.SetLedStatus(ledIndex, on);
return android::binder::Status::ok(); return android::binder::Status::ok();
...@@ -46,6 +53,19 @@ class LEDService : public brillo::examples::ledflasher::BnLEDService { ...@@ -46,6 +53,19 @@ class LEDService : public brillo::examples::ledflasher::BnLEDService {
return android::binder::Status::ok(); return android::binder::Status::ok();
} }
android::binder::Status getAllLEDNames(std::vector<String16>* leds) override {
std::vector<std::string> ledNames = leds_.GetNames();
for (const std::string& name : ledNames) {
leds->push_back(String16{name.c_str()});
}
return android::binder::Status::ok();
}
android::binder::Status setAllLEDs(bool on) override {
leds_.SetAllLeds(on);
return android::binder::Status::ok();
}
private: private:
LedStatus leds_; LedStatus leds_;
}; };
......
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
namespace { namespace {
brillo::StreamPtr GetLEDDataStream(size_t index, bool write) { brillo::StreamPtr GetLEDDataStream(size_t index, bool write) {
CHECK(index < LedStatus::num_leds);
std::string led_path; std::string led_path;
if( index == 3 ) { if( index == 3 ) {
led_path = "/sys/class/leds/boot/brightness"; led_path = "/sys/class/leds/boot/brightness";
...@@ -48,7 +47,7 @@ brillo::StreamPtr GetLEDDataStream(size_t index, bool write) { ...@@ -48,7 +47,7 @@ brillo::StreamPtr GetLEDDataStream(size_t index, bool write) {
} // anonymous namespace } // anonymous namespace
LedStatus::LedStatus() { LedStatus::LedStatus() {
// Try to open the lights hal. // Try to open the lights HAL.
int ret = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, &lights_hal_); int ret = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, &lights_hal_);
if (ret) { if (ret) {
LOG(ERROR) << "Failed to load the lights HAL."; LOG(ERROR) << "Failed to load the lights HAL.";
...@@ -57,14 +56,12 @@ LedStatus::LedStatus() { ...@@ -57,14 +56,12 @@ LedStatus::LedStatus() {
CHECK(lights_hal_); CHECK(lights_hal_);
LOG(INFO) << "Loaded lights HAL."; LOG(INFO) << "Loaded lights HAL.";
// If we can open the hal, then we map each number from 1 - 4 to one of the // If we can open the HAL, then we map each number to one of the LEDs
// leds available on the board. Note, multiple numbers could be mapped to the // available on the board.
// same led.
const std::initializer_list<const char*> kLogicalLights = { const std::initializer_list<const char*> kLogicalLights = {
LIGHT_ID_BACKLIGHT, LIGHT_ID_KEYBOARD, LIGHT_ID_BUTTONS, LIGHT_ID_BATTERY, LIGHT_ID_BACKLIGHT, LIGHT_ID_KEYBOARD, LIGHT_ID_BUTTONS, LIGHT_ID_BATTERY,
LIGHT_ID_NOTIFICATIONS, LIGHT_ID_ATTENTION, LIGHT_ID_BLUETOOTH, LIGHT_ID_NOTIFICATIONS, LIGHT_ID_ATTENTION, LIGHT_ID_BLUETOOTH,
LIGHT_ID_WIFI}; LIGHT_ID_WIFI};
size_t led_index = 0;
for (const char* light_name : kLogicalLights) { for (const char* light_name : kLogicalLights) {
light_device_t* light_device = nullptr; light_device_t* light_device = nullptr;
ret = lights_hal_->methods->open( ret = lights_hal_->methods->open(
...@@ -74,41 +71,39 @@ LedStatus::LedStatus() { ...@@ -74,41 +71,39 @@ LedStatus::LedStatus() {
if (ret || !light_device) { if (ret || !light_device) {
continue; continue;
} }
hal_led_map_.emplace(led_index, light_name); hal_leds_.push_back(light_name);
led_index++; hal_led_status_.push_back(false);
if (led_index == num_leds) {
// We have already mapped all num_leds LEDs.
break;
}
} }
// If the size of the map is zero, then the lights hal doesn't have any valid // If the size of the map is zero, then the lights HAL doesn't have any valid
// leds. // leds.
if (hal_led_map_.empty()) { if (hal_leds_.empty()) {
LOG(ERROR) << "Unable to open any light devices using the hal."; LOG(INFO) << "Unable to open any light devices using the HAL.";
lights_hal_ = nullptr; lights_hal_ = nullptr;
return; return;
} }
}
// If not all 4 numbers have been mapped, then we map them to the first led size_t LedStatus::GetLedCount() const {
// mapped. return lights_hal_ ? hal_leds_.size() : 1;
for (size_t i = hal_led_map_.size(); i < num_leds; i++) {
hal_led_map_.emplace(i, hal_led_map_[0]);
}
hal_led_status_.resize(num_leds);
} }
std::vector<bool> LedStatus::GetStatus() const { std::vector<bool> LedStatus::GetStatus() const {
if (lights_hal_) if (lights_hal_)
return hal_led_status_; return hal_led_status_;
std::vector<bool> leds(num_leds); std::vector<bool> leds(GetLedCount());
for (size_t index = 0; index < num_leds; index++) for (size_t index = 0; index < GetLedCount(); index++)
leds[index] = IsLedOn(index); leds[index] = IsLedOn(index);
return leds; return leds;
} }
std::vector<std::string> LedStatus::GetNames() const {
return lights_hal_ ? hal_leds_ : std::vector<std::string>(GetLedCount());
}
bool LedStatus::IsLedOn(size_t index) const { bool LedStatus::IsLedOn(size_t index) const {
CHECK(index < GetLedCount());
if (lights_hal_) if (lights_hal_)
return hal_led_status_[index]; return hal_led_status_[index];
...@@ -132,6 +127,7 @@ bool LedStatus::IsLedOn(size_t index) const { ...@@ -132,6 +127,7 @@ bool LedStatus::IsLedOn(size_t index) const {
} }
void LedStatus::SetLedStatus(size_t index, bool on) { void LedStatus::SetLedStatus(size_t index, bool on) {
CHECK(index < GetLedCount());
if (lights_hal_) { if (lights_hal_) {
light_state_t state = {}; light_state_t state = {};
state.color = on; state.color = on;
...@@ -141,23 +137,24 @@ void LedStatus::SetLedStatus(size_t index, bool on) { ...@@ -141,23 +137,24 @@ void LedStatus::SetLedStatus(size_t index, bool on) {
state.brightnessMode = BRIGHTNESS_MODE_USER; state.brightnessMode = BRIGHTNESS_MODE_USER;
light_device_t* light_device = nullptr; light_device_t* light_device = nullptr;
int rc = lights_hal_->methods->open( int rc = lights_hal_->methods->open(
lights_hal_, hal_led_map_[index].c_str(), lights_hal_,
hal_leds_[index].c_str(),
reinterpret_cast<hw_device_t**>(&light_device)); reinterpret_cast<hw_device_t**>(&light_device));
if (rc) { if (rc) {
LOG(ERROR) << "Unable to open " << hal_led_map_[index]; LOG(ERROR) << "Unable to open " << hal_leds_[index];
return; return;
} }
CHECK(light_device); CHECK(light_device);
rc = light_device->set_light(light_device, &state); rc = light_device->set_light(light_device, &state);
if (rc) { if (rc) {
LOG(ERROR) << "Unable to set " << hal_led_map_[index]; LOG(ERROR) << "Unable to set " << hal_leds_[index];
return; return;
} }
hal_led_status_[index] = on; hal_led_status_[index] = on;
light_device->common.close( light_device->common.close(
reinterpret_cast<hw_device_t*>(light_device)); reinterpret_cast<hw_device_t*>(light_device));
if (rc) { if (rc) {
LOG(ERROR) << "Unable to close " << hal_led_map_[index]; LOG(ERROR) << "Unable to close " << hal_leds_[index];
return; return;
} }
return; return;
...@@ -170,3 +167,8 @@ void LedStatus::SetLedStatus(size_t index, bool on) { ...@@ -170,3 +167,8 @@ void LedStatus::SetLedStatus(size_t index, bool on) {
std::string brightness = on ? "255" : "0"; std::string brightness = on ? "255" : "0";
stream->WriteAllBlocking(brightness.data(), brightness.size(), nullptr); stream->WriteAllBlocking(brightness.data(), brightness.size(), nullptr);
} }
void LedStatus::SetAllLeds(bool on) {
for (size_t i = 0; i < GetLedCount(); i++)
SetLedStatus(i, on);
}
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#ifndef LEDFLASHER_SRC_LEDSERVICE_LEDSTATUS_H_ #ifndef LEDFLASHER_SRC_LEDSERVICE_LEDSTATUS_H_
#define LEDFLASHER_SRC_LEDSERVICE_LEDSTATUS_H_ #define LEDFLASHER_SRC_LEDSERVICE_LEDSTATUS_H_
#include <map> #include <string>
#include <vector> #include <vector>
#include <base/macros.h> #include <base/macros.h>
...@@ -28,17 +28,17 @@ class LedStatus final { ...@@ -28,17 +28,17 @@ class LedStatus final {
LedStatus(); LedStatus();
std::vector<bool> GetStatus() const; std::vector<bool> GetStatus() const;
std::vector<std::string> GetNames() const;
bool IsLedOn(size_t index) const; bool IsLedOn(size_t index) const;
void SetLedStatus(size_t index, bool on); void SetLedStatus(size_t index, bool on);
void SetAllLeds(bool on);
static const size_t num_leds = 4; size_t GetLedCount() const;
private: private:
const hw_module_t* lights_hal_{nullptr}; const hw_module_t* lights_hal_{nullptr};
// Maps the lights available to the hal to numbers 1 - 4. It is possible for // Contains the names of LEDs in the HAL for each of supported LEDs.
// multiple numbers to be mapped to the same led on the board. std::vector<std::string> hal_leds_;
std::map<size_t, std::string> hal_led_map_; // Since the HAL doesn't have a way to track the led status, we maintain that
// Since the hal doesn't have a way to track the led status, we maintain that
// info here. // info here.
std::vector<bool> hal_led_status_; std::vector<bool> hal_led_status_;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment