Skip to content
This repository has been archived by the owner on Nov 13, 2023. It is now read-only.

Commit

Permalink
Merge pull request #688 from robotpy/keep-putdata
Browse files Browse the repository at this point in the history
Keep a reference to Sendable objects when passed to SmartDashboard::putData
  • Loading branch information
virtuald committed Feb 16, 2021
2 parents 1b7ea32 + dc9ae3e commit 91ea62f
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 4 deletions.
44 changes: 40 additions & 4 deletions gen/SmartDashboard.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
---

extra_includes:
- src/rpy/SmartDashboardData.h

classes:
SmartDashboard:
nodelete: true
Expand All @@ -21,13 +24,38 @@ classes:
Delete:
GetEntry:
PutData:
# overrides ensure data doesn't die if this is the only reference
overloads:
wpi::StringRef, Sendable*:
# keepalive:
# - [1, 3]
cpp_code: |
[](py::str &key, std::shared_ptr<Sendable> data) {
// convert key to a raw string so that we can create a StringRef
Py_ssize_t raw_size;
const char *raw_str = PyUnicode_AsUTF8AndSize(key.ptr(), &raw_size);
if (raw_str == NULL) {
throw py::error_already_set();
}
wpi::StringRef keyRef(raw_str, raw_size);
frc::SmartDashboard::PutData(keyRef, data.get());
// this comes after the PutData to ensure that the original object doesn't die
// while PutData is called
rpy::addSmartDashboardData(key, data);
}
Sendable*:
# keepalive:
# - [1, 2]
cpp_code: |
[](std::shared_ptr<Sendable> value) {
frc::SmartDashboard::PutData(value.get());
// this comes after the PutData to ensure that the original object doesn't die
// while PutData is called
auto name = SendableRegistry::GetInstance().GetName(value.get());
if (!name.empty()) {
py::str key(name);
rpy::addSmartDashboardData(key, value);
}
}
GetData:
PutBoolean:
SetDefaultBoolean:
Expand Down Expand Up @@ -139,3 +167,11 @@ classes:
GetValue:
PostListenerTask:
UpdateValues:

inline_code: |
// ensure that the smart dashboard data is released when python shuts down
static int unused; // the capsule needs something to reference
py::capsule cleanup(&unused, [](void *) {
rpy::clearSmartDashboardData();
});
m.add_object("_sd_cleanup", cleanup);
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ sources = [
"wpilib/src/main.cpp",
"wpilib/src/rpy/ControlWord.cpp",
"wpilib/src/rpy/Notifier.cpp",
"wpilib/src/rpy/SmartDashboardData.cpp",
"wpilib/src/rpy/SpeedControllerGroup.cpp",
]

Expand Down
10 changes: 10 additions & 0 deletions tests/test_wpilib.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import wpilib
import weakref


def test_sendable_chooser():
Expand All @@ -7,3 +8,12 @@ def test_sendable_chooser():

chooser.setDefaultOption("option", True)
assert chooser.getSelected() is True


def test_smart_dashboard_putdata():
t = wpilib.Talon(4)
ref = weakref.ref(t)
wpilib.SmartDashboard.putData("talon", t)
del t
assert bool(ref) is True
assert wpilib.SmartDashboard.getData("talon") is ref()
33 changes: 33 additions & 0 deletions wpilib/src/rpy/SmartDashboardData.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

#include "SmartDashboardData.h"

namespace rpy {

//
// Ensures that python objects added to the SmartDashboard have at least one
// reference to them
//
// All functions here must be called with the GIL held
//

static py::dict &getSmartDashboardData() {
static py::dict data;
return data;
}

void addSmartDashboardData(py::str &key, std::shared_ptr<frc::Sendable> data) {
auto &sdData = getSmartDashboardData();
sdData[key] = py::cast(data);
}

void clearSmartDashboardData() {
auto &sdData = getSmartDashboardData();
if (sdData) {
sdData.clear();
// force the dictionary to be deleted otherwise it'll crash when libc++
// is unwinding static objects after interpreter destruction
sdData.dec_ref();
}
}

} // namespace rpy
16 changes: 16 additions & 0 deletions wpilib/src/rpy/SmartDashboardData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

#pragma once

#include <frc/smartdashboard/Sendable.h>
#include <robotpy_build.h>

namespace rpy {

//
// These functions must be called with the GIL held
//

void addSmartDashboardData(py::str &key, std::shared_ptr<frc::Sendable> data);
void clearSmartDashboardData();

} // namespace rpy

0 comments on commit 91ea62f

Please sign in to comment.