/*----------------------------------------------------------------------------*/ /* 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 "Dashboard.h" #include "DriverStation.h" //#include "NetworkCommunication/UsageReporting.h" #include "HAL/cpp/Synchronized.hpp" #include "WPIErrors.h" #include #include const int32_t Dashboard::kMaxDashboardDataSize; /** * Dashboard contructor. * * This is only called once when the DriverStation constructor is called. */ Dashboard::Dashboard(MUTEX_ID statusDataSem) : m_userStatusData (NULL) , m_userStatusDataSize (0) , m_localBuffer (NULL) , m_localPrintBuffer (NULL) , m_packPtr (NULL) , m_printSemaphore (0) , m_statusDataSemaphore (statusDataSem) { m_userStatusData = new char[kMaxDashboardDataSize]; m_localBuffer = new char[kMaxDashboardDataSize]; m_localPrintBuffer = new char[kMaxDashboardDataSize * 2]; m_localPrintBuffer[0] = 0; m_packPtr = m_localBuffer; m_printSemaphore = initializeMutexNormal(); } /** * Dashboard destructor. * * Called only when the DriverStation class is destroyed. */ Dashboard::~Dashboard() { deleteMutex(m_printSemaphore); m_packPtr = NULL; delete [] m_localPrintBuffer; m_localPrintBuffer = NULL; delete [] m_localBuffer; m_localBuffer = NULL; delete [] m_userStatusData; m_userStatusData = NULL; } /** * Pack a signed 8-bit int into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddI8(int8_t value) { if (!ValidateAdd(sizeof(int8_t))) return; memcpy(m_packPtr, (char*)&value, sizeof(value)); m_packPtr += sizeof(value); AddedElement(kI8); } /** * Pack a signed 16-bit int into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddI16(int16_t value) { if (!ValidateAdd(sizeof(int16_t))) return; memcpy(m_packPtr, (char*)&value, sizeof(value)); m_packPtr += sizeof(value); AddedElement(kI16); } /** * Pack a signed 32-bit int into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddI32(int32_t value) { if (!ValidateAdd(sizeof(int32_t))) return; memcpy(m_packPtr, (char*)&value, sizeof(value)); m_packPtr += sizeof(value); AddedElement(kI32); } /** * Pack an unsigned 8-bit int into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddU8(uint8_t value) { if (!ValidateAdd(sizeof(uint8_t))) return; memcpy(m_packPtr, (char*)&value, sizeof(value)); m_packPtr += sizeof(value); AddedElement(kU8); } /** * Pack an unsigned 16-bit int into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddU16(uint16_t value) { if (!ValidateAdd(sizeof(uint16_t))) return; memcpy(m_packPtr, (char*)&value, sizeof(value)); m_packPtr += sizeof(value); AddedElement(kU16); } /** * Pack an unsigned 32-bit int into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddU32(uint32_t value) { if (!ValidateAdd(sizeof(uint32_t))) return; memcpy(m_packPtr, (char*)&value, sizeof(value)); m_packPtr += sizeof(value); AddedElement(kU32); } /** * Pack a 32-bit floating point number into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddFloat(float value) { if (!ValidateAdd(sizeof(float))) return; memcpy(m_packPtr, (char*)&value, sizeof(value)); m_packPtr += sizeof(value); AddedElement(kFloat); } /** * Pack a 64-bit floating point number into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddDouble(double value) { if (!ValidateAdd(sizeof(double))) return; memcpy(m_packPtr, (char*)&value, sizeof(value)); m_packPtr += sizeof(value); AddedElement(kDouble); } /** * Pack a boolean into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddBoolean(bool value) { if (!ValidateAdd(sizeof(char))) return; *m_packPtr = value ? 1 : 0; m_packPtr += sizeof(char); AddedElement(kBoolean); } /** * Pack a NULL-terminated string of 8-bit characters into the dashboard data structure. * @param value Data to be packed into the structure. */ void Dashboard::AddString(char* value) { AddString(value, strlen(value)); } /** * Pack a string of 8-bit characters of specified length into the dashboard data structure. * @param value Data to be packed into the structure. * @param length The number of bytes in the string to pack. */ void Dashboard::AddString(char* value, int32_t length) { if (!ValidateAdd(length + sizeof(length))) return; memcpy(m_packPtr, (char*)&length, sizeof(length)); m_packPtr += sizeof(length); memcpy(m_packPtr, value, length); m_packPtr += length; AddedElement(kString); } /** * Start an array in the packed dashboard data structure. * * After calling AddArray(), call the appropriate Add method for each element of the array. * Make sure you call the same add each time. An array must contain elements of the same type. * You can use clusters inside of arrays to make each element of the array contain a structure of values. * You can also nest arrays inside of other arrays. * Every call to AddArray() must have a matching call to FinalizeArray(). */ void Dashboard::AddArray() { if (!ValidateAdd(sizeof(int32_t))) return; m_complexTypeStack.push(kArray); m_arrayElementCount.push_back(0); m_arraySizePtr.push_back((int32_t*)m_packPtr); m_packPtr += sizeof(int32_t); } /** * Indicate the end of an array packed into the dashboard data structure. * * After packing data into the array, call FinalizeArray(). * Every call to AddArray() must have a matching call to FinalizeArray(). */ void Dashboard::FinalizeArray() { if (m_complexTypeStack.top() != kArray) { wpi_setWPIError(MismatchedComplexTypeClose); return; } m_complexTypeStack.pop(); *(m_arraySizePtr.back()) = m_arrayElementCount.back(); m_arraySizePtr.pop_back(); if (m_arrayElementCount.back() != 0) { m_expectedArrayElementType.pop_back(); } m_arrayElementCount.pop_back(); AddedElement(kOther); } /** * Start a cluster in the packed dashboard data structure. * * After calling AddCluster(), call the appropriate Add method for each element of the cluster. * You can use clusters inside of arrays to make each element of the array contain a structure of values. * Every call to AddCluster() must have a matching call to FinalizeCluster(). */ void Dashboard::AddCluster() { m_complexTypeStack.push(kCluster); } /** * Indicate the end of a cluster packed into the dashboard data structure. * * After packing data into the cluster, call FinalizeCluster(). * Every call to AddCluster() must have a matching call to FinalizeCluster(). */ void Dashboard::FinalizeCluster() { if (m_complexTypeStack.top() != kCluster) { wpi_setWPIError(MismatchedComplexTypeClose); return; } m_complexTypeStack.pop(); AddedElement(kOther); } /** * Print a string to the UserData text on the Dashboard. * * This will add text to the buffer to send to the dashboard. * You must call Finalize() periodically to actually send the buffer to the dashboard if you are not using the packed dashboard data. */ void Dashboard::Printf(const char *writeFmt, ...) { va_list args; int32_t size; // Check if the buffer has already been used for packing. if (m_packPtr != m_localBuffer) { wpi_setWPIError(DashboardDataCollision); return; } va_start (args, writeFmt); { Synchronized sync(m_printSemaphore); vsprintf(m_localPrintBuffer + strlen(m_localPrintBuffer), writeFmt, args); size = strlen(m_localPrintBuffer); } if (size > kMaxDashboardDataSize) { wpi_setWPIError(DashboardDataOverflow); } va_end (args); } /** * Indicate that the packing is complete and commit the buffer to the DriverStation. * * The packing of the dashboard packet is complete. * If you are not using the packed dashboard data, you can call Finalize() to commit the Printf() buffer and the error string buffer. * In effect, you are packing an empty structure. * Prepares a packet to go to the dashboard... * @return The total size of the data packed into the userData field of the status packet. */ int32_t Dashboard::Finalize() { if (!m_complexTypeStack.empty()) { wpi_setWPIError(MismatchedComplexTypeClose); return 0; } static bool reported = false; if (!reported) { HALReport(HALUsageReporting::kResourceType_Dashboard, 0); reported = true; } Synchronized sync(m_statusDataSemaphore); // Sequence number DriverStation::GetInstance()->IncrementUpdateNumber(); // Packed Dashboard Data m_userStatusDataSize = m_packPtr - m_localBuffer; memcpy(m_userStatusData, m_localBuffer, m_userStatusDataSize); m_packPtr = m_localBuffer; return m_userStatusDataSize; } /** * Called by the DriverStation class to retrieve buffers, sizes, etc. for writing * to the NetworkCommunication task. * This function is called while holding the m_statusDataSemaphore. */ void Dashboard::GetStatusBuffer(char **userStatusData, int32_t* userStatusDataSize) { // User printed strings if (m_localPrintBuffer[0] != 0) { // Sequence number DriverStation::GetInstance()->IncrementUpdateNumber(); int32_t printSize; Synchronized syncPrint(m_printSemaphore); printSize = strlen(m_localPrintBuffer); m_userStatusDataSize = printSize; memcpy(m_userStatusData, m_localPrintBuffer, m_userStatusDataSize); m_localPrintBuffer[0] = 0; } *userStatusData = m_userStatusData; *userStatusDataSize = m_userStatusDataSize; } /** * Validate that the data being packed will fit in the buffer. */ bool Dashboard::ValidateAdd(int32_t size) { if ((m_packPtr - m_localBuffer) + size > kMaxDashboardDataSize) { wpi_setWPIError(DashboardDataOverflow); return false; } // Make sure printf is not being used at the same time. if (m_localPrintBuffer[0] != 0) { wpi_setWPIError(DashboardDataCollision); return false; } return true; } /** * Check for consistent types when adding elements to an array and keep track of the number of elements in the array. */ void Dashboard::AddedElement(Type type) { if(IsArrayRoot()) { if (m_arrayElementCount.back() == 0) { m_expectedArrayElementType.push_back(type); } else { if (type != m_expectedArrayElementType.back()) { wpi_setWPIError(InconsistentArrayValueAdded); } } m_arrayElementCount.back() = m_arrayElementCount.back() + 1; } } /** * If the top of the type stack an array? */ bool Dashboard::IsArrayRoot() { return !m_complexTypeStack.empty() && m_complexTypeStack.top() == kArray; }