or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

entry-interface.mdindex.mdinstance-management.mdintegration-utilities.mdlisteners-events.mdpersistence-data.mdtable-operations.md

integration-utilities.mddocs/

0

# Integration Utilities

1

2

Property decorators and utility classes for seamless integration with object-oriented robotics code and WPILib components. These utilities simplify NetworkTables usage in class-based robot code and provide interfaces to common FRC dashboard components.

3

4

## Capabilities

5

6

### Property Decorator

7

8

Create class properties that automatically sync with NetworkTables.

9

10

```python { .api }

11

def ntproperty(key: str, defaultValue, writeDefault: bool = True,

12

doc: str = None, persistent: bool = False,

13

*, inst = NetworkTables) -> property:

14

"""

15

Create a property that automatically synchronizes with NetworkTables.

16

17

Parameters:

18

- key: str. Full NetworkTables path (e.g., '/SmartDashboard/speed')

19

- defaultValue: Any. Default value if entry doesn't exist

20

- writeDefault: bool. Whether to write default value to NetworkTables

21

- doc: str, optional. Property docstring

22

- persistent: bool. Whether to make the NetworkTables entry persistent

23

- inst: NetworkTablesInstance. NetworkTables instance to use

24

25

Returns:

26

property: Python property descriptor that syncs with NetworkTables

27

"""

28

```

29

30

### SendableChooser Interface

31

32

Interface for WPILib SendableChooser objects over NetworkTables.

33

34

```python { .api }

35

class ChooserControl:

36

def __init__(self, key: str, on_choices=None, on_selected=None,

37

*, inst=NetworkTables):

38

"""

39

Create a SendableChooser interface.

40

41

Parameters:

42

- key: str. NetworkTables key for the chooser (e.g., 'Autonomous Mode')

43

- on_choices: Callable, optional. Function called when choices change

44

- on_selected: Callable, optional. Function called when selection changes

45

- inst: NetworkTablesInstance. NetworkTables instance to use

46

"""

47

48

def getChoices() -> List[str]:

49

"""

50

Get available choices from the chooser.

51

52

Returns:

53

List[str]: List of available choice strings

54

"""

55

56

def getSelected() -> Optional[str]:

57

"""

58

Get the currently selected choice.

59

60

Returns:

61

Optional[str]: Selected choice string, or None if nothing selected

62

"""

63

64

def setSelected(selection: str) -> None:

65

"""

66

Set the selected choice (if it exists in available choices).

67

68

Parameters:

69

- selection: str. Choice to select

70

"""

71

72

def close() -> None:

73

"""Clean up resources and remove listeners."""

74

```

75

76

## Usage Examples

77

78

### Basic Property Integration

79

80

```python

81

from networktables.util import ntproperty

82

from networktables import NetworkTables

83

84

class DriveSubsystem:

85

# Automatically sync with SmartDashboard

86

max_speed = ntproperty('/SmartDashboard/maxSpeed', 1.0, persistent=True)

87

current_speed = ntproperty('/SmartDashboard/currentSpeed', 0.0)

88

drive_mode = ntproperty('/SmartDashboard/driveMode', 'manual')

89

90

def __init__(self):

91

# Properties are available immediately

92

print(f"Initial max speed: {self.max_speed}")

93

94

def set_speed(self, speed):

95

# Clamp to maximum

96

speed = min(speed, self.max_speed)

97

self.current_speed = speed

98

# Value automatically appears on SmartDashboard

99

100

def get_speed(self):

101

return self.current_speed

102

103

# Usage

104

NetworkTables.initialize(server='roborio-1234-frc.local')

105

drive = DriveSubsystem()

106

107

# These property accesses automatically sync with NetworkTables

108

drive.max_speed = 0.8 # Updates SmartDashboard

109

current = drive.current_speed # Reads from SmartDashboard

110

```

111

112

### Advanced Property Configuration

113

114

```python

115

from networktables.util import ntproperty

116

from networktables import NetworkTables

117

118

class RobotConfiguration:

119

# Persistent configuration values

120

max_speed = ntproperty(

121

'/Config/maxSpeed',

122

1.0,

123

persistent=True,

124

doc="Maximum robot speed in m/s"

125

)

126

127

autonomous_delay = ntproperty(

128

'/Config/autonomousDelay',

129

0.0,

130

persistent=True,

131

doc="Delay before autonomous starts in seconds"

132

)

133

134

# Dashboard display values (not persistent)

135

battery_voltage = ntproperty('/SmartDashboard/batteryVoltage', 0.0)

136

connection_count = ntproperty('/SmartDashboard/connectionCount', 0)

137

138

# Vision processing parameters

139

camera_exposure = ntproperty('/Vision/exposure', 50, persistent=True)

140

target_threshold = ntproperty('/Vision/threshold', 128, persistent=True)

141

142

def __init__(self):

143

self.load_config()

144

145

def load_config(self):

146

"""Load configuration from NetworkTables."""

147

print(f"Max speed: {self.max_speed}")

148

print(f"Auto delay: {self.autonomous_delay}")

149

print(f"Camera exposure: {self.camera_exposure}")

150

151

def update_dashboard(self, voltage, connections):

152

"""Update dashboard values."""

153

self.battery_voltage = voltage

154

self.connection_count = connections

155

156

# Usage

157

config = RobotConfiguration()

158

config.max_speed = 0.75 # Automatically persistent

159

```

160

161

### Property Validation and Processing

162

163

```python

164

from networktables.util import ntproperty

165

from networktables import NetworkTables

166

167

class SmartMotorController:

168

_speed_raw = ntproperty('/Motors/speed', 0.0)

169

_enabled = ntproperty('/Motors/enabled', False)

170

171

@property

172

def speed(self):

173

"""Get motor speed with validation."""

174

return max(-1.0, min(1.0, self._speed_raw))

175

176

@speed.setter

177

def speed(self, value):

178

"""Set motor speed with validation."""

179

self._speed_raw = max(-1.0, min(1.0, value))

180

181

@property

182

def enabled(self):

183

"""Check if motor is enabled."""

184

return self._enabled

185

186

@enabled.setter

187

def enabled(self, value):

188

"""Enable/disable motor with safety check."""

189

if not value:

190

self._speed_raw = 0.0 # Stop motor when disabled

191

self._enabled = value

192

193

def emergency_stop(self):

194

"""Emergency stop with logging."""

195

print("EMERGENCY STOP ACTIVATED")

196

self.enabled = False

197

self.speed = 0.0

198

199

# Usage

200

motor = SmartMotorController()

201

motor.speed = 1.5 # Automatically clamped to 1.0

202

motor.enabled = True

203

```

204

205

### SendableChooser Integration

206

207

```python

208

from networktables.util import ChooserControl

209

from networktables import NetworkTables

210

211

class AutonomousSelector:

212

def __init__(self):

213

self.selected_mode = None

214

215

# Create chooser interface

216

self.chooser = ChooserControl(

217

'Autonomous Mode',

218

on_choices=self.on_choices_changed,

219

on_selected=self.on_selection_changed

220

)

221

222

def on_choices_changed(self, choices):

223

"""Called when available autonomous modes change."""

224

print(f"Available autonomous modes: {choices}")

225

226

def on_selection_changed(self, selected):

227

"""Called when autonomous mode selection changes."""

228

print(f"Selected autonomous mode: {selected}")

229

self.selected_mode = selected

230

231

def get_autonomous_mode(self):

232

"""Get the currently selected autonomous mode."""

233

return self.chooser.getSelected()

234

235

def set_default_mode(self, mode):

236

"""Set the default autonomous mode."""

237

choices = self.chooser.getChoices()

238

if mode in choices:

239

self.chooser.setSelected(mode)

240

else:

241

print(f"Warning: Mode '{mode}' not available in {choices}")

242

243

def cleanup(self):

244

"""Clean up resources."""

245

self.chooser.close()

246

247

# Usage in robot code

248

NetworkTables.initialize(server='roborio-1234-frc.local')

249

auto_selector = AutonomousSelector()

250

251

# Later in autonomous init

252

selected_mode = auto_selector.get_autonomous_mode()

253

if selected_mode == 'Defense':

254

run_defense_autonomous()

255

elif selected_mode == 'Shoot and Move':

256

run_shoot_and_move_autonomous()

257

258

# Cleanup when done

259

auto_selector.cleanup()

260

```

261

262

### Multiple Chooser Management

263

264

```python

265

from networktables.util import ChooserControl

266

from networktables import NetworkTables

267

268

class DashboardInterface:

269

def __init__(self):

270

self.choosers = {}

271

self.selections = {}

272

273

# Create multiple choosers

274

self.create_chooser('Autonomous Mode', self.on_auto_change)

275

self.create_chooser('Drive Mode', self.on_drive_change)

276

self.create_chooser('Camera Selection', self.on_camera_change)

277

278

def create_chooser(self, name, callback):

279

"""Create a chooser with callback."""

280

chooser = ChooserControl(name, on_selected=callback)

281

self.choosers[name] = chooser

282

self.selections[name] = None

283

284

def on_auto_change(self, selected):

285

"""Handle autonomous mode changes."""

286

self.selections['Autonomous Mode'] = selected

287

print(f"Autonomous mode: {selected}")

288

289

def on_drive_change(self, selected):

290

"""Handle drive mode changes."""

291

self.selections['Drive Mode'] = selected

292

print(f"Drive mode: {selected}")

293

294

# React to drive mode changes

295

if selected == 'Precision':

296

self.set_max_speed(0.5)

297

elif selected == 'Turbo':

298

self.set_max_speed(1.0)

299

300

def on_camera_change(self, selected):

301

"""Handle camera selection changes."""

302

self.selections['Camera Selection'] = selected

303

print(f"Active camera: {selected}")

304

305

def set_max_speed(self, speed):

306

"""Update max speed on dashboard."""

307

sd = NetworkTables.getTable('SmartDashboard')

308

sd.putNumber('maxSpeed', speed)

309

310

def get_selection(self, chooser_name):

311

"""Get current selection for a chooser."""

312

return self.selections.get(chooser_name)

313

314

def set_default_selections(self):

315

"""Set default selections for all choosers."""

316

defaults = {

317

'Autonomous Mode': 'Defense',

318

'Drive Mode': 'Normal',

319

'Camera Selection': 'Front'

320

}

321

322

for name, default in defaults.items():

323

if name in self.choosers:

324

self.choosers[name].setSelected(default)

325

326

def cleanup(self):

327

"""Clean up all choosers."""

328

for chooser in self.choosers.values():

329

chooser.close()

330

self.choosers.clear()

331

self.selections.clear()

332

333

# Usage

334

dashboard = DashboardInterface()

335

dashboard.set_default_selections()

336

337

# Access selections

338

auto_mode = dashboard.get_selection('Autonomous Mode')

339

drive_mode = dashboard.get_selection('Drive Mode')

340

341

# Cleanup when robot shuts down

342

dashboard.cleanup()

343

```

344

345

### Custom Property Types

346

347

```python

348

from networktables.util import ntproperty

349

from networktables import NetworkTables

350

import json

351

352

class RobotTelemetry:

353

# Simple numeric properties

354

x_position = ntproperty('/Robot/position/x', 0.0)

355

y_position = ntproperty('/Robot/position/y', 0.0)

356

heading = ntproperty('/Robot/heading', 0.0)

357

358

# JSON-encoded complex data

359

_pose_json = ntproperty('/Robot/pose', '{"x": 0, "y": 0, "angle": 0}')

360

361

@property

362

def pose(self):

363

"""Get robot pose as a dictionary."""

364

try:

365

return json.loads(self._pose_json)

366

except json.JSONDecodeError:

367

return {"x": 0, "y": 0, "angle": 0}

368

369

@pose.setter

370

def pose(self, pose_dict):

371

"""Set robot pose from a dictionary."""

372

self._pose_json = json.dumps(pose_dict)

373

374

# Array properties using Value objects

375

_targets_raw = ntproperty('/Vision/targets', [])

376

377

@property

378

def targets(self):

379

"""Get vision targets as list."""

380

return self._targets_raw if isinstance(self._targets_raw, list) else []

381

382

@targets.setter

383

def targets(self, target_list):

384

"""Set vision targets."""

385

self._targets_raw = target_list

386

387

def update_position(self, x, y, angle):

388

"""Update robot position."""

389

self.x_position = x

390

self.y_position = y

391

self.heading = angle

392

393

# Also update compound pose

394

self.pose = {"x": x, "y": y, "angle": angle}

395

396

def add_target(self, target_data):

397

"""Add a vision target."""

398

current_targets = self.targets

399

current_targets.append(target_data)

400

self.targets = current_targets

401

402

# Usage

403

telemetry = RobotTelemetry()

404

telemetry.update_position(1.5, 2.3, 45.0)

405

telemetry.add_target({"distance": 3.2, "angle": 15.0})

406

407

print(f"Current pose: {telemetry.pose}")

408

print(f"Targets: {telemetry.targets}")

409

```

410

411

## Best Practices

412

413

1. **Use descriptive paths**: Choose clear, hierarchical NetworkTables paths

414

2. **Make configuration persistent**: Use `persistent=True` for values that should survive restarts

415

3. **Validate inputs**: Add property setters with validation for critical values

416

4. **Clean up choosers**: Always call `close()` on ChooserControl objects

417

5. **Handle JSON errors**: Use try/catch when working with JSON-encoded properties

418

6. **Document properties**: Use the `doc` parameter to document property purpose

419

7. **Group related properties**: Organize properties by subsystem or functionality