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