/** ****************************************************************************** * @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" #include "pios_struct_helper.h" #include "inc/uavobjectprivate.h" // Private functions static InstanceHandle createInstance(struct UAVOData *obj, uint16_t instId); static int32_t connectObj(UAVObjHandle obj_handle, xQueueHandle queue, UAVObjEventCallback cb, uint8_t eventMask, bool fast); static int32_t disconnectObj(UAVObjHandle obj_handle, xQueueHandle queue, UAVObjEventCallback cb); static void instanceAutoUpdated(UAVObjHandle obj_handle, uint16_t instId); int32_t UAVObjPers_stub(__attribute__((unused)) UAVObjHandle obj_handle, __attribute__((unused)) uint16_t instId) { return 0; } int32_t UAVObjSave(UAVObjHandle obj_handle, uint16_t instId) __attribute__((weak, alias("UAVObjPers_stub")));; int32_t UAVObjLoad(UAVObjHandle obj_handle, uint16_t instId) __attribute__((weak, alias("UAVObjPers_stub"))); int32_t UAVObjDelete(UAVObjHandle obj_handle, uint16_t instId) __attribute__((weak, alias("UAVObjPers_stub"))); // Private variables static xSemaphoreHandle mutex; static const UAVObjMetadata defMetadata = { .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), .telemetryUpdatePeriod = 0, .gcsTelemetryUpdatePeriod = 0, .loggingUpdatePeriod = 0, }; static UAVObjStats stats; static inline bool IsMetaobject(UAVObjHandle obj_handle) { /* Recover the common object header */ struct UAVOBase *uavo_base = (struct UAVOBase *)obj_handle; return uavo_base->flags.isMeta; } static inline bool IsSingleInstance(UAVObjHandle obj_handle) { /* Recover the common object header */ struct UAVOBase *uavo_base = (struct UAVOBase *)obj_handle; return uavo_base->flags.isSingle; } static inline bool IsSettings(UAVObjHandle obj_handle) { /* Recover the common object header */ struct UAVOBase *uavo_base = (struct UAVOBase *)obj_handle; return uavo_base->flags.isSettings; } static inline bool IsPriority(UAVObjHandle obj_handle) { /* Recover the common object header */ struct UAVOBase *uavo_base = (struct UAVOBase *)obj_handle; return uavo_base->flags.isPriority; } /** * Is this a metaobject? * \param[in] obj The object handle * \return True (1) if this is metaobject */ bool UAVObjIsMetaobject(UAVObjHandle obj_handle) { PIOS_Assert(obj_handle); return IsMetaobject(obj_handle); } /** * 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 */ bool UAVObjIsSingleInstance(UAVObjHandle obj_handle) { PIOS_Assert(obj_handle); return IsSingleInstance(obj_handle); } /** * Is this a settings object? * \param[in] obj The object handle * \return True (1) if this is a settings object */ bool UAVObjIsSettings(UAVObjHandle obj_handle) { PIOS_Assert(obj_handle); return IsSettings(obj_handle); } /** * Is this a prioritized object? * \param[in] obj The object handle * \return True (1) if this is a prioritized object */ bool UAVObjIsPriority(UAVObjHandle obj_handle) { return IsPriority(obj_handle); } /** * Initialize the object manager * \return 0 Success * \return -1 Failure */ int32_t UAVObjInitialize() { // Initialize variables memset(&stats, 0, sizeof(UAVObjStats)); /* Initialize _uavo_handles start/stop pointers */ #if (defined(__MACH__) && defined(__APPLE__)) uint64_t aslr_offset = (uint64_t)&_aslr_offset - getsectbyname("__DATA", "_aslr")->addr; __start__uavo_handles = (struct UAVOData * *)(getsectbyname("__DATA", "_uavo_handles")->addr + aslr_offset); __stop__uavo_handles = (struct UAVOData * *)((uint64_t)__start__uavo_handles + getsectbyname("__DATA", "_uavo_handles")->size); #endif // Initialize the uavo handle table memset(__start__uavo_handles, 0, (uintptr_t)__stop__uavo_handles - (uintptr_t)__start__uavo_handles); // Create mutex mutex = xSemaphoreCreateRecursiveMutex(); if (mutex == NULL) { return -1; } // Done return 0; } /***************** * Statistics ****************/ /** * 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); } /************************ * Object Initialization ***********************/ static void UAVObjInitMetaData(struct UAVOMeta *obj_meta) { /* Fill in the common part of the UAVO */ struct UAVOBase *uavo_base = &(obj_meta->base); memset(uavo_base, 0, sizeof(*uavo_base)); uavo_base->flags.isMeta = true; uavo_base->flags.isSingle = true; uavo_base->next_event = NULL; /* Clear the instance data carried in the UAVO */ memset(&(obj_meta->instance0), 0, sizeof(obj_meta->instance0)); } static struct UAVOData *UAVObjAllocSingle(uint32_t num_bytes) { /* Compute the complete size of the object, including the data for a single embedded instance */ uint32_t object_size = sizeof(struct UAVOSingle) + num_bytes; /* Allocate the object from the heap */ struct UAVOSingle *uavo_single = (struct UAVOSingle *)pios_malloc(object_size); if (!uavo_single) { return NULL; } /* Fill in the common part of the UAVO */ struct UAVOBase *uavo_base = &(uavo_single->uavo.base); memset(uavo_base, 0, sizeof(*uavo_base)); uavo_base->flags.isSingle = true; uavo_base->next_event = NULL; /* Clear the instance data carried in the UAVO */ memset(&(uavo_single->instance0), 0, num_bytes); /* Give back the generic UAVO part */ return &(uavo_single->uavo); } static struct UAVOData *UAVObjAllocMulti(uint32_t num_bytes) { /* Compute the complete size of the object, including the data for a single embedded instance */ uint32_t object_size = sizeof(struct UAVOMulti) + num_bytes; /* Allocate the object from the heap */ struct UAVOMulti *uavo_multi = (struct UAVOMulti *)pios_malloc(object_size); if (!uavo_multi) { return NULL; } /* Fill in the common part of the UAVO */ struct UAVOBase *uavo_base = &(uavo_multi->uavo.base); memset(uavo_base, 0, sizeof(*uavo_base)); uavo_base->flags.isSingle = false; uavo_base->next_event = NULL; /* Set up the type-specific part of the UAVO */ uavo_multi->num_instances = 1; /* Clear the multi instance data carried in the UAVO */ memset(&(uavo_multi->instance0), 0, sizeof(struct UAVOMultiInst) + num_bytes); /* Give back the generic UAVO part */ return &(uavo_multi->uavo); } /************************** * UAVObject Database APIs *************************/ /** * Register and new object in the object manager. * \param[in] id Unique object ID * \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, bool isSingleInstance, bool isSettings, bool isPriority, uint32_t num_bytes, UAVObjInitializeCallback initCb) { struct UAVOData *uavo_data = NULL; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); /* Don't allow duplicate registrations */ if (UAVObjGetByID(id)) { goto unlock_exit; } /* Map the various flags to one of the UAVO types we understand */ if (isSingleInstance) { uavo_data = UAVObjAllocSingle(num_bytes); } else { uavo_data = UAVObjAllocMulti(num_bytes); } if (!uavo_data) { goto unlock_exit; } /* Fill in the details about this UAVO */ uavo_data->id = id; uavo_data->instance_size = num_bytes; if (isSettings) { uavo_data->base.flags.isSettings = true; // settings defaults to being sent with priority uavo_data->base.flags.isPriority = true; } else { uavo_data->base.flags.isPriority = isPriority; } /* Initialize the embedded meta UAVO */ UAVObjInitMetaData(&uavo_data->metaObj); /* Initialize object fields and metadata to default values */ if (initCb) { initCb((UAVObjHandle)uavo_data, 0); } /* Always try to load the meta object from flash */ UAVObjLoad((UAVObjHandle) & (uavo_data->metaObj), 0); /* Attempt to load settings object from flash */ if (uavo_data->base.flags.isSettings) { UAVObjLoad((UAVObjHandle)uavo_data, 0); } // fire events for outer object and its embedded meta object instanceAutoUpdated((UAVObjHandle)uavo_data, 0); instanceAutoUpdated((UAVObjHandle) & (uavo_data->metaObj), 0); unlock_exit: xSemaphoreGiveRecursive(mutex); return (UAVObjHandle)uavo_data; } /** * 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) { UAVObjHandle *found_obj = (UAVObjHandle *)NULL; // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Look for object UAVO_LIST_ITERATE(tmp_obj) if (tmp_obj->id == id) { found_obj = (UAVObjHandle *)tmp_obj; goto unlock_exit; } if (MetaObjectId(tmp_obj->id) == id) { found_obj = (UAVObjHandle *)&(tmp_obj->metaObj); goto unlock_exit; } } unlock_exit: xSemaphoreGiveRecursive(mutex); return found_obj; } /** * Get the object's ID * \param[in] obj The object handle * \return The object ID */ uint32_t UAVObjGetID(UAVObjHandle obj_handle) { PIOS_Assert(obj_handle); /* Recover the common object header */ struct UAVOBase *uavo_base = (struct UAVOBase *)obj_handle; if (IsMetaobject(obj_handle)) { /* We have a meta object, find our containing UAVO */ struct UAVOData *uavo_data = container_of((struct UAVOMeta *)uavo_base, struct UAVOData, metaObj); return MetaObjectId(uavo_data->id); } else { /* We have a data object, augment our pointer */ struct UAVOData *uavo_data = (struct UAVOData *)uavo_base; return uavo_data->id; } } /** * 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) { PIOS_Assert(obj); uint32_t instance_size; /* Recover the common object header */ struct UAVOBase *uavo_base = (struct UAVOBase *)obj; if (uavo_base->flags.isMeta) { instance_size = MetaNumBytes; } else { /* We have a data object, augment our pointer */ struct UAVOData *uavo = (struct UAVOData *)uavo_base; instance_size = uavo->instance_size; } return instance_size; } /** * 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_handle) { PIOS_Assert(obj_handle); /* Recover the common object header */ struct UAVOBase *uavo_base = (struct UAVOBase *)obj_handle; if (IsMetaobject(obj_handle)) { /* We have a meta object, find our containing UAVO. */ struct UAVOData *uavo_data = container_of((struct UAVOMeta *)uavo_base, struct UAVOData, metaObj); return (UAVObjHandle)uavo_data; } else { /* We have a data object, augment our pointer */ struct UAVOData *uavo_data = (struct UAVOData *)uavo_base; return (UAVObjHandle) & (uavo_data->metaObj); } } /** * 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_handle) { PIOS_Assert(obj_handle); if (IsSingleInstance(obj_handle)) { /* Only one instance is allowed */ return 1; } else { /* Multi-instance object. Inspect the object */ /* Augment our pointer to reflect the proper type */ struct UAVOMulti *uavo_multi = (struct UAVOMulti *)obj_handle; return uavo_multi->num_instances; } } /** * 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_handle, UAVObjInitializeCallback initCb) { PIOS_Assert(obj_handle); if (IsMetaobject(obj_handle)) { return 0; } // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); InstanceHandle instEntry; uint16_t instId = 0; // Create new instance instId = UAVObjGetNumInstances(obj_handle); instEntry = createInstance((struct UAVOData *)obj_handle, instId); if (instEntry == NULL) { goto unlock_exit; } // Initialize instance data if (initCb) { initCb(obj_handle, instId); } unlock_exit: xSemaphoreGiveRecursive(mutex); return instId; } /** * 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_handle, uint16_t instId, const uint8_t *dataIn) { PIOS_Assert(obj_handle); // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; if (IsMetaobject(obj_handle)) { if (instId != 0) { goto unlock_exit; } memcpy(MetaDataPtr((struct UAVOMeta *)obj_handle), dataIn, MetaNumBytes); } else { struct UAVOData *obj; InstanceHandle instEntry; // Cast handle to object obj = (struct UAVOData *)obj_handle; // Get the instance instEntry = getInstance(obj, instId); // If the instance does not exist create it and any other instances before it if (instEntry == NULL) { instEntry = createInstance(obj, instId); if (instEntry == NULL) { goto unlock_exit; } } // Set the data memcpy(InstanceData(instEntry), dataIn, obj->instance_size); } // Fire event sendEvent((struct UAVOBase *)obj_handle, instId, EV_UNPACKED); rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * 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_handle, uint16_t instId, uint8_t *dataOut) { PIOS_Assert(obj_handle); // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; if (IsMetaobject(obj_handle)) { if (instId != 0) { goto unlock_exit; } memcpy(dataOut, MetaDataPtr((struct UAVOMeta *)obj_handle), MetaNumBytes); } else { struct UAVOData *obj; InstanceHandle instEntry; // Cast handle to object obj = (struct UAVOData *)obj_handle; // Get the instance instEntry = getInstance(obj, instId); if (instEntry == NULL) { goto unlock_exit; } // Pack data memcpy(dataOut, InstanceData(instEntry), obj->instance_size); } rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * Update a CRC with an object data * \param[in] obj The object handle * \param[in] instId The instance ID * \param[in] crc The crc to update * \return the updated crc */ uint8_t UAVObjUpdateCRC(UAVObjHandle obj_handle, uint16_t instId, uint8_t crc) { PIOS_Assert(obj_handle); // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); if (IsMetaobject(obj_handle)) { if (instId != 0) { goto unlock_exit; } // TODO } else { struct UAVOData *obj; InstanceHandle instEntry; // Cast handle to object obj = (struct UAVOData *)obj_handle; // Get the instance instEntry = getInstance(obj, instId); if (instEntry == NULL) { goto unlock_exit; } // Update crc crc = PIOS_CRC_updateCRC(crc, (uint8_t *)InstanceData(instEntry), (int32_t)obj->instance_size); } unlock_exit: xSemaphoreGiveRecursive(mutex); return crc; } /** * Actually write the object's data to the logfile * \param[in] obj The object handle * \param[in] instId The object instance ID */ #ifdef PIOS_INCLUDE_DEBUGLOG void UAVObjInstanceWriteToLog(UAVObjHandle obj_handle, uint16_t instId) { PIOS_Assert(obj_handle); // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); if (IsMetaobject(obj_handle)) { if (instId != 0) { goto unlock_exit; } PIOS_DEBUGLOG_UAVObject(UAVObjGetID(obj_handle), instId, MetaNumBytes, (uint8_t *)MetaDataPtr((struct UAVOMeta *)obj_handle)); } else { struct UAVOData *obj; InstanceHandle instEntry; // Cast handle to object obj = (struct UAVOData *)obj_handle; // Get the instance instEntry = getInstance(obj, instId); if (instEntry == NULL) { goto unlock_exit; } // Pack data PIOS_DEBUGLOG_UAVObject(UAVObjGetID(obj_handle), instId, obj->instance_size, (uint8_t *)InstanceData(instEntry)); } unlock_exit: xSemaphoreGiveRecursive(mutex); } #else /* ifdef PIOS_INCLUDE_DEBUGLOG */ void UAVObjInstanceWriteToLog(__attribute__((unused)) UAVObjHandle obj_handle, __attribute__((unused)) uint16_t instId) {} #endif /* ifdef PIOS_INCLUDE_DEBUGLOG */ /** * Save all settings objects to the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjSaveSettings() { // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; // Save all settings objects UAVO_LIST_ITERATE(obj) // Check if this is a settings object if (IsSettings(obj)) { // Save object if (UAVObjSave((UAVObjHandle)obj, 0) == -1) { goto unlock_exit; } } } rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * Load all settings objects from the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjLoadSettings() { // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; // Load all settings objects UAVO_LIST_ITERATE(obj) // Check if this is a settings object if (IsSettings(obj)) { // Load object if (UAVObjLoad((UAVObjHandle)obj, 0) == -1) { goto unlock_exit; } } } rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * Delete all settings objects from the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjDeleteSettings() { // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; // Save all settings objects UAVO_LIST_ITERATE(obj) // Check if this is a settings object if (IsSettings(obj)) { // Save object if (UAVObjDelete((UAVObjHandle)obj, 0) == -1) { goto unlock_exit; } } } rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * Save all metaobjects to the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjSaveMetaobjects() { // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; // Save all settings objects UAVO_LIST_ITERATE(obj) // Save object if (UAVObjSave((UAVObjHandle)MetaObjectPtr(obj), 0) == -1) { goto unlock_exit; } } rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * Load all metaobjects from the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjLoadMetaobjects() { // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; // Load all settings objects UAVO_LIST_ITERATE(obj) // Load object if (UAVObjLoad((UAVObjHandle)MetaObjectPtr(obj), 0) == -1) { goto unlock_exit; } } rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * Delete all metaobjects from the SD card. * @return 0 if success or -1 if failure */ int32_t UAVObjDeleteMetaobjects() { // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; // Load all settings objects UAVO_LIST_ITERATE(obj) // Load object if (UAVObjDelete((UAVObjHandle)MetaObjectPtr(obj), 0) == -1) { goto unlock_exit; } } rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * 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_handle, const void *dataIn) { return UAVObjSetInstanceData(obj_handle, 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_handle, const void *dataIn, uint32_t offset, uint32_t size) { return UAVObjSetInstanceDataField(obj_handle, 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_handle, void *dataOut) { return UAVObjGetInstanceData(obj_handle, 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_handle, void *dataOut, uint32_t offset, uint32_t size) { return UAVObjGetInstanceDataField(obj_handle, 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_handle, uint16_t instId, const void *dataIn) { PIOS_Assert(obj_handle); // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; if (IsMetaobject(obj_handle)) { if (instId != 0) { goto unlock_exit; } memcpy(MetaDataPtr((struct UAVOMeta *)obj_handle), dataIn, MetaNumBytes); } else { struct UAVOData *obj; InstanceHandle instEntry; // Cast to object info obj = (struct UAVOData *)obj_handle; // Check access level if (UAVObjReadOnly(obj_handle)) { goto unlock_exit; } // Get instance information instEntry = getInstance(obj, instId); if (instEntry == NULL) { goto unlock_exit; } // Set data memcpy(InstanceData(instEntry), dataIn, obj->instance_size); } // Fire event sendEvent((struct UAVOBase *)obj_handle, instId, EV_UPDATED); rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * 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_handle, uint16_t instId, const void *dataIn, uint32_t offset, uint32_t size) { PIOS_Assert(obj_handle); // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; if (IsMetaobject(obj_handle)) { // Get instance information if (instId != 0) { goto unlock_exit; } // Check for overrun if ((size + offset) > MetaNumBytes) { goto unlock_exit; } // Set data memcpy(MetaDataPtr((struct UAVOMeta *)obj_handle) + offset, dataIn, size); } else { struct UAVOData *obj; InstanceHandle instEntry; // Cast to object info obj = (struct UAVOData *)obj_handle; // Check access level if (UAVObjReadOnly(obj_handle)) { goto unlock_exit; } // Get instance information instEntry = getInstance(obj, instId); if (instEntry == NULL) { goto unlock_exit; } // Check for overrun if ((size + offset) > obj->instance_size) { goto unlock_exit; } // Set data memcpy(InstanceData(instEntry) + offset, dataIn, size); } // Fire event sendEvent((struct UAVOBase *)obj_handle, instId, EV_UPDATED); rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * 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_handle, uint16_t instId, void *dataOut) { PIOS_Assert(obj_handle); // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; if (IsMetaobject(obj_handle)) { // Get instance information if (instId != 0) { goto unlock_exit; } // Set data memcpy(dataOut, MetaDataPtr((struct UAVOMeta *)obj_handle), MetaNumBytes); } else { struct UAVOData *obj; InstanceHandle instEntry; // Cast to object info obj = (struct UAVOData *)obj_handle; // Get instance information instEntry = getInstance(obj, instId); if (instEntry == NULL) { goto unlock_exit; } // Set data memcpy(dataOut, InstanceData(instEntry), obj->instance_size); } rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * 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_handle, uint16_t instId, void *dataOut, uint32_t offset, uint32_t size) { PIOS_Assert(obj_handle); // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); int32_t rc = -1; if (IsMetaobject(obj_handle)) { // Get instance information if (instId != 0) { goto unlock_exit; } // Check for overrun if ((size + offset) > MetaNumBytes) { goto unlock_exit; } // Set data memcpy(dataOut, MetaDataPtr((struct UAVOMeta *)obj_handle) + offset, size); } else { struct UAVOData *obj; InstanceHandle instEntry; // Cast to object info obj = (struct UAVOData *)obj_handle; // Get instance information instEntry = getInstance(obj, instId); if (instEntry == NULL) { goto unlock_exit; } // Check for overrun if ((size + offset) > obj->instance_size) { goto unlock_exit; } // Set data memcpy(dataOut, InstanceData(instEntry) + offset, size); } rc = 0; unlock_exit: xSemaphoreGiveRecursive(mutex); return rc; } /** * 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_handle, const UAVObjMetadata *dataIn) { PIOS_Assert(obj_handle); // Set metadata (metadata of metaobjects can not be modified) if (IsMetaobject(obj_handle)) { return -1; } xSemaphoreTakeRecursive(mutex, portMAX_DELAY); UAVObjSetData((UAVObjHandle)MetaObjectPtr((struct UAVOData *)obj_handle), dataIn); 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_handle, UAVObjMetadata *dataOut) { PIOS_Assert(obj_handle); // Lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Get metadata if (IsMetaobject(obj_handle)) { memcpy(dataOut, &defMetadata, sizeof(UAVObjMetadata)); } else { UAVObjGetData((UAVObjHandle)MetaObjectPtr((struct UAVOData *)obj_handle), dataOut); } // Unlock xSemaphoreGiveRecursive(mutex); return 0; } /******************************* * Object Metadata Manipulation ******************************/ /** * Get the UAVObject metadata access member * \param[in] metadata The metadata object * \return the access type */ UAVObjAccessType UAVObjGetAccess(const UAVObjMetadata *metadata) { PIOS_Assert(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) { PIOS_Assert(metadata); 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) { PIOS_Assert(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) { PIOS_Assert(metadata); 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) { PIOS_Assert(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) { PIOS_Assert(metadata); 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) { PIOS_Assert(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) { PIOS_Assert(metadata); 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) { PIOS_Assert(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) { PIOS_Assert(metadata); 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) { PIOS_Assert(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) { PIOS_Assert(metadata); SET_BITS(metadata->flags, UAVOBJ_GCS_TELEMETRY_UPDATE_MODE_SHIFT, val, UAVOBJ_UPDATE_MODE_MASK); } /** * Get the UAVObject metadata logging update mode * \param[in] metadata The metadata object * \return the GCS telemetry update mode */ UAVObjUpdateMode UAVObjGetLoggingUpdateMode(const UAVObjMetadata *metadata) { PIOS_Assert(metadata); return (metadata->flags >> UAVOBJ_LOGGING_UPDATE_MODE_SHIFT) & UAVOBJ_UPDATE_MODE_MASK; } /** * Set the UAVObject metadata logging update mode member * \param[in] metadata The metadata object * \param[in] val The GCS telemetry update mode */ void UAVObjSetLoggingUpdateMode(UAVObjMetadata *metadata, UAVObjUpdateMode val) { PIOS_Assert(metadata); SET_BITS(metadata->flags, UAVOBJ_LOGGING_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_handle) { PIOS_Assert(obj_handle); if (!IsMetaobject(obj_handle)) { return UAVObjGetAccess(LinkedMetaDataPtr((struct UAVOData *)obj_handle)) == 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_handle, xQueueHandle queue, uint8_t eventMask) { PIOS_Assert(obj_handle); PIOS_Assert(queue); int32_t res; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); res = connectObj(obj_handle, queue, 0, eventMask, false); 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_handle, xQueueHandle queue) { PIOS_Assert(obj_handle); PIOS_Assert(queue); int32_t res; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); res = disconnectObj(obj_handle, 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_handle, UAVObjEventCallback cb, uint8_t eventMask, bool fast) { PIOS_Assert(obj_handle); int32_t res; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); res = connectObj(obj_handle, 0, cb, eventMask, fast); 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_handle, UAVObjEventCallback cb) { PIOS_Assert(obj_handle); int32_t res; xSemaphoreTakeRecursive(mutex, portMAX_DELAY); res = disconnectObj(obj_handle, 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_handle) { UAVObjRequestInstanceUpdate(obj_handle, 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_handle, uint16_t instId) { PIOS_Assert(obj_handle); xSemaphoreTakeRecursive(mutex, portMAX_DELAY); sendEvent((struct UAVOBase *)obj_handle, instId, EV_UPDATE_REQ); xSemaphoreGiveRecursive(mutex); } /** * Trigger a EV_UPDATED_MANUAL event for an object. * \param[in] obj The object handle */ void UAVObjUpdated(UAVObjHandle obj_handle) { UAVObjInstanceUpdated(obj_handle, UAVOBJ_ALL_INSTANCES); } /** * Trigger a EV_UPDATED_MANUAL event for an object instance. * \param[in] obj The object handle * \param[in] instId The object instance ID */ void UAVObjInstanceUpdated(UAVObjHandle obj_handle, uint16_t instId) { PIOS_Assert(obj_handle); xSemaphoreTakeRecursive(mutex, portMAX_DELAY); sendEvent((struct UAVOBase *)obj_handle, instId, EV_UPDATED_MANUAL); xSemaphoreGiveRecursive(mutex); } /** * Trigger a EV_UPDATED event for an object instance. * \param[in] obj The object handle * \param[in] instId The object instance ID */ static void instanceAutoUpdated(UAVObjHandle obj_handle, uint16_t instId) { PIOS_Assert(obj_handle); xSemaphoreTakeRecursive(mutex, portMAX_DELAY); sendEvent((struct UAVOBase *)obj_handle, instId, EV_UPDATED); xSemaphoreGiveRecursive(mutex); } /* * Log the object's data (triggers a EV_LOGGING_MANUAL event on this object). * \param[in] obj The object handle */ void UAVObjLogging(UAVObjHandle obj_handle) { UAVObjInstanceLogging(obj_handle, UAVOBJ_ALL_INSTANCES); } /** * Log the object's data (triggers a EV_LOGGING_MANUAL event on this object). * \param[in] obj The object handle * \param[in] instId The object instance ID */ void UAVObjInstanceLogging(UAVObjHandle obj_handle, uint16_t instId) { PIOS_Assert(obj_handle); xSemaphoreTakeRecursive(mutex, portMAX_DELAY); sendEvent((struct UAVOBase *)obj_handle, instId, EV_LOGGING_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)) { PIOS_Assert(iterator); // Get lock xSemaphoreTakeRecursive(mutex, portMAX_DELAY); // Iterate through the list and invoke iterator for each object UAVO_LIST_ITERATE (obj) (*iterator)((UAVObjHandle)obj); (*iterator)((UAVObjHandle) &obj->metaObj); } // Release lock xSemaphoreGiveRecursive(mutex); } /** * Send a triggered event to all event queues registered on the object. */ int32_t sendEvent(struct UAVOBase *obj, uint16_t instId, UAVObjEventType triggered_event) { /* Set up the message that will be sent to all registered listeners */ UAVObjEvent msg = { .obj = (UAVObjHandle)obj, .event = triggered_event, .instId = instId, .lowPriority = false, }; // Go through each object and push the event message in the queue (if event is activated for the queue) struct ObjectEventEntry *event; LL_FOREACH(obj->next_event, event) { if (event->eventMask == 0 || (event->eventMask & triggered_event) != 0) { // Send to queue if a valid queue is registered if (event->queue) { // will not block if (xQueueSend(event->queue, &msg, 0) != pdTRUE) { ++stats.eventQueueErrors; stats.lastQueueErrorID = UAVObjGetID(obj); } } // Invoke callback (from event task) if a valid one is registered if (event->cb) { if (event->fast) { event->cb(&msg); } else if (EventCallbackDispatch(&msg, event->cb) != pdTRUE) { // invoke callback from the event task, will not block ++stats.eventCallbackErrors; stats.lastCallbackErrorID = UAVObjGetID(obj); } } } } return 0; } /** * Create a new object instance, return the instance info or NULL if failure. */ static InstanceHandle createInstance(struct UAVOData *obj, uint16_t instId) { struct UAVOMultiInst *instEntry; /* Don't allow more than one instance for single instance objects */ if (IsSingleInstance(&(obj->base))) { PIOS_Assert(0); return NULL; } /* Don't create more than the allowed number of instances */ if (instId >= UAVOBJ_MAX_INSTANCES) { return NULL; } /* Don't allow duplicate instances */ if (instId < UAVObjGetNumInstances(&(obj->base))) { return NULL; } // Create any missing instances (all instance IDs must be sequential) for (uint16_t n = UAVObjGetNumInstances(&(obj->base)); n < instId; ++n) { if (createInstance(obj, n) == NULL) { return NULL; } } /* Create the actual instance */ uint32_t size = sizeof(struct UAVOMultiInst) + obj->instance_size; instEntry = (struct UAVOMultiInst *)pios_malloc(size); if (!instEntry) { return NULL; } memset(instEntry, 0, size); LL_APPEND(((struct UAVOMulti *)obj)->instance0.next, instEntry); ((struct UAVOMulti *)obj)->num_instances++; // Fire event instanceAutoUpdated((UAVObjHandle)obj, instId); // Done return InstanceDataOffset(instEntry); } /** * Get the instance information or NULL if the instance does not exist */ InstanceHandle getInstance(struct UAVOData *obj, uint16_t instId) { if (IsMetaobject(&obj->base)) { /* Metadata Instance */ if (instId != 0) { return NULL; } /* Augment our pointer to reflect the proper type */ struct UAVOMeta *uavo_meta = (struct UAVOMeta *)obj; return &(uavo_meta->instance0); } else if (IsSingleInstance(&(obj->base))) { /* Single Instance */ if (instId != 0) { return NULL; } /* Augment our pointer to reflect the proper type */ struct UAVOSingle *uavo_single = (struct UAVOSingle *)obj; return &(uavo_single->instance0); } else { /* Multi Instance */ /* Augment our pointer to reflect the proper type */ struct UAVOMulti *uavo_multi = (struct UAVOMulti *)obj; if (instId >= uavo_multi->num_instances) { return NULL; } // Look for specified instance ID uint16_t instance = 0; struct UAVOMultiInst *instEntry; LL_FOREACH(&(uavo_multi->instance0), instEntry) { if (instance++ == instId) { /* Found it */ return &(instEntry->instance); } } /* Instance 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_handle, xQueueHandle queue, UAVObjEventCallback cb, uint8_t eventMask, bool fast) { struct ObjectEventEntry *event; struct UAVOBase *obj; // Check that the queue is not already connected, if it is simply update event mask obj = (struct UAVOBase *)obj_handle; LL_FOREACH(obj->next_event, event) { if (event->queue == queue && event->cb == cb) { // Already connected, update event mask and return event->eventMask = eventMask; event->fast = fast; return 0; } } // Add queue to list event = (struct ObjectEventEntry *)pios_malloc(sizeof(struct ObjectEventEntry)); if (event == NULL) { return -1; } event->queue = queue; event->cb = cb; event->eventMask = eventMask; event->fast = fast; LL_APPEND(obj->next_event, event); // 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_handle, xQueueHandle queue, UAVObjEventCallback cb) { struct ObjectEventEntry *event; struct UAVOBase *obj; // Find queue and remove it obj = (struct UAVOBase *)obj_handle; LL_FOREACH(obj->next_event, event) { if ((event->queue == queue && event->cb == cb)) { LL_DELETE(obj->next_event, event); vPortFree(event); return 0; } } // If this point is reached the queue was not found return -1; }