blob: 7a3d778ad4252f84d8c7768e42ceb86e451d17bc [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 "Dashboard.h"
8#include "DriverStation.h"
9#include "NetworkCommunication/UsageReporting.h"
10#include "Synchronized.h"
11#include "WPIErrors.h"
12#include <strLib.h>
13
14const INT32 Dashboard::kMaxDashboardDataSize;
15
16/**
17 * Dashboard contructor.
18 *
19 * This is only called once when the DriverStation constructor is called.
20 */
21Dashboard::Dashboard(SEM_ID statusDataSem)
22 : m_userStatusData (NULL)
23 , m_userStatusDataSize (0)
24 , m_localBuffer (NULL)
25 , m_localPrintBuffer (NULL)
26 , m_packPtr (NULL)
27 , m_printSemaphore (0)
28 , m_statusDataSemaphore (statusDataSem)
29{
30 m_userStatusData = new char[kMaxDashboardDataSize];
31 m_localBuffer = new char[kMaxDashboardDataSize];
32 m_localPrintBuffer = new char[kMaxDashboardDataSize * 2];
33 m_localPrintBuffer[0] = 0;
34 m_packPtr = m_localBuffer;
35 m_printSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
36}
37
38/**
39 * Dashboard destructor.
40 *
41 * Called only when the DriverStation class is destroyed.
42 */
43Dashboard::~Dashboard()
44{
45 semDelete(m_printSemaphore);
46 m_packPtr = NULL;
47 delete [] m_localPrintBuffer;
48 m_localPrintBuffer = NULL;
49 delete [] m_localBuffer;
50 m_localBuffer = NULL;
51 delete [] m_userStatusData;
52 m_userStatusData = NULL;
53}
54
55/**
56 * Pack a signed 8-bit int into the dashboard data structure.
57 * @param value Data to be packed into the structure.
58 */
59void Dashboard::AddI8(INT8 value)
60{
61 if (!ValidateAdd(sizeof(INT8))) return;
62 memcpy(m_packPtr, (char*)&value, sizeof(value));
63 m_packPtr += sizeof(value);
64 AddedElement(kI8);
65}
66
67/**
68 * Pack a signed 16-bit int into the dashboard data structure.
69 * @param value Data to be packed into the structure.
70 */
71void Dashboard::AddI16(INT16 value)
72{
73 if (!ValidateAdd(sizeof(INT16))) return;
74 memcpy(m_packPtr, (char*)&value, sizeof(value));
75 m_packPtr += sizeof(value);
76 AddedElement(kI16);
77}
78
79/**
80 * Pack a signed 32-bit int into the dashboard data structure.
81 * @param value Data to be packed into the structure.
82 */
83void Dashboard::AddI32(INT32 value)
84{
85 if (!ValidateAdd(sizeof(INT32))) return;
86 memcpy(m_packPtr, (char*)&value, sizeof(value));
87 m_packPtr += sizeof(value);
88 AddedElement(kI32);
89}
90
91/**
92 * Pack an unsigned 8-bit int into the dashboard data structure.
93 * @param value Data to be packed into the structure.
94 */
95void Dashboard::AddU8(UINT8 value)
96{
97 if (!ValidateAdd(sizeof(UINT8))) return;
98 memcpy(m_packPtr, (char*)&value, sizeof(value));
99 m_packPtr += sizeof(value);
100 AddedElement(kU8);
101}
102
103/**
104 * Pack an unsigned 16-bit int into the dashboard data structure.
105 * @param value Data to be packed into the structure.
106 */
107void Dashboard::AddU16(UINT16 value)
108{
109 if (!ValidateAdd(sizeof(UINT16))) return;
110 memcpy(m_packPtr, (char*)&value, sizeof(value));
111 m_packPtr += sizeof(value);
112 AddedElement(kU16);
113}
114
115/**
116 * Pack an unsigned 32-bit int into the dashboard data structure.
117 * @param value Data to be packed into the structure.
118 */
119void Dashboard::AddU32(UINT32 value)
120{
121 if (!ValidateAdd(sizeof(UINT32))) return;
122 memcpy(m_packPtr, (char*)&value, sizeof(value));
123 m_packPtr += sizeof(value);
124 AddedElement(kU32);
125}
126
127/**
128 * Pack a 32-bit floating point number into the dashboard data structure.
129 * @param value Data to be packed into the structure.
130 */
131void Dashboard::AddFloat(float value)
132{
133 if (!ValidateAdd(sizeof(float))) return;
134 memcpy(m_packPtr, (char*)&value, sizeof(value));
135 m_packPtr += sizeof(value);
136 AddedElement(kFloat);
137}
138
139/**
140 * Pack a 64-bit floating point number into the dashboard data structure.
141 * @param value Data to be packed into the structure.
142 */
143void Dashboard::AddDouble(double value)
144{
145 if (!ValidateAdd(sizeof(double))) return;
146 memcpy(m_packPtr, (char*)&value, sizeof(value));
147 m_packPtr += sizeof(value);
148 AddedElement(kDouble);
149}
150
151/**
152 * Pack a boolean into the dashboard data structure.
153 * @param value Data to be packed into the structure.
154 */
155void Dashboard::AddBoolean(bool value)
156{
157 if (!ValidateAdd(sizeof(char))) return;
158 *m_packPtr = value ? 1 : 0;
159 m_packPtr += sizeof(char);
160 AddedElement(kBoolean);
161}
162
163/**
164 * Pack a NULL-terminated string of 8-bit characters into the dashboard data structure.
165 * @param value Data to be packed into the structure.
166 */
167void Dashboard::AddString(char* value)
168{
169 AddString(value, strlen(value));
170}
171
172/**
173 * Pack a string of 8-bit characters of specified length into the dashboard data structure.
174 * @param value Data to be packed into the structure.
175 * @param length The number of bytes in the string to pack.
176 */
177void Dashboard::AddString(char* value, INT32 length)
178{
179 if (!ValidateAdd(length + sizeof(length))) return;
180 memcpy(m_packPtr, (char*)&length, sizeof(length));
181 m_packPtr += sizeof(length);
182 memcpy(m_packPtr, value, length);
183 m_packPtr += length;
184 AddedElement(kString);
185}
186
187/**
188 * Start an array in the packed dashboard data structure.
189 *
190 * After calling AddArray(), call the appropriate Add method for each element of the array.
191 * Make sure you call the same add each time. An array must contain elements of the same type.
192 * You can use clusters inside of arrays to make each element of the array contain a structure of values.
193 * You can also nest arrays inside of other arrays.
194 * Every call to AddArray() must have a matching call to FinalizeArray().
195 */
196void Dashboard::AddArray()
197{
198 if (!ValidateAdd(sizeof(INT32))) return;
199 m_complexTypeStack.push(kArray);
200 m_arrayElementCount.push_back(0);
201 m_arraySizePtr.push_back((INT32*)m_packPtr);
202 m_packPtr += sizeof(INT32);
203}
204
205/**
206 * Indicate the end of an array packed into the dashboard data structure.
207 *
208 * After packing data into the array, call FinalizeArray().
209 * Every call to AddArray() must have a matching call to FinalizeArray().
210 */
211void Dashboard::FinalizeArray()
212{
213 if (m_complexTypeStack.top() != kArray)
214 {
215 wpi_setWPIError(MismatchedComplexTypeClose);
216 return;
217 }
218 m_complexTypeStack.pop();
219 *(m_arraySizePtr.back()) = m_arrayElementCount.back();
220 m_arraySizePtr.pop_back();
221 if (m_arrayElementCount.back() != 0)
222 {
223 m_expectedArrayElementType.pop_back();
224 }
225 m_arrayElementCount.pop_back();
226 AddedElement(kOther);
227}
228
229/**
230 * Start a cluster in the packed dashboard data structure.
231 *
232 * After calling AddCluster(), call the appropriate Add method for each element of the cluster.
233 * You can use clusters inside of arrays to make each element of the array contain a structure of values.
234 * Every call to AddCluster() must have a matching call to FinalizeCluster().
235 */
236void Dashboard::AddCluster()
237{
238 m_complexTypeStack.push(kCluster);
239}
240
241/**
242 * Indicate the end of a cluster packed into the dashboard data structure.
243 *
244 * After packing data into the cluster, call FinalizeCluster().
245 * Every call to AddCluster() must have a matching call to FinalizeCluster().
246 */
247void Dashboard::FinalizeCluster()
248{
249 if (m_complexTypeStack.top() != kCluster)
250 {
251 wpi_setWPIError(MismatchedComplexTypeClose);
252 return;
253 }
254 m_complexTypeStack.pop();
255 AddedElement(kOther);
256}
257
258/**
259 * Print a string to the UserData text on the Dashboard.
260 *
261 * This will add text to the buffer to send to the dashboard.
262 * You must call Finalize() periodically to actually send the buffer to the dashboard if you are not using the packed dashboard data.
263 */
264void Dashboard::Printf(const char *writeFmt, ...)
265{
266 va_list args;
267 INT32 size;
268
269 // Check if the buffer has already been used for packing.
270 if (m_packPtr != m_localBuffer)
271 {
272 wpi_setWPIError(DashboardDataCollision);
273 return;
274 }
275 va_start (args, writeFmt);
276 {
277 Synchronized sync(m_printSemaphore);
278 vsprintf(m_localPrintBuffer + strlen(m_localPrintBuffer), writeFmt, args);
279 size = strlen(m_localPrintBuffer);
280 }
281 if (size > kMaxDashboardDataSize)
282 {
283 wpi_setWPIError(DashboardDataOverflow);
284 }
285
286 va_end (args);
287}
288
289/**
290 * Indicate that the packing is complete and commit the buffer to the DriverStation.
291 *
292 * The packing of the dashboard packet is complete.
293 * If you are not using the packed dashboard data, you can call Finalize() to commit the Printf() buffer and the error string buffer.
294 * In effect, you are packing an empty structure.
295 * Prepares a packet to go to the dashboard...
296 * @return The total size of the data packed into the userData field of the status packet.
297 */
298INT32 Dashboard::Finalize()
299{
300 if (!m_complexTypeStack.empty())
301 {
302 wpi_setWPIError(MismatchedComplexTypeClose);
303 return 0;
304 }
305
306 static bool reported = false;
307 if (!reported)
308 {
309 nUsageReporting::report(nUsageReporting::kResourceType_Dashboard, 0);
310 reported = true;
311 }
312
313 Synchronized sync(m_statusDataSemaphore);
314
315 // Sequence number
316 DriverStation::GetInstance()->IncrementUpdateNumber();
317
318 // Packed Dashboard Data
319 m_userStatusDataSize = m_packPtr - m_localBuffer;
320 memcpy(m_userStatusData, m_localBuffer, m_userStatusDataSize);
321 m_packPtr = m_localBuffer;
322
323 return m_userStatusDataSize;
324}
325
326/**
327 * Called by the DriverStation class to retrieve buffers, sizes, etc. for writing
328 * to the NetworkCommunication task.
329 * This function is called while holding the m_statusDataSemaphore.
330 */
331void Dashboard::GetStatusBuffer(char **userStatusData, INT32* userStatusDataSize)
332{
333 // User printed strings
334 if (m_localPrintBuffer[0] != 0)
335 {
336 // Sequence number
337 DriverStation::GetInstance()->IncrementUpdateNumber();
338
339 INT32 printSize;
340 Synchronized syncPrint(m_printSemaphore);
341 printSize = strlen(m_localPrintBuffer);
342 m_userStatusDataSize = printSize;
343 memcpy(m_userStatusData, m_localPrintBuffer, m_userStatusDataSize);
344 m_localPrintBuffer[0] = 0;
345 }
346
347 *userStatusData = m_userStatusData;
348 *userStatusDataSize = m_userStatusDataSize;
349}
350
351/**
352 * Validate that the data being packed will fit in the buffer.
353 */
354bool Dashboard::ValidateAdd(INT32 size)
355{
356 if ((m_packPtr - m_localBuffer) + size > kMaxDashboardDataSize)
357 {
358 wpi_setWPIError(DashboardDataOverflow);
359 return false;
360 }
361 // Make sure printf is not being used at the same time.
362 if (m_localPrintBuffer[0] != 0)
363 {
364 wpi_setWPIError(DashboardDataCollision);
365 return false;
366 }
367 return true;
368}
369
370/**
371 * Check for consistent types when adding elements to an array and keep track of the number of elements in the array.
372 */
373void Dashboard::AddedElement(Type type)
374{
375 if(IsArrayRoot())
376 {
377 if (m_arrayElementCount.back() == 0)
378 {
379 m_expectedArrayElementType.push_back(type);
380 }
381 else
382 {
383 if (type != m_expectedArrayElementType.back())
384 {
385 wpi_setWPIError(InconsistentArrayValueAdded);
386 }
387 }
388 m_arrayElementCount.back() = m_arrayElementCount.back() + 1;
389 }
390}
391
392/**
393 * If the top of the type stack an array?
394 */
395bool Dashboard::IsArrayRoot()
396{
397 return !m_complexTypeStack.empty() && m_complexTypeStack.top() == kArray;
398}
399