/** ****************************************************************************** * @addtogroup UAVObjects OpenPilot UAVObjects * @{ * @addtogroup UAV Object Manager * @brief The core UAV Objects functions, most of which are wrappered by * autogenerated defines * @{ * * * @file uavobjectmanager.h * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. * @brief Object manager library. This library holds a collection of all objects. * It can be used by all modules/libraries to find an object reference. * @see The GNU Public License (GPL) Version 3 * *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "openpilot.h" // Constants // Private types // Macros #define SET_BITS(var, shift, value, mask) var = (var & ~(mask << shift)) | (value << shift); #define OLGetIsMetaobject(olp) ((olp)->flags & OL_IS_METAOBJECT) #define OLSetIsMetaobject(olp, val) ((olp)->flags = (((val) == 0) ? ((olp)->flags & ~OL_IS_METAOBJECT) : ((olp)->flags | OL_IS_METAOBJECT))) #define OLGetIsSingleInstance(olp) ((olp)->flags & OL_IS_SINGLE_INSTANCE) #define OLSetIsSingleInstance(olp, val) ((olp)->flags = (((val) == 0) ? ((olp)->flags & ~OL_IS_SINGLE_INSTANCE) : ((olp)->flags | OL_IS_SINGLE_INSTANCE))) #define OLGetIsSettings(olp) ((olp)->flags & OL_IS_SETTINGS) #define OLSetIsSettings(olp, val) ((olp)->flags = (((val) == 0) ? ((olp)->flags & ~OL_IS_SETTINGS) : ((olp)->flags | OL_IS_SETTINGS))) /** * List of event queues and the eventmask associated with the queue. */ struct ObjectEventListStruct { xQueueHandle queue; UAVObjEventCallback cb; uint8_t eventMask; struct ObjectEventListStruct *next; }; typedef struct ObjectEventListStruct ObjectEventList; /** * List of object instances, holds the actual data structure and instance ID */ struct ObjectInstListStruct { void *data; uint16_t instId; struct ObjectInstListStruct *next; }; typedef struct ObjectInstListStruct ObjectInstList; typedef enum { OL_IS_METAOBJECT = 0x01, /** Set if this is a metaobject */ OL_IS_SINGLE_INSTANCE = 0x02, /** Set if this object has a single instance */ OL_IS_SETTINGS = 0x04 /** Set if this object is a settings object */ } ObjectListFlags; /** * List of objects registered in the object manager */ struct ObjectListStruct { uint32_t id; /** The object ID */ const char *name; /** The object name */ ObjectListFlags flags; /** The object list mode flags */ uint16_t numBytes; /** Number of data bytes contained in the object (for a single instance) */ uint16_t numInstances; /** Number of instances */ struct ObjectListStruct *linkedObj; /** Linked object, for regular objects this is the metaobject and for metaobjects it is the parent object */ ObjectInstList instances; /** List of object instances, instance 0 always exists */ ObjectEventList *events; /** Event queues registered on the object */ struct ObjectListStruct *next; /** Needed by linked list library (utlist.h) */ }; typedef struct ObjectListStruct ObjectList; // Private functions static int32_t sendEvent(ObjectList * obj, uint16_t instId, UAVObjEventType event); static ObjectInstList *createInstance(ObjectList * obj, uint16_t instId); static ObjectInstList *getInstance(ObjectList * obj, uint16_t instId); static int32_t connectObj(UAVObjHandle obj, xQueueHandle queue, UAVObjEventCallback cb, uint8_t eventMask); static int32_t disconnectObj(UAVObjHandle obj, xQueueHandle queue, UAVObjEventCallback cb); #if defined(PIOS_INCLUDE_SDCARD) static void objectFilename(ObjectList * obj, uint8_t * filename); static void customSPrintf(uint8_t * buffer, uint8_t * format, ...); #endif // Private variables static ObjectList *objList; static xSemaphoreHandle mutex; static UAVObjMetadata defMetadata; static UAVObjStats stats; /** * Initialize the object manager * \return 0 Success * \return -1 Failure */ int32_t UAVObjInitialize() { // Initialize variables objList = NULL; memset(&stats, 0, sizeof(UAVObjStats)); // Create mutex mutex = xSemaphoreCreateRecursiveMutex(); if (mutex == NULL) return -1; // Initialize default metadata structure (metadata of metaobjects) UAVObjMetadataInitialize(&defMetadata); // Done return 0; } /** * Get the statistics counters * @param[out] statsOut The statistics counters will be copied there */ void UAVObjGetStats(UAVObjStats * statsOut) { xSemaphoreTakeRecursive(mutex, portMAX_DELAY); memcpy(statsOut, &stats, sizeof(UAVObjStats)); xSemaphoreGiveRecursive(mutex); } /** * Clear the statistics counters */ void UAVObjClearStats() { xSemaphoreTakeRecursive(mutex, portMAX_DELAY); memset(&stats, 0, sizeof(UAVObjStats)); xSemaphoreGiveRecursive(mutex); } /** * Register and new object in the object manager. * \param[in] id Unique object ID * \param[in] name Object name * \param[in] nameName Metaobject name * \param[in] isMetaobject Is this a metaobject (1:true, 0:false) * \param[in] isSingleInstance Is this a single instance or multi-instance object * \param[in] isSettings Is this a settings object * \param[in] numBytes Number of bytes of object data (for one instance) * \param[in] initCb Default field and metadata initialization function * \return Object handle, or NULL if failure. * \return */ UAVObjHandle UAVObjRegister(uint32_t id, const char *name, const char *metaName, int32_t isMetaobject, int32_t isSingleInstance, int32_t isSettings, uint32_t numBytes, UAVObjInitializeCallback initCb) { ObjectList *objEntry; ObjectInstList *instEntry; ObjectList *metaObj; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Check that the object is not already registered LL_FOREACH(objList, objEntry) { if (objEntry->id == id) { // Already registered, ignore xSemaphoreGiveRecursive(mutex); return NULL; } } // Create and append entry objEntry = (ObjectList *) pvPortMalloc(sizeof(ObjectList)); if (objEntry == NULL) { xSemaphoreGiveRecursive(mutex); return NULL; } objEntry->id = id; objEntry->name = name; OLSetIsMetaobject(objEntry, isMetaobject); OLSetIsSingleInstance(objEntry, isSingleInstance); OLSetIsSettings(objEntry, isSettings); objEntry->numBytes = numBytes; objEntry->events = NULL; objEntry->numInstances = 0; objEntry->instances.data = NULL; objEntry->instances.instId = 0xFFFF; objEntry->instances.next = NULL; objEntry->linkedObj = NULL; // will be set later LL_APPEND(objList, objEntry); // Create instance zero instEntry = createInstance(objEntry, 0); if (instEntry == NULL) { xSemaphoreGiveRecursive(mutex); return NULL; } // Create metaobject and update linkedObj if (isMetaobject) { objEntry->linkedObj = NULL; // will be set later } else { // Create metaobject metaObj = (ObjectList *) UAVObjRegister(id + 1, metaName, NULL, 1, 1, 0, sizeof (UAVObjMetadata), NULL); // Link two objects objEntry->linkedObj = metaObj; metaObj->linkedObj = objEntry; } // Initialize object fields and metadata to default values if (initCb != NULL) { initCb((UAVObjHandle) objEntry, 0); } // Attempt to load object's metadata from the SD card (not done directly on the metaobject, but through the object) if (!OLGetIsMetaobject(objEntry)) { UAVObjLoad((UAVObjHandle) objEntry->linkedObj, 0); } // If this is a settings object, attempt to load from SD card if (OLGetIsSettings(objEntry)) { UAVObjLoad((UAVObjHandle) objEntry, 0); } // Release lock xSemaphoreGiveRecursive(mutex); return (UAVObjHandle) objEntry; } /** * Retrieve an object from the list given its id * \param[in] The object ID * \return The object or NULL if not found. */ UAVObjHandle UAVObjGetByID(uint32_t id) { ObjectList *objEntry; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Look for object LL_FOREACH(objList, objEntry) { if (objEntry->id == id) { // Release lock xSemaphoreGiveRecursive(mutex); // Done, object found return (UAVObjHandle) objEntry; } } // Object not found, release lock and return error xSemaphoreGiveRecursive(mutex); return NULL; } /** * Retrieve an object from the list given its name * \param[in] name The name of the object * \return The object or NULL if not found. */ UAVObjHandle UAVObjGetByName(char *name) { ObjectList *objEntry; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Look for object LL_FOREACH(objList, objEntry) { if (objEntry->name != NULL && strcmp(objEntry->name, name) == 0) { // Release lock xSemaphoreGiveRecursive(mutex); // Done, object found return (UAVObjHandle) objEntry; } } // Object not found, release lock and return error xSemaphoreGiveRecursive(mutex); return NULL; } /** * Get the object's ID * \param[in] obj The object handle * \return The object ID */ uint32_t UAVObjGetID(UAVObjHandle obj) { return ((ObjectList *) obj)->id; } /** * Get the object's name * \param[in] obj The object handle * \return The object's name */ const char *UAVObjGetName(UAVObjHandle obj) { return ((ObjectList *) obj)->name; } /** * Get the number of bytes of the object's data (for one instance) * \param[in] obj The object handle * \return The number of bytes */ uint32_t UAVObjGetNumBytes(UAVObjHandle obj) { return ((ObjectList *) obj)->numBytes; } /** * Get the object this object is linked to. For regular objects, the linked object * is the metaobject. For metaobjects the linked object is the parent object. * This function is normally only needed by the telemetry module. * \param[in] obj The object handle * \return The object linked object handle */ UAVObjHandle UAVObjGetLinkedObj(UAVObjHandle obj) { return (UAVObjHandle) (((ObjectList *) obj)->linkedObj); } /** * Get the number of instances contained in the object. * \param[in] obj The object handle * \return The number of instances */ uint16_t UAVObjGetNumInstances(UAVObjHandle obj) { uint32_t numInstances; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); numInstances = ((ObjectList *) obj)->numInstances; xSemaphoreGiveRecursive(mutex); return numInstances; } /** * Create a new instance in the object. * \param[in] obj The object handle * \return The instance ID or 0 if an error */ uint16_t UAVObjCreateInstance(UAVObjHandle obj, UAVObjInitializeCallback initCb) { ObjectList *objEntry; ObjectInstList *instEntry; // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Create new instance objEntry = (ObjectList *) obj; instEntry = createInstance(objEntry, objEntry->numInstances); if (instEntry == NULL) { xSemaphoreGiveRecursive(mutex); return -1; } // Initialize instance data if (initCb != NULL) { initCb(obj, instEntry->instId); } // Unlock xSemaphoreGiveRecursive(mutex); return instEntry->instId; } /** * Does this object contains a single instance or multiple instances? * \param[in] obj The object handle * \return True (1) if this is a single instance object */ int32_t UAVObjIsSingleInstance(UAVObjHandle obj) { return OLGetIsSingleInstance((ObjectList *) obj); } /** * Is this a metaobject? * \param[in] obj The object handle * \return True (1) if this is metaobject */ int32_t UAVObjIsMetaobject(UAVObjHandle obj) { return OLGetIsMetaobject((ObjectList *) obj); } /** * Is this a settings object? * \param[in] obj The object handle * \return True (1) if this is a settings object */ int32_t UAVObjIsSettings(UAVObjHandle obj) { return OLGetIsSettings((ObjectList *) obj); } /** * Unpack an object from a byte array * \param[in] obj The object handle * \param[in] instId The instance ID * \param[in] dataIn The byte array * \return 0 if success or -1 if failure */ int32_t UAVObjUnpack(UAVObjHandle obj, uint16_t instId, const uint8_t * dataIn) { ObjectList *objEntry; ObjectInstList *instEntry; // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast handle to object objEntry = (ObjectList *) obj; // Get the instance instEntry = getInstance(objEntry, instId); // If the instance does not exist create it and any other instances before it if (instEntry == NULL) { instEntry = createInstance(objEntry, instId); if (instEntry == NULL) { // Error, unlock and return xSemaphoreGiveRecursive(mutex); return -1; } } // Set the data memcpy(instEntry->data, dataIn, objEntry->numBytes); // Fire event sendEvent(objEntry, instId, EV_UNPACKED); // Unlock xSemaphoreGiveRecursive(mutex); return 0; } /** * Pack an object to a byte array * \param[in] obj The object handle * \param[in] instId The instance ID * \param[out] dataOut The byte array * \return 0 if success or -1 if failure */ int32_t UAVObjPack(UAVObjHandle obj, uint16_t instId, uint8_t * dataOut) { ObjectList *objEntry; ObjectInstList *instEntry; // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast handle to object objEntry = (ObjectList *) obj; // Get the instance instEntry = getInstance(objEntry, instId); if (instEntry == NULL) { // Error, unlock and return xSemaphoreGiveRecursive(mutex); return -1; } // Pack data memcpy(dataOut, instEntry->data, objEntry->numBytes); // Unlock xSemaphoreGiveRecursive(mutex); return 0; } /** * Save the data of the specified object instance to the file system (SD card). * The object will be appended and the file will not be closed. * The object data can be restored using the UAVObjLoad function. * @param[in] obj The object handle. * @param[in] instId The instance ID * @param[in] file File to append to * @return 0 if success or -1 if failure */ int32_t UAVObjSaveToFile(UAVObjHandle obj, uint16_t instId, FILEINFO * file) { #if defined(PIOS_INCLUDE_SDCARD) uint32_t bytesWritten; ObjectList *objEntry; ObjectInstList *instEntry; // Check for file system availability if (PIOS_SDCARD_IsMounted() == 0) { return -1; } // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast to object objEntry = (ObjectList *) obj; // Get the instance information instEntry = getInstance(objEntry, instId); if (instEntry == NULL) { xSemaphoreGiveRecursive(mutex); return -1; } // Write the object ID PIOS_FWRITE(file, &objEntry->id, sizeof(objEntry->id), &bytesWritten); // Write the instance ID if (!OLGetIsSingleInstance(objEntry)) { PIOS_FWRITE(file, &instEntry->instId, sizeof(instEntry->instId), &bytesWritten); } // Write the data and check that the write was successful PIOS_FWRITE(file, instEntry->data, objEntry->numBytes, &bytesWritten); if (bytesWritten != objEntry->numBytes) { xSemaphoreGiveRecursive(mutex); return -1; } // Done xSemaphoreGiveRecursive(mutex); #endif /* PIOS_INCLUDE_SDCARD */ return 0; } /** * Save the data of the specified object to the file system (SD card). * If the object contains multiple instances, all of them will be saved. * A new file with the name of the object will be created. * The object data can be restored using the UAVObjLoad function. * @param[in] obj The object handle. * @param[in] instId The instance ID * @param[in] file File to append to * @return 0 if success or -1 if failure */ int32_t UAVObjSave(UAVObjHandle obj, uint16_t instId) { #if defined(PIOS_INCLUDE_FLASH_SECTOR_SETTINGS) ObjectList *objEntry = (ObjectList *) obj; if (objEntry == NULL) return -1; ObjectInstList *instEntry = getInstance(objEntry, instId); if (instEntry == NULL) return -1; if (instEntry->data == NULL) return -1; if (PIOS_FLASHFS_ObjSave(obj, instId, instEntry->data) != 0) return -1; #endif #if defined(PIOS_INCLUDE_SDCARD) FILEINFO file; ObjectList *objEntry; uint8_t filename[14]; // Check for file system availability if (PIOS_SDCARD_IsMounted() == 0) { return -1; } // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast to object objEntry = (ObjectList *) obj; // Get filename objectFilename(objEntry, filename); // Open file if (PIOS_FOPEN_WRITE(filename, file)) { xSemaphoreGiveRecursive(mutex); return -1; } // Append object if (UAVObjSaveToFile(obj, instId, &file) == -1) { PIOS_FCLOSE(file); xSemaphoreGiveRecursive(mutex); return -1; } // Done, close file and unlock PIOS_FCLOSE(file); xSemaphoreGiveRecursive(mutex); #endif /* PIOS_INCLUDE_SDCARD */ return 0; } /** * Load an object from the file system (SD card). * @param[in] file File to read from * @return The handle of the object loaded or NULL if a failure */ UAVObjHandle UAVObjLoadFromFile(FILEINFO * file) { #if defined(PIOS_INCLUDE_SDCARD) uint32_t bytesRead; ObjectList *objEntry; ObjectInstList *instEntry; uint32_t objId; uint16_t instId; UAVObjHandle obj; // Check for file system availability if (PIOS_SDCARD_IsMounted() == 0) { return NULL; } // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Read the object ID if (PIOS_FREAD(file, &objId, sizeof(objId), &bytesRead)) { xSemaphoreGiveRecursive(mutex); return NULL; } // Get the object obj = UAVObjGetByID(objId); if (obj == 0) { xSemaphoreGiveRecursive(mutex); return NULL; } objEntry = (ObjectList *) obj; // Get the instance ID instId = 0; if (!OLGetIsSingleInstance(objEntry)) { if (PIOS_FREAD (file, &instId, sizeof(instId), &bytesRead)) { xSemaphoreGiveRecursive(mutex); return NULL; } } // Get the instance information instEntry = getInstance(objEntry, instId); // If the instance does not exist create it and any other instances before it if (instEntry == NULL) { instEntry = createInstance(objEntry, instId); if (instEntry == NULL) { // Error, unlock and return xSemaphoreGiveRecursive(mutex); return NULL; } } // Read the instance data if (PIOS_FREAD (file, instEntry->data, objEntry->numBytes, &bytesRead)) { xSemaphoreGiveRecursive(mutex); return NULL; } // Fire event sendEvent(objEntry, instId, EV_UNPACKED); // Unlock xSemaphoreGiveRecursive(mutex); return obj; #else /* PIOS_INCLUDE_SDCARD */ return NULL; #endif } /** * Load an object from the file system (SD card). * A file with the name of the object will be opened. * The object data can be saved using the UAVObjSave function. * @param[in] obj The object handle. * @param[in] instId The object instance * @return 0 if success or -1 if failure */ int32_t UAVObjLoad(UAVObjHandle obj, uint16_t instId) { #if defined(PIOS_INCLUDE_FLASH_SECTOR_SETTINGS) ObjectList *objEntry = (ObjectList *) obj; if (objEntry == NULL) return -1; ObjectInstList *instEntry = getInstance(objEntry, instId); if (instEntry == NULL) return -1; if (instEntry->data == NULL) return -1; // Fire event on success if (PIOS_FLASHFS_ObjLoad(obj, instId, instEntry->data) == 0) sendEvent(objEntry, instId, EV_UNPACKED); else return -1; #endif #if defined(PIOS_INCLUDE_SDCARD) FILEINFO file; ObjectList *objEntry; UAVObjHandle loadedObj; ObjectList *loadedObjEntry; uint8_t filename[14]; // Check for file system availability if (PIOS_SDCARD_IsMounted() == 0) { return -1; } // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast to object objEntry = (ObjectList *) obj; // Get filename objectFilename(objEntry, filename); // Open file if (PIOS_FOPEN_READ(filename, file)) { xSemaphoreGiveRecursive(mutex); return -1; } // Load object loadedObj = UAVObjLoadFromFile(&file); if (loadedObj == 0) { PIOS_FCLOSE(file); xSemaphoreGiveRecursive(mutex); return -1; } // Check that the IDs match loadedObjEntry = (ObjectList *) loadedObj; if (loadedObjEntry->id != objEntry->id) { PIOS_FCLOSE(file); xSemaphoreGiveRecursive(mutex); return -1; } // Done, close file and unlock PIOS_FCLOSE(file); xSemaphoreGiveRecursive(mutex); #endif /* PIOS_INCLUDE_SDCARD */ return 0; } /** * Delete an object from the file system (SD card). * @param[in] obj The object handle. * @param[in] instId The object instance * @return 0 if success or -1 if failure */ int32_t UAVObjDelete(UAVObjHandle obj, uint16_t instId) { #if defined(PIOS_INCLUDE_FLASH_SECTOR_SETTINGS) PIOS_FLASHFS_ObjDelete(obj, instId); #endif #if defined(PIOS_INCLUDE_SDCARD) ObjectList *objEntry; uint8_t filename[14]; // Check for file system availability if (PIOS_SDCARD_IsMounted() == 0) { return -1; } // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast to object objEntry = (ObjectList *) obj; // Get filename objectFilename(objEntry, filename); // Delete file PIOS_FUNLINK(filename); // Done xSemaphoreGiveRecursive(mutex); #endif /* PIOS_INCLUDE_SDCARD */ return 0; } /** * Save all settings objects to the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjSaveSettings() { ObjectList *objEntry; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Save all settings objects LL_FOREACH(objList, objEntry) { // Check if this is a settings object if (OLGetIsSettings(objEntry)) { // Save object if (UAVObjSave((UAVObjHandle) objEntry, 0) == -1) { xSemaphoreGiveRecursive(mutex); return -1; } } } // Done xSemaphoreGiveRecursive(mutex); return 0; } /** * Load all settings objects from the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjLoadSettings() { ObjectList *objEntry; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Load all settings objects LL_FOREACH(objList, objEntry) { // Check if this is a settings object if (OLGetIsSettings(objEntry)) { // Load object if (UAVObjLoad((UAVObjHandle) objEntry, 0) == -1) { xSemaphoreGiveRecursive(mutex); return -1; } } } // Done xSemaphoreGiveRecursive(mutex); return 0; } /** * Delete all settings objects from the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjDeleteSettings() { ObjectList *objEntry; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Save all settings objects LL_FOREACH(objList, objEntry) { // Check if this is a settings object if (OLGetIsSettings(objEntry)) { // Save object if (UAVObjDelete((UAVObjHandle) objEntry, 0) == -1) { xSemaphoreGiveRecursive(mutex); return -1; } } } // Done xSemaphoreGiveRecursive(mutex); return 0; } /** * Save all metaobjects to the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjSaveMetaobjects() { ObjectList *objEntry; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Save all settings objects LL_FOREACH(objList, objEntry) { // Check if this is a settings object if (OLGetIsMetaobject(objEntry)) { // Save object if (UAVObjSave((UAVObjHandle) objEntry, 0) == -1) { xSemaphoreGiveRecursive(mutex); return -1; } } } // Done xSemaphoreGiveRecursive(mutex); return 0; } /** * Load all metaobjects from the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjLoadMetaobjects() { ObjectList *objEntry; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Load all settings objects LL_FOREACH(objList, objEntry) { // Check if this is a settings object if (OLGetIsMetaobject(objEntry)) { // Load object if (UAVObjLoad((UAVObjHandle) objEntry, 0) == -1) { xSemaphoreGiveRecursive(mutex); return -1; } } } // Done xSemaphoreGiveRecursive(mutex); return 0; } /** * Delete all metaobjects from the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjDeleteMetaobjects() { ObjectList *objEntry; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Load all settings objects LL_FOREACH(objList, objEntry) { // Check if this is a settings object if (OLGetIsMetaobject(objEntry)) { // Load object if (UAVObjDelete((UAVObjHandle) objEntry, 0) == -1) { xSemaphoreGiveRecursive(mutex); return -1; } } } // Done xSemaphoreGiveRecursive(mutex); return 0; } /** * Set the object data * \param[in] obj The object handle * \param[in] dataIn The object's data structure * \return 0 if success or -1 if failure */ int32_t UAVObjSetData(UAVObjHandle obj, const void *dataIn) { return UAVObjSetInstanceData(obj, 0, dataIn); } /** * Set the object data * \param[in] obj The object handle * \param[in] dataIn The object's data structure * \return 0 if success or -1 if failure */ int32_t UAVObjSetDataField(UAVObjHandle obj, const void* dataIn, uint32_t offset, uint32_t size) { return UAVObjSetInstanceDataField(obj, 0, dataIn, offset, size); } /** * Get the object data * \param[in] obj The object handle * \param[out] dataOut The object's data structure * \return 0 if success or -1 if failure */ int32_t UAVObjGetData(UAVObjHandle obj, void *dataOut) { return UAVObjGetInstanceData(obj, 0, dataOut); } /** * Get the object data * \param[in] obj The object handle * \param[out] dataOut The object's data structure * \return 0 if success or -1 if failure */ int32_t UAVObjGetDataField(UAVObjHandle obj, void* dataOut, uint32_t offset, uint32_t size) { return UAVObjGetInstanceDataField(obj, 0, dataOut, offset, size); } /** * Set the data of a specific object instance * \param[in] obj The object handle * \param[in] instId The object instance ID * \param[in] dataIn The object's data structure * \return 0 if success or -1 if failure */ int32_t UAVObjSetInstanceData(UAVObjHandle obj, uint16_t instId, const void *dataIn) { ObjectList *objEntry; ObjectInstList *instEntry; UAVObjMetadata *mdata; // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast to object info objEntry = (ObjectList *) obj; // Check access level if (!OLGetIsMetaobject(objEntry)) { mdata = (UAVObjMetadata *) (objEntry->linkedObj->instances. data); if (UAVObjGetAccess(mdata) == ACCESS_READONLY) { xSemaphoreGiveRecursive(mutex); return -1; } } // Get instance information instEntry = getInstance(objEntry, instId); if (instEntry == NULL) { // Error, unlock and return xSemaphoreGiveRecursive(mutex); return -1; } // Set data memcpy(instEntry->data, dataIn, objEntry->numBytes); // Fire event sendEvent(objEntry, instId, EV_UPDATED); // Unlock xSemaphoreGiveRecursive(mutex); return 0; } /** * Set the data of a specific object instance * \param[in] obj The object handle * \param[in] instId The object instance ID * \param[in] dataIn The object's data structure * \return 0 if success or -1 if failure */ int32_t UAVObjSetInstanceDataField(UAVObjHandle obj, uint16_t instId, const void* dataIn, uint32_t offset, uint32_t size) { ObjectList* objEntry; ObjectInstList* instEntry; UAVObjMetadata* mdata; // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast to object info objEntry = (ObjectList*)obj; // Check access level if ( !OLGetIsMetaobject(objEntry) ) { mdata = (UAVObjMetadata*)(objEntry->linkedObj->instances.data); if ( UAVObjGetAccess(mdata) == ACCESS_READONLY ) { xSemaphoreGiveRecursive(mutex); return -1; } } // Get instance information instEntry = getInstance(objEntry, instId); if ( instEntry == NULL ) { // Error, unlock and return xSemaphoreGiveRecursive(mutex); return -1; } // return if we set too much of what we have if ( (size + offset) > objEntry->numBytes) { // Error, unlock and return xSemaphoreGiveRecursive(mutex); return -1; } // Set data memcpy(instEntry->data + offset, dataIn, size); // Fire event sendEvent(objEntry, instId, EV_UPDATED); // Unlock xSemaphoreGiveRecursive(mutex); return 0; } /** * Get the data of a specific object instance * \param[in] obj The object handle * \param[in] instId The object instance ID * \param[out] dataOut The object's data structure * \return 0 if success or -1 if failure */ int32_t UAVObjGetInstanceData(UAVObjHandle obj, uint16_t instId, void *dataOut) { ObjectList *objEntry; ObjectInstList *instEntry; // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast to object info objEntry = (ObjectList *) obj; // Get instance information instEntry = getInstance(objEntry, instId); if (instEntry == NULL) { // Error, unlock and return xSemaphoreGiveRecursive(mutex); return -1; } // Set data memcpy(dataOut, instEntry->data, objEntry->numBytes); // Unlock xSemaphoreGiveRecursive(mutex); return 0; } /** * Get the data of a specific object instance * \param[in] obj The object handle * \param[in] instId The object instance ID * \param[out] dataOut The object's data structure * \return 0 if success or -1 if failure */ int32_t UAVObjGetInstanceDataField(UAVObjHandle obj, uint16_t instId, void* dataOut, uint32_t offset, uint32_t size) { ObjectList* objEntry; ObjectInstList* instEntry; // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Cast to object info objEntry = (ObjectList*)obj; // Get instance information instEntry = getInstance(objEntry, instId); if ( instEntry == NULL ) { // Error, unlock and return xSemaphoreGiveRecursive(mutex); return -1; } // return if we request too much of what we can give if ( (size + offset) > objEntry->numBytes) { // Error, unlock and return xSemaphoreGiveRecursive(mutex); return -1; } // Set data memcpy(dataOut, instEntry->data + offset, size); // Unlock xSemaphoreGiveRecursive(mutex); return 0; } /** * Set the object metadata * \param[in] obj The object handle * \param[in] dataIn The object's metadata structure * \return 0 if success or -1 if failure */ int32_t UAVObjSetMetadata(UAVObjHandle obj, const UAVObjMetadata * dataIn) { ObjectList *objEntry; // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Set metadata (metadata of metaobjects can not be modified) objEntry = (ObjectList *) obj; if (!OLGetIsMetaobject(objEntry)) { UAVObjSetData((UAVObjHandle) objEntry->linkedObj, dataIn); } else { return -1; } // Unlock xSemaphoreGiveRecursive(mutex); return 0; } /** * Get the object metadata * \param[in] obj The object handle * \param[out] dataOut The object's metadata structure * \return 0 if success or -1 if failure */ int32_t UAVObjGetMetadata(UAVObjHandle obj, UAVObjMetadata * dataOut) { ObjectList *objEntry; // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Get metadata objEntry = (ObjectList *) obj; if (OLGetIsMetaobject(objEntry)) { memcpy(dataOut, &defMetadata, sizeof(UAVObjMetadata)); } else { UAVObjGetData((UAVObjHandle) objEntry->linkedObj, dataOut); } // Unlock xSemaphoreGiveRecursive(mutex); return 0; } /** * Initialize a UAVObjMetadata object. * \param[in] metadata The metadata object */ void UAVObjMetadataInitialize(UAVObjMetadata* metadata) { metadata->flags = ACCESS_READWRITE << UAVOBJ_ACCESS_SHIFT | ACCESS_READWRITE << UAVOBJ_GCS_ACCESS_SHIFT | 1 << UAVOBJ_TELEMETRY_ACKED_SHIFT | 1 << UAVOBJ_GCS_TELEMETRY_ACKED_SHIFT | UPDATEMODE_ONCHANGE << UAVOBJ_TELEMETRY_UPDATE_MODE_SHIFT | UPDATEMODE_ONCHANGE << UAVOBJ_GCS_TELEMETRY_UPDATE_MODE_SHIFT; metadata->telemetryUpdatePeriod = 0; metadata->gcsTelemetryUpdatePeriod = 0; metadata->loggingUpdatePeriod = 0; } /** * Get the UAVObject metadata access member * \param[in] metadata The metadata object * \return the access type */ UAVObjAccessType UAVObjGetAccess(const UAVObjMetadata* metadata) { return (metadata->flags >> UAVOBJ_ACCESS_SHIFT) & 1; } /** * Set the UAVObject metadata access member * \param[in] metadata The metadata object * \param[in] mode The access mode */ void UAVObjSetAccess(UAVObjMetadata* metadata, UAVObjAccessType mode) { SET_BITS(metadata->flags, UAVOBJ_ACCESS_SHIFT, mode, 1); } /** * Get the UAVObject metadata GCS access member * \param[in] metadata The metadata object * \return the GCS access type */ UAVObjAccessType UAVObjGetGcsAccess(const UAVObjMetadata* metadata) { return (metadata->flags >> UAVOBJ_GCS_ACCESS_SHIFT) & 1; } /** * Set the UAVObject metadata GCS access member * \param[in] metadata The metadata object * \param[in] mode The access mode */ void UAVObjSetGcsAccess(UAVObjMetadata* metadata, UAVObjAccessType mode) { SET_BITS(metadata->flags, UAVOBJ_GCS_ACCESS_SHIFT, mode, 1); } /** * Get the UAVObject metadata telemetry acked member * \param[in] metadata The metadata object * \return the telemetry acked boolean */ uint8_t UAVObjGetTelemetryAcked(const UAVObjMetadata* metadata) { return (metadata->flags >> UAVOBJ_TELEMETRY_ACKED_SHIFT) & 1; } /** * Set the UAVObject metadata telemetry acked member * \param[in] metadata The metadata object * \param[in] val The telemetry acked boolean */ void UAVObjSetTelemetryAcked(UAVObjMetadata* metadata, uint8_t val) { SET_BITS(metadata->flags, UAVOBJ_TELEMETRY_ACKED_SHIFT, val, 1); } /** * Get the UAVObject metadata GCS telemetry acked member * \param[in] metadata The metadata object * \return the telemetry acked boolean */ uint8_t UAVObjGetGcsTelemetryAcked(const UAVObjMetadata* metadata) { return (metadata->flags >> UAVOBJ_GCS_TELEMETRY_ACKED_SHIFT) & 1; } /** * Set the UAVObject metadata GCS telemetry acked member * \param[in] metadata The metadata object * \param[in] val The GCS telemetry acked boolean */ void UAVObjSetGcsTelemetryAcked(UAVObjMetadata* metadata, uint8_t val) { SET_BITS(metadata->flags, UAVOBJ_GCS_TELEMETRY_ACKED_SHIFT, val, 1); } /** * Get the UAVObject metadata telemetry update mode * \param[in] metadata The metadata object * \return the telemetry update mode */ UAVObjUpdateMode UAVObjGetTelemetryUpdateMode(const UAVObjMetadata* metadata) { return (metadata->flags >> UAVOBJ_TELEMETRY_UPDATE_MODE_SHIFT) & UAVOBJ_UPDATE_MODE_MASK; } /** * Set the UAVObject metadata telemetry update mode member * \param[in] metadata The metadata object * \param[in] val The telemetry update mode */ void UAVObjSetTelemetryUpdateMode(UAVObjMetadata* metadata, UAVObjUpdateMode val) { SET_BITS(metadata->flags, UAVOBJ_TELEMETRY_UPDATE_MODE_SHIFT, val, UAVOBJ_UPDATE_MODE_MASK); } /** * Get the UAVObject metadata GCS telemetry update mode * \param[in] metadata The metadata object * \return the GCS telemetry update mode */ UAVObjUpdateMode UAVObjGetGcsTelemetryUpdateMode(const UAVObjMetadata* metadata) { return (metadata->flags >> UAVOBJ_GCS_TELEMETRY_UPDATE_MODE_SHIFT) & UAVOBJ_UPDATE_MODE_MASK; } /** * Set the UAVObject metadata GCS telemetry update mode member * \param[in] metadata The metadata object * \param[in] val The GCS telemetry update mode */ void UAVObjSetGcsTelemetryUpdateMode(UAVObjMetadata* metadata, UAVObjUpdateMode val) { SET_BITS(metadata->flags, UAVOBJ_GCS_TELEMETRY_UPDATE_MODE_SHIFT, val, UAVOBJ_UPDATE_MODE_MASK); } /** * Check if an object is read only * \param[in] obj The object handle * \return * \arg 0 if not read only * \arg 1 if read only * \arg -1 if unable to get meta data */ int8_t UAVObjReadOnly(UAVObjHandle obj) { ObjectList *objEntry; UAVObjMetadata *mdata; // Cast to object info objEntry = (ObjectList *) obj; // Check access level if (!OLGetIsMetaobject(objEntry)) { mdata = (UAVObjMetadata *) (objEntry->linkedObj->instances. data); return UAVObjGetAccess(mdata) == ACCESS_READONLY; } return -1; } /** * Connect an event queue to the object, if the queue is already connected then the event mask is only updated. * All events matching the event mask will be pushed to the event queue. * \param[in] obj The object handle * \param[in] queue The event queue * \param[in] eventMask The event mask, if EV_MASK_ALL then all events are enabled (e.g. EV_UPDATED | EV_UPDATED_MANUAL) * \return 0 if success or -1 if failure */ int32_t UAVObjConnectQueue(UAVObjHandle obj, xQueueHandle queue, uint8_t eventMask) { int32_t res; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); res = connectObj(obj, queue, 0, eventMask); xSemaphoreGiveRecursive(mutex); return res; } /** * Disconnect an event queue from the object. * \param[in] obj The object handle * \param[in] queue The event queue * \return 0 if success or -1 if failure */ int32_t UAVObjDisconnectQueue(UAVObjHandle obj, xQueueHandle queue) { int32_t res; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); res = disconnectObj(obj, queue, 0); xSemaphoreGiveRecursive(mutex); return res; } /** * Connect an event callback to the object, if the callback is already connected then the event mask is only updated. * The supplied callback will be invoked on all events matching the event mask. * \param[in] obj The object handle * \param[in] cb The event callback * \param[in] eventMask The event mask, if EV_MASK_ALL then all events are enabled (e.g. EV_UPDATED | EV_UPDATED_MANUAL) * \return 0 if success or -1 if failure */ int32_t UAVObjConnectCallback(UAVObjHandle obj, UAVObjEventCallback cb, uint8_t eventMask) { int32_t res; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); res = connectObj(obj, 0, cb, eventMask); xSemaphoreGiveRecursive(mutex); return res; } /** * Disconnect an event callback from the object. * \param[in] obj The object handle * \param[in] cb The event callback * \return 0 if success or -1 if failure */ int32_t UAVObjDisconnectCallback(UAVObjHandle obj, UAVObjEventCallback cb) { int32_t res; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); res = disconnectObj(obj, 0, cb); xSemaphoreGiveRecursive(mutex); return res; } /** * Request an update of the object's data from the GCS. The call will not wait for the response, a EV_UPDATED event * will be generated as soon as the object is updated. * \param[in] obj The object handle */ void UAVObjRequestUpdate(UAVObjHandle obj) { UAVObjRequestInstanceUpdate(obj, UAVOBJ_ALL_INSTANCES); } /** * Request an update of the object's data from the GCS. The call will not wait for the response, a EV_UPDATED event * will be generated as soon as the object is updated. * \param[in] obj The object handle * \param[in] instId Object instance ID to update */ void UAVObjRequestInstanceUpdate(UAVObjHandle obj, uint16_t instId) { xSemaphoreTakeRecursive(mutex, portMAX_DELAY); sendEvent((ObjectList *) obj, instId, EV_UPDATE_REQ); xSemaphoreGiveRecursive(mutex); } /** * Send the object's data to the GCS (triggers a EV_UPDATED_MANUAL event on this object). * \param[in] obj The object handle */ void UAVObjUpdated(UAVObjHandle obj) { UAVObjInstanceUpdated(obj, UAVOBJ_ALL_INSTANCES); } /** * Send the object's data to the GCS (triggers a EV_UPDATED_MANUAL event on this object). * \param[in] obj The object handle * \param[in] instId The object instance ID */ void UAVObjInstanceUpdated(UAVObjHandle obj, uint16_t instId) { xSemaphoreTakeRecursive(mutex, portMAX_DELAY); sendEvent((ObjectList *) obj, instId, EV_UPDATED_MANUAL); xSemaphoreGiveRecursive(mutex); } /** * Iterate through all objects in the list. * \param iterator This function will be called once for each object, * the object will be passed as a parameter */ void UAVObjIterate(void (*iterator) (UAVObjHandle obj)) { ObjectList *objEntry; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Iterate through the list and invoke iterator for each object LL_FOREACH(objList, objEntry) { (*iterator) ((UAVObjHandle) objEntry); } // Release lock xSemaphoreGiveRecursive(mutex); } /** * Send an event to all event queues registered on the object. */ static int32_t sendEvent(ObjectList * obj, uint16_t instId, UAVObjEventType event) { ObjectEventList *eventEntry; UAVObjEvent msg; // Setup event msg.obj = (UAVObjHandle) obj; msg.event = event; msg.instId = instId; // Go through each object and push the event message in the queue (if event is activated for the queue) LL_FOREACH(obj->events, eventEntry) { if (eventEntry->eventMask == 0 || (eventEntry->eventMask & event) != 0) { // Send to queue if a valid queue is registered if (eventEntry->queue != 0) { if (xQueueSend(eventEntry->queue, &msg, 0) != pdTRUE) // will not block { stats.lastQueueErrorID = UAVObjGetID(obj); ++stats.eventQueueErrors; } } // Invoke callback (from event task) if a valid one is registered if (eventEntry->cb != 0) { if (EventCallbackDispatch(&msg, eventEntry->cb) != pdTRUE) // invoke callback from the event task, will not block { ++stats.eventCallbackErrors; stats.lastCallbackErrorID = UAVObjGetID(obj); } } } } // Done return 0; } /** * Create a new object instance, return the instance info or NULL if failure. */ static ObjectInstList *createInstance(ObjectList * obj, uint16_t instId) { ObjectInstList *instEntry; int32_t n; // For single instance objects, only instance zero is allowed if (OLGetIsSingleInstance(obj) && instId != 0) { return NULL; } // Make sure that the instance ID is within limits if (instId >= UAVOBJ_MAX_INSTANCES) { return NULL; } // Check if the instance already exists if (getInstance(obj, instId) != NULL) { return NULL; } // Create any missing instances (all instance IDs must be sequential) for (n = obj->numInstances; n < instId; ++n) { if (createInstance(obj, n) == NULL) { return NULL; } } if (instId == 0) { /* Instance 0 ObjectInstList allocated with ObjectList element */ instEntry = &obj->instances; instEntry->data = pvPortMalloc(obj->numBytes); if (instEntry->data == NULL) return NULL; memset(instEntry->data, 0, obj->numBytes); instEntry->instId = instId; } else { // Create the actual instance instEntry = (ObjectInstList *) pvPortMalloc(sizeof(ObjectInstList)); if (instEntry == NULL) return NULL; instEntry->data = pvPortMalloc(obj->numBytes); if (instEntry->data == NULL) return NULL; memset(instEntry->data, 0, obj->numBytes); instEntry->instId = instId; LL_APPEND(obj->instances.next, instEntry); } ++obj->numInstances; // Fire event UAVObjInstanceUpdated((UAVObjHandle) obj, instId); // Done return instEntry; } /** * Get the instance information or NULL if the instance does not exist */ static ObjectInstList *getInstance(ObjectList * obj, uint16_t instId) { ObjectInstList *instEntry; // Look for specified instance ID LL_FOREACH(&(obj->instances), instEntry) { if (instEntry->instId == instId) { return instEntry; } } // If this point is reached then instance id was not found return NULL; } /** * Connect an event queue to the object, if the queue is already connected then the event mask is only updated. * \param[in] obj The object handle * \param[in] queue The event queue * \param[in] cb The event callback * \param[in] eventMask The event mask, if EV_MASK_ALL then all events are enabled (e.g. EV_UPDATED | EV_UPDATED_MANUAL) * \return 0 if success or -1 if failure */ static int32_t connectObj(UAVObjHandle obj, xQueueHandle queue, UAVObjEventCallback cb, uint8_t eventMask) { ObjectEventList *eventEntry; ObjectList *objEntry; // Check that the queue is not already connected, if it is simply update event mask objEntry = (ObjectList *) obj; LL_FOREACH(objEntry->events, eventEntry) { if (eventEntry->queue == queue && eventEntry->cb == cb) { // Already connected, update event mask and return eventEntry->eventMask = eventMask; return 0; } } // Add queue to list eventEntry = (ObjectEventList *) pvPortMalloc(sizeof(ObjectEventList)); if (eventEntry == NULL) { return -1; } eventEntry->queue = queue; eventEntry->cb = cb; eventEntry->eventMask = eventMask; LL_APPEND(objEntry->events, eventEntry); // Done return 0; } /** * Disconnect an event queue from the object * \param[in] obj The object handle * \param[in] queue The event queue * \param[in] cb The event callback * \return 0 if success or -1 if failure */ static int32_t disconnectObj(UAVObjHandle obj, xQueueHandle queue, UAVObjEventCallback cb) { ObjectEventList *eventEntry; ObjectList *objEntry; // Find queue and remove it objEntry = (ObjectList *) obj; LL_FOREACH(objEntry->events, eventEntry) { if ((eventEntry->queue == queue && eventEntry->cb == cb)) { LL_DELETE(objEntry->events, eventEntry); vPortFree(eventEntry); return 0; } } // If this point is reached the queue was not found return -1; } #if defined(PIOS_INCLUDE_SDCARD) /** * Wrapper for the sprintf function */ static void customSPrintf(uint8_t * buffer, uint8_t * format, ...) { va_list args; va_start(args, format); vsprintf((char *)buffer, (char *)format, args); } /** * Get an 8 character (plus extension) filename for the object. */ static void objectFilename(ObjectList * obj, uint8_t * filename) { customSPrintf(filename, (uint8_t *) "%X.obj", obj->id); } #endif /* PIOS_INCLUDE_SDCARD */