forked from rbmj/wpilib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DriverStation.cpp
506 lines (453 loc) · 15.1 KB
/
DriverStation.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#include "DriverStation.h"
#include "AnalogChannel.h"
#include "Synchronized.h"
#include "Timer.h"
#include "NetworkCommunication/FRCComm.h"
#include "NetworkCommunication/UsageReporting.h"
#include "MotorSafetyHelper.h"
#include "Utility.h"
#include "WPIErrors.h"
#include <strLib.h>
const uint32_t DriverStation::kBatteryModuleNumber;
const uint32_t DriverStation::kBatteryChannel;
const uint32_t DriverStation::kJoystickPorts;
const uint32_t DriverStation::kJoystickAxes;
constexpr float DriverStation::kUpdatePeriod;
DriverStation* DriverStation::m_instance = NULL;
uint8_t DriverStation::m_updateNumber = 0;
/**
* DriverStation contructor.
*
* This is only called once the first time GetInstance() is called
*/
DriverStation::DriverStation()
: m_controlData (NULL)
, m_digitalOut (0)
, m_batteryChannel (NULL)
, m_statusDataSemaphore (semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE))
, m_task ("DriverStation", (FUNCPTR)DriverStation::InitTask)
, m_dashboardHigh(m_statusDataSemaphore)
, m_dashboardLow(m_statusDataSemaphore)
, m_dashboardInUseHigh(&m_dashboardHigh)
, m_dashboardInUseLow(&m_dashboardLow)
, m_newControlData(0)
, m_packetDataAvailableSem (0)
, m_enhancedIO()
, m_waitForDataSem(0)
, m_approxMatchTimeOffset(-1.0)
, m_userInDisabled(false)
, m_userInAutonomous(false)
, m_userInTeleop(false)
, m_userInTest(false)
{
// Create a new semaphore
m_packetDataAvailableSem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
m_newControlData = semBCreate (SEM_Q_FIFO, SEM_EMPTY);
// Register that semaphore with the network communications task.
// It will signal when new packet data is available.
setNewDataSem(m_packetDataAvailableSem);
m_waitForDataSem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
m_controlData = new FRCCommonControlData;
// initialize packet number and control words to zero;
m_controlData->packetIndex = 0;
m_controlData->control = 0;
// set all joystick axis values to neutral; buttons to OFF
m_controlData->stick0Axis1 = m_controlData->stick0Axis2 = m_controlData->stick0Axis3 = 0;
m_controlData->stick1Axis1 = m_controlData->stick1Axis2 = m_controlData->stick1Axis3 = 0;
m_controlData->stick2Axis1 = m_controlData->stick2Axis2 = m_controlData->stick2Axis3 = 0;
m_controlData->stick3Axis1 = m_controlData->stick3Axis2 = m_controlData->stick3Axis3 = 0;
m_controlData->stick0Axis4 = m_controlData->stick0Axis5 = m_controlData->stick0Axis6 = 0;
m_controlData->stick1Axis4 = m_controlData->stick1Axis5 = m_controlData->stick1Axis6 = 0;
m_controlData->stick2Axis4 = m_controlData->stick2Axis5 = m_controlData->stick2Axis6 = 0;
m_controlData->stick3Axis4 = m_controlData->stick3Axis5 = m_controlData->stick3Axis6 = 0;
m_controlData->stick0Buttons = 0;
m_controlData->stick1Buttons = 0;
m_controlData->stick2Buttons = 0;
m_controlData->stick3Buttons = 0;
// initialize the analog and digital data.
m_controlData->analog1 = 0;
m_controlData->analog2 = 0;
m_controlData->analog3 = 0;
m_controlData->analog4 = 0;
m_controlData->dsDigitalIn = 0;
m_batteryChannel = new AnalogChannel(kBatteryModuleNumber, kBatteryChannel);
AddToSingletonList();
if (!m_task.Start((int32_t)this))
{
wpi_setWPIError(DriverStationTaskError);
}
}
DriverStation::~DriverStation()
{
m_task.Stop();
semDelete(m_statusDataSemaphore);
delete m_batteryChannel;
delete m_controlData;
m_instance = NULL;
semDelete(m_waitForDataSem);
// Unregister our semaphore.
setNewDataSem(0);
semDelete(m_packetDataAvailableSem);
}
void DriverStation::InitTask(DriverStation *ds)
{
ds->Run();
}
void DriverStation::Run()
{
int period = 0;
while (true)
{
semTake(m_packetDataAvailableSem, WAIT_FOREVER);
SetData();
m_enhancedIO.UpdateData();
GetData();
semFlush(m_waitForDataSem);
if (++period >= 4)
{
MotorSafetyHelper::CheckMotors();
period = 0;
}
if (m_userInDisabled)
FRC_NetworkCommunication_observeUserProgramDisabled();
if (m_userInAutonomous)
FRC_NetworkCommunication_observeUserProgramAutonomous();
if (m_userInTeleop)
FRC_NetworkCommunication_observeUserProgramTeleop();
if (m_userInTest)
FRC_NetworkCommunication_observeUserProgramTest();
}
}
/**
* Return a pointer to the singleton DriverStation.
*/
DriverStation* DriverStation::GetInstance()
{
if (m_instance == NULL)
{
m_instance = new DriverStation();
}
return m_instance;
}
/**
* Copy data from the DS task for the user.
* If no new data exists, it will just be returned, otherwise
* the data will be copied from the DS polling loop.
*/
void DriverStation::GetData()
{
static bool lastEnabled = false;
getCommonControlData(m_controlData, WAIT_FOREVER);
if (!lastEnabled && IsEnabled())
{
// If starting teleop, assume that autonomous just took up 15 seconds
if (IsAutonomous())
m_approxMatchTimeOffset = Timer::GetFPGATimestamp();
else
m_approxMatchTimeOffset = Timer::GetFPGATimestamp() - 15.0;
}
else if (lastEnabled && !IsEnabled())
{
m_approxMatchTimeOffset = -1.0;
}
lastEnabled = IsEnabled();
semGive(m_newControlData);
}
/**
* Copy status data from the DS task for the user.
*/
void DriverStation::SetData()
{
char *userStatusDataHigh;
int32_t userStatusDataHighSize;
char *userStatusDataLow;
int32_t userStatusDataLowSize;
Synchronized sync(m_statusDataSemaphore);
m_dashboardInUseHigh->GetStatusBuffer(&userStatusDataHigh, &userStatusDataHighSize);
m_dashboardInUseLow->GetStatusBuffer(&userStatusDataLow, &userStatusDataLowSize);
setStatusData(GetBatteryVoltage(), m_digitalOut, m_updateNumber,
userStatusDataHigh, userStatusDataHighSize, userStatusDataLow, userStatusDataLowSize, WAIT_FOREVER);
m_dashboardInUseHigh->Flush();
m_dashboardInUseLow->Flush();
}
/**
* Read the battery voltage from the specified AnalogChannel.
*
* This accessor assumes that the battery voltage is being measured
* through the voltage divider on an analog breakout.
*
* @return The battery voltage.
*/
float DriverStation::GetBatteryVoltage()
{
if (m_batteryChannel == NULL)
wpi_setWPIError(NullParameter);
// The Analog bumper has a voltage divider on the battery source.
// Vbatt *--/\/\/\--* Vsample *--/\/\/\--* Gnd
// 680 Ohms 1000 Ohms
return m_batteryChannel->GetAverageVoltage() * (1680.0 / 1000.0);
}
/**
* Get the value of the axis on a joystick.
* This depends on the mapping of the joystick connected to the specified port.
*
* @param stick The joystick to read.
* @param axis The analog axis value to read from the joystick.
* @return The value of the axis on the joystick.
*/
float DriverStation::GetStickAxis(uint32_t stick, uint32_t axis)
{
if (axis < 1 || axis > kJoystickAxes)
{
wpi_setWPIError(BadJoystickAxis);
return 0.0;
}
int8_t value;
switch (stick)
{
case 1:
value = m_controlData->stick0Axes[axis-1];
break;
case 2:
value = m_controlData->stick1Axes[axis-1];
break;
case 3:
value = m_controlData->stick2Axes[axis-1];
break;
case 4:
value = m_controlData->stick3Axes[axis-1];
break;
default:
wpi_setWPIError(BadJoystickIndex);
return 0.0;
}
float result;
if (value < 0)
result = ((float) value) / 128.0;
else
result = ((float) value) / 127.0;
wpi_assert(result <= 1.0 && result >= -1.0);
if (result > 1.0)
result = 1.0;
else if (result < -1.0)
result = -1.0;
return result;
}
/**
* The state of the buttons on the joystick.
* 12 buttons (4 msb are unused) from the joystick.
*
* @param stick The joystick to read.
* @return The state of the buttons on the joystick.
*/
short DriverStation::GetStickButtons(uint32_t stick)
{
if (stick < 1 || stick > 4)
wpi_setWPIErrorWithContext(ParameterOutOfRange, "stick must be between 1 and 4");
switch (stick)
{
case 1:
return m_controlData->stick0Buttons;
case 2:
return m_controlData->stick1Buttons;
case 3:
return m_controlData->stick2Buttons;
case 4:
return m_controlData->stick3Buttons;
}
return 0;
}
// 5V divided by 10 bits
#define kDSAnalogInScaling ((float)(5.0 / 1023.0))
/**
* Get an analog voltage from the Driver Station.
* The analog values are returned as voltage values for the Driver Station analog inputs.
* These inputs are typically used for advanced operator interfaces consisting of potentiometers
* or resistor networks representing values on a rotary switch.
*
* @param channel The analog input channel on the driver station to read from. Valid range is 1 - 4.
* @return The analog voltage on the input.
*/
float DriverStation::GetAnalogIn(uint32_t channel)
{
if (channel < 1 || channel > 4)
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 4");
static uint8_t reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationCIO, channel, nUsageReporting::kDriverStationCIO_Analog);
reported_mask |= (1 >> channel);
}
switch (channel)
{
case 1:
return kDSAnalogInScaling * m_controlData->analog1;
case 2:
return kDSAnalogInScaling * m_controlData->analog2;
case 3:
return kDSAnalogInScaling * m_controlData->analog3;
case 4:
return kDSAnalogInScaling * m_controlData->analog4;
}
return 0.0;
}
/**
* Get values from the digital inputs on the Driver Station.
* Return digital values from the Drivers Station. These values are typically used for buttons
* and switches on advanced operator interfaces.
* @param channel The digital input to get. Valid range is 1 - 8.
*/
bool DriverStation::GetDigitalIn(uint32_t channel)
{
if (channel < 1 || channel > 8)
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 8");
static uint8_t reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationCIO, channel, nUsageReporting::kDriverStationCIO_DigitalIn);
reported_mask |= (1 >> channel);
}
return ((m_controlData->dsDigitalIn >> (channel-1)) & 0x1) ? true : false;
}
/**
* Set a value for the digital outputs on the Driver Station.
*
* Control digital outputs on the Drivers Station. These values are typically used for
* giving feedback on a custom operator station such as LEDs.
*
* @param channel The digital output to set. Valid range is 1 - 8.
* @param value The state to set the digital output.
*/
void DriverStation::SetDigitalOut(uint32_t channel, bool value)
{
if (channel < 1 || channel > 8)
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 8");
static uint8_t reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationCIO, channel, nUsageReporting::kDriverStationCIO_DigitalOut);
reported_mask |= (1 >> channel);
}
m_digitalOut &= ~(0x1 << (channel-1));
m_digitalOut |= ((uint8_t)value << (channel-1));
}
/**
* Get a value that was set for the digital outputs on the Driver Station.
* @param channel The digital ouput to monitor. Valid range is 1 through 8.
* @return A digital value being output on the Drivers Station.
*/
bool DriverStation::GetDigitalOut(uint32_t channel)
{
if (channel < 1 || channel > 8)
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 8");
return ((m_digitalOut >> (channel-1)) & 0x1) ? true : false;;
}
bool DriverStation::IsEnabled()
{
return m_controlData->enabled;
}
bool DriverStation::IsDisabled()
{
return !m_controlData->enabled;
}
bool DriverStation::IsAutonomous()
{
return m_controlData->autonomous;
}
bool DriverStation::IsOperatorControl()
{
return !(m_controlData->autonomous || m_controlData->test);
}
bool DriverStation::IsTest()
{
return m_controlData->test;
}
/**
* Has a new control packet from the driver station arrived since the last time this function was called?
* Warning: If you call this function from more than one place at the same time,
* you will not get the get the intended behavior
* @return True if the control data has been updated since the last call.
*/
bool DriverStation::IsNewControlData()
{
return semTake(m_newControlData, NO_WAIT) == 0;
}
/**
* Is the driver station attached to a Field Management System?
* Note: This does not work with the Blue DS.
* @return True if the robot is competing on a field being controlled by a Field Management System
*/
bool DriverStation::IsFMSAttached()
{
return m_controlData->fmsAttached;
}
/**
* Return the DS packet number.
* The packet number is the index of this set of data returned by the driver station.
* Each time new data is received, the packet number (included with the sent data) is returned.
* @return The driver station packet number
*/
uint32_t DriverStation::GetPacketNumber()
{
return m_controlData->packetIndex;
}
/**
* Return the alliance that the driver station says it is on.
* This could return kRed or kBlue
* @return The Alliance enum
*/
DriverStation::Alliance DriverStation::GetAlliance()
{
if (m_controlData->dsID_Alliance == 'R') return kRed;
if (m_controlData->dsID_Alliance == 'B') return kBlue;
wpi_assert(false);
return kInvalid;
}
/**
* Return the driver station location on the field
* This could return 1, 2, or 3
* @return The location of the driver station
*/
uint32_t DriverStation::GetLocation()
{
wpi_assert ((m_controlData->dsID_Position >= '1') && (m_controlData->dsID_Position <= '3'));
return m_controlData->dsID_Position - '0';
}
/**
* Wait until a new packet comes from the driver station
* This blocks on a semaphore, so the waiting is efficient.
* This is a good way to delay processing until there is new driver station data to act on
*/
void DriverStation::WaitForData()
{
semTake(m_waitForDataSem, WAIT_FOREVER);
}
/**
* Return the approximate match time
* The FMS does not currently send the official match time to the robots
* This returns the time since the enable signal sent from the Driver Station
* At the beginning of autonomous, the time is reset to 0.0 seconds
* At the beginning of teleop, the time is reset to +15.0 seconds
* If the robot is disabled, this returns 0.0 seconds
* Warning: This is not an official time (so it cannot be used to argue with referees)
* @return Match time in seconds since the beginning of autonomous
*/
double DriverStation::GetMatchTime()
{
if (m_approxMatchTimeOffset < 0.0)
return 0.0;
return Timer::GetFPGATimestamp() - m_approxMatchTimeOffset;
}
/**
* Return the team number that the Driver Station is configured for
* @return The team number
*/
uint16_t DriverStation::GetTeamNumber()
{
return m_controlData->teamID;
}