/**
 ******************************************************************************
 * @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"

extern uintptr_t pios_uavo_settings_fs_id;

#if (defined(__MACH__) && defined(__APPLE__))
#include <mach-o/getsect.h>
#endif

// Constants

// Private types

// Macros
#define SET_BITS(var, shift, value, mask) var = (var & ~(mask << shift)) | (value << shift);

// Mach-o: dummy segment to calculate ASLR offset in sim_osx
#if (defined(__MACH__) && defined(__APPLE__))
static long _aslr_offset __attribute__((section("__DATA,_aslr")));
#endif

/* Table of UAVO handles */
#if (defined(__MACH__) && defined(__APPLE__))
/* Mach-o format */
static struct UAVOData * *__start__uavo_handles;
static struct UAVOData * *__stop__uavo_handles;
#else
/* ELF format: automagically defined at compile time */
extern struct UAVOData *__start__uavo_handles[] __attribute__((weak));
extern struct UAVOData *__stop__uavo_handles[] __attribute__((weak));
#endif

#define UAVO_LIST_ITERATE(_item) \
    for (struct UAVOData * *_uavo_slot = __start__uavo_handles; \
         _uavo_slot && _uavo_slot < __stop__uavo_handles; \
         _uavo_slot++) { \
        struct UAVOData *_item = *_uavo_slot; \
        if (_item == NULL) { continue; }

/**
 * List of event queues and the eventmask associated with the queue.
 */

/** opaque type for instances **/
typedef void *InstanceHandle;

struct ObjectEventEntry {
    struct ObjectEventEntry *next;
    xQueueHandle queue;
    UAVObjEventCallback     cb;
    uint8_t eventMask;
};

/*
   MetaInstance   == [UAVOBase [UAVObjMetadata]]
   SingleInstance == [UAVOBase [UAVOData [InstanceData]]]
   MultiInstance  == [UAVOBase [UAVOData [NumInstances [InstanceData0 [next]]]]
                                                  ____________________/
   \-->[InstanceData1 [next]]
                                                  _________...________/
   \-->[InstanceDataN [next]]
 */

/*
 * UAVO Base Type
 *   - All Types of UAVObjects are of this base type
 *   - The flags determine what type(s) this object
 */
struct UAVOBase {
    /* Let these objects be added to an event queue */
    struct ObjectEventEntry *next_event;

    /* Describe the type of object that follows this header */
    struct UAVOInfo {
        bool isMeta        : 1;
        bool isSingle      : 1;
        bool isSettings    : 1;
    } flags;
} __attribute__((packed));

/* Augmented type for Meta UAVO */
struct UAVOMeta {
    struct UAVOBase base;
    UAVObjMetadata  instance0;
} __attribute__((packed));

/* Shared data structure for all data-carrying UAVObjects (UAVOSingle and UAVOMulti) */
struct UAVOData {
    struct UAVOBase base;
    uint32_t id;
    /*
     * Embed the Meta object as another complete UAVO
     * inside the payload for this UAVO.
     */
    struct UAVOMeta metaObj;
    uint16_t instance_size;
} __attribute__((packed, aligned(4)));

/* Augmented type for Single Instance Data UAVO */
struct UAVOSingle {
    struct UAVOData uavo;

    uint8_t instance0[];
    /*
     * Additional space will be malloc'd here to hold the
     * the data for this instance.
     */
} __attribute__((packed));

/* Part of a linked list of instances chained off of a multi instance UAVO. */
struct UAVOMultiInst {
    struct UAVOMultiInst *next;
    uint8_t instance[];
    /*
     * Additional space will be malloc'd here to hold the
     * the data for this instance.
     */
} __attribute__((packed));

/* Augmented type for Multi Instance Data UAVO */
struct UAVOMulti {
    struct UAVOData uavo;
    uint16_t num_instances;
    struct UAVOMultiInst instance0 __attribute__((aligned(4)));
    /*
     * Additional space will be malloc'd here to hold the
     * the data for instance 0.
     */
} __attribute__((packed));

/** all information about a metaobject are hardcoded constants **/
#define MetaNumBytes sizeof(UAVObjMetadata)
#define MetaBaseObjectPtr(obj)           ((struct UAVOData *)((obj) - offsetof(struct UAVOData, metaObj)))
#define MetaObjectPtr(obj)               ((struct UAVODataMeta *)&((obj)->metaObj))
#define MetaDataPtr(obj)                 ((UAVObjMetadata *)&((obj)->instance0))
#define LinkedMetaDataPtr(obj)           ((UAVObjMetadata *)&((obj)->metaObj.instance0))
#define MetaObjectId(id)                 ((id) + 1)

/** all information about instances are dependant on object type **/
#define ObjSingleInstanceDataOffset(obj) ((void *)(&(((struct UAVOSingle *)obj)->instance0)))
#define InstanceDataOffset(inst)         ((void *)&(((struct UAVOMultiInst *)inst)->instance))
#define InstanceData(instance)           (void *)instance

// Private functions
static int32_t sendEvent(struct UAVOBase *obj, uint16_t instId,
                         UAVObjEventType event);
static InstanceHandle createInstance(struct UAVOData *obj, uint16_t instId);
static InstanceHandle getInstance(struct UAVOData *obj, uint16_t instId);
static int32_t connectObj(UAVObjHandle obj_handle, xQueueHandle queue,
                          UAVObjEventCallback cb, uint8_t eventMask);
static int32_t disconnectObj(UAVObjHandle obj_handle, xQueueHandle queue,
                             UAVObjEventCallback cb);

#if defined(PIOS_USE_SETTINGS_ON_SDCARD) && defined(PIOS_INCLUDE_FLASH_LOGFS_SETTINGS)
#error Both PIOS_USE_SETTINGS_ON_SDCARD and PIOS_INCLUDE_FLASH_LOGFS_SETTINGS. Only one settings storage allowed.
#endif

#if defined(PIOS_USE_SETTINGS_ON_SDCARD)
static void objectFilename(UAVObjHandle obj_handle, uint8_t *filename);
static void customSPrintf(uint8_t *buffer, uint8_t *format, ...);
#endif

// 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;

/**
 * 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 *)pvPortMalloc(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 *)pvPortMalloc(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,
                            int32_t isSingleInstance, int32_t isSettings,
                            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;
    }

    /* 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
    UAVObjInstanceUpdated((UAVObjHandle)uavo_data, 0);
    UAVObjInstanceUpdated((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 (UAVObjIsMetaobject(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 (UAVObjIsMetaobject(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 (UAVObjIsSingleInstance(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 (UAVObjIsMetaobject(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;
}

/**
 * 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);

    /* Recover the common object header */
    struct UAVOBase *uavo_base = (struct UAVOBase *)obj_handle;

    return uavo_base->flags.isSingle;
}

/**
 * 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);

    /* Recover the common object header */
    struct UAVOBase *uavo_base = (struct UAVOBase *)obj_handle;

    return uavo_base->flags.isMeta;
}

/**
 * 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);

    /* Recover the common object header */
    struct UAVOBase *uavo_base = (struct UAVOBase *)obj_handle;

    return uavo_base->flags.isSettings;
}

/**
 * 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 (UAVObjIsMetaobject(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 (UAVObjIsMetaobject(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;
}

#if defined(PIOS_USE_SETTINGS_ON_SDCARD)
/**
 * 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_handle, uint16_t instId,
                         FILEINFO *file)
{
    PIOS_Assert(obj_handle);

    uint32_t bytesWritten;
    // Check for file system availability
    if (PIOS_SDCARD_IsMounted() == 0) {
        return -1;
    }
    // Lock
    xSemaphoreTakeRecursive(mutex, portMAX_DELAY);

    if (UAVObjIsMetaobject(obj_handle)) {
        // Get the instance information
        if (instId != 0) {
            xSemaphoreGiveRecursive(mutex);
            return -1;
        }
        // Write the object ID
        uint32_t objId = UAVObjGetID(obj_handle);
        PIOS_FWRITE(file, &objId, sizeof(objId),
                    &bytesWritten);

        // Write the data and check that the write was successful
        PIOS_FWRITE(file, MetaDataPtr((struct UAVOMeta *)obj_handle), MetaNumBytes,
                    &bytesWritten);
        if (bytesWritten != MetaNumBytes) {
            xSemaphoreGiveRecursive(mutex);
            return -1;
        }
    } else {
        struct UAVOData *uavo;
        InstanceHandle instEntry;

        // Cast to object
        uavo = (struct UAVOData *)obj_handle;

        // Get the instance information
        instEntry = getInstance(uavo, instId);
        if (instEntry == NULL) {
            xSemaphoreGiveRecursive(mutex);
            return -1;
        }
        // Write the object ID
        PIOS_FWRITE(file, &uavo->id, sizeof(uavo->id),
                    &bytesWritten);

        // Write the instance ID
        if (!UAVObjIsSingleInstance(obj_handle)) {
            PIOS_FWRITE(file, &instId,
                        sizeof(instId), &bytesWritten);
        }
        // Write the data and check that the write was successful
        PIOS_FWRITE(file, InstanceData(instEntry), uavo->instance_size,
                    &bytesWritten);
        if (bytesWritten != uavo->instance_size) {
            xSemaphoreGiveRecursive(mutex);
            return -1;
        }
    }
    // Done
    xSemaphoreGiveRecursive(mutex);
    return 0;
}
#endif /* PIOS_USE_SETTINGS_ON_SDCARD */

/**
 * 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_handle, __attribute__((unused)) uint16_t instId)
{
    PIOS_Assert(obj_handle);

#if defined(PIOS_INCLUDE_FLASH_LOGFS_SETTINGS)
    if (UAVObjIsMetaobject(obj_handle)) {
        if (instId != 0) {
            return -1;
        }

        if (PIOS_FLASHFS_ObjSave(pios_uavo_settings_fs_id, UAVObjGetID(obj_handle), instId, (uint8_t *)MetaDataPtr((struct UAVOMeta *)obj_handle), UAVObjGetNumBytes(obj_handle)) != 0) {
            return -1;
        }
    } else {
        InstanceHandle instEntry = getInstance((struct UAVOData *)obj_handle, instId);

        if (instEntry == NULL) {
            return -1;
        }

        if (InstanceData(instEntry) == NULL) {
            return -1;
        }

        if (PIOS_FLASHFS_ObjSave(pios_uavo_settings_fs_id, UAVObjGetID(obj_handle), instId, InstanceData(instEntry), UAVObjGetNumBytes(obj_handle)) != 0) {
            return -1;
        }
    }
#endif /* if defined(PIOS_INCLUDE_FLASH_LOGFS_SETTINGS) */
#if defined(PIOS_USE_SETTINGS_ON_SDCARD)
    FILEINFO file;
    uint8_t filename[14];

    // Check for file system availability
    if (PIOS_SDCARD_IsMounted() == 0) {
        return -1;
    }
    // Lock
    xSemaphoreTakeRecursive(mutex, portMAX_DELAY);

    // Get filename
    objectFilename(obj_handle, filename);

    // Open file
    if (PIOS_FOPEN_WRITE(filename, file)) {
        xSemaphoreGiveRecursive(mutex);
        return -1;
    }
    // Append object
    if (UAVObjSaveToFile(obj_handle, instId, &file) == -1) {
        PIOS_FCLOSE(file);
        xSemaphoreGiveRecursive(mutex);
        return -1;
    }
    // Done, close file and unlock
    PIOS_FCLOSE(file);
    xSemaphoreGiveRecursive(mutex);
#endif /* PIOS_USE_SETTINGS_ON_SDCARD */
    return 0;
}

#if defined(PIOS_USE_SETTINGS_ON_SDCARD)
/**
 * Load an object from the file system (SD card).
 * @param[in] obj The object handle.
 * @param[in] file File to read from
 * @return 0 if success or -1 if failure
 */
int32_t UAVObjLoadFromFile(UAVObjHandle obj_handle, FILEINFO *file)
{
    uint32_t bytesRead;
    struct UAVOBase *objEntry;
    InstanceHandle instEntry;
    uint32_t objId;
    uint16_t instId;

    // Check for file system availability
    if (PIOS_SDCARD_IsMounted() == 0) {
        return -1;
    }
    // Get the object
    if (obj_handle == 0) {
        return -1;
    }
    objEntry = (struct UAVOBase *)obj_handle;

    // Lock
    xSemaphoreTakeRecursive(mutex, portMAX_DELAY);

    // Read the object ID
    if (PIOS_FREAD(file, &objId, sizeof(objId), &bytesRead)) {
        xSemaphoreGiveRecursive(mutex);
        return -1;
    }

    // Check that the IDs match
    if (objId != UAVObjGetID(obj_handle)) {
        xSemaphoreGiveRecursive(mutex);
        return -1;
    }

    // Get the instance ID
    instId = 0;
    if (!UAVObjIsSingleInstance(obj_handle)) {
        if (PIOS_FREAD
                (file, &instId, sizeof(instId), &bytesRead)) {
            xSemaphoreGiveRecursive(mutex);
            return -1;
        }
    }

    if (UAVObjIsMetaobject(obj_handle)) {
        // If the instance does not exist create it and any other instances before it
        if (instId != 0) {
            // Error, unlock and return
            xSemaphoreGiveRecursive(mutex);
            return -1;
        }
        // Read the instance data
        if (PIOS_FREAD
                (file, MetaDataPtr((struct UAVOMeta *)obj_handle), MetaNumBytes, &bytesRead)) {
            xSemaphoreGiveRecursive(mutex);
            return -1;
        }
    } else {
        // Get the instance information
        instEntry = getInstance((struct UAVOData *)objEntry, instId);

        // If the instance does not exist create it and any other instances before it
        if (instEntry == NULL) {
            instEntry = createInstance((struct UAVOData *)objEntry, instId);
            if (instEntry == NULL) {
                // Error, unlock and return
                xSemaphoreGiveRecursive(mutex);
                return -1;
            }
        }
        // Read the instance data
        if (PIOS_FREAD
                (file, InstanceData(instEntry), ((struct UAVOData *)objEntry)->instance_size, &bytesRead)) {
            xSemaphoreGiveRecursive(mutex);
            return -1;
        }
    }

    // Fire event
    sendEvent(objEntry, instId, EV_UNPACKED);

    // Unlock
    xSemaphoreGiveRecursive(mutex);
    return 0;
}
#endif /* PIOS_USE_SETTINGS_ON_SDCARD */

/**
 * 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_handle, __attribute__((unused)) uint16_t instId)
{
    PIOS_Assert(obj_handle);

#if defined(PIOS_INCLUDE_FLASH_LOGFS_SETTINGS)
    if (UAVObjIsMetaobject(obj_handle)) {
        if (instId != 0) {
            return -1;
        }

        // Fire event on success
        if (PIOS_FLASHFS_ObjLoad(pios_uavo_settings_fs_id, UAVObjGetID(obj_handle), instId, (uint8_t *)MetaDataPtr((struct UAVOMeta *)obj_handle), UAVObjGetNumBytes(obj_handle)) == 0) {
            sendEvent((struct UAVOBase *)obj_handle, instId, EV_UNPACKED);
        } else {
            return -1;
        }
    } else {
        InstanceHandle instEntry = getInstance((struct UAVOData *)obj_handle, instId);

        if (instEntry == NULL) {
            return -1;
        }

        // Fire event on success
        if (PIOS_FLASHFS_ObjLoad(pios_uavo_settings_fs_id, UAVObjGetID(obj_handle), instId, InstanceData(instEntry), UAVObjGetNumBytes(obj_handle)) == 0) {
            sendEvent((struct UAVOBase *)obj_handle, instId, EV_UNPACKED);
        } else {
            return -1;
        }
    }

#endif /* if defined(PIOS_INCLUDE_FLASH_LOGFS_SETTINGS) */

#if defined(PIOS_USE_SETTINGS_ON_SDCARD)
    FILEINFO file;
    uint8_t filename[14];

    // Check for file system availability
    if (PIOS_SDCARD_IsMounted() == 0) {
        return -1;
    }
    // Lock
    xSemaphoreTakeRecursive(mutex, portMAX_DELAY);

    // Get filename
    objectFilename(obj_handle, filename);

    // Open file
    if (PIOS_FOPEN_READ(filename, file)) {
        xSemaphoreGiveRecursive(mutex);
        return -1;
    }
    // Load object
    if (UAVObjLoadFromFile(obj_handle, &file) != 0) {
        PIOS_FCLOSE(file);
        xSemaphoreGiveRecursive(mutex);
        return -1;
    }
    // Done, close file and unlock
    PIOS_FCLOSE(file);
    xSemaphoreGiveRecursive(mutex);
#endif /* PIOS_USE_SETTINGS_ON_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_handle, __attribute__((unused)) uint16_t instId)
{
    PIOS_Assert(obj_handle);
#if defined(PIOS_INCLUDE_FLASH_LOGFS_SETTINGS)
    PIOS_FLASHFS_ObjDelete(pios_uavo_settings_fs_id, UAVObjGetID(obj_handle), instId);
#endif
#if defined(PIOS_USE_SETTINGS_ON_SDCARD)
    uint8_t filename[14];

    // Check for file system availability
    if (PIOS_SDCARD_IsMounted() == 0) {
        return -1;
    }
    // Lock
    xSemaphoreTakeRecursive(mutex, portMAX_DELAY);

    // Get filename
    objectFilename(obj_handle, filename);

    // Delete file
    PIOS_FUNLINK(filename);

    // Done
    xSemaphoreGiveRecursive(mutex);
#endif /* PIOS_USE_SETTINGS_ON_SDCARD */
    return 0;
}

/**
 * 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 (UAVObjIsSettings(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 (UAVObjIsSettings(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 (UAVObjIsSettings(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 (UAVObjIsMetaobject(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 (UAVObjIsMetaobject(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 (UAVObjIsMetaobject(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 (UAVObjIsMetaobject(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 (UAVObjIsMetaobject(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 (UAVObjIsMetaobject(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);
}


/**
 * 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 (!UAVObjIsMetaobject(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);
    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)
{
    PIOS_Assert(obj_handle);
    int32_t res;
    xSemaphoreTakeRecursive(mutex, portMAX_DELAY);
    res = connectObj(obj_handle, 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_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);
}

/**
 * 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_handle)
{
    UAVObjInstanceUpdated(obj_handle, 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_handle, uint16_t instId)
{
    PIOS_Assert(obj_handle);
    xSemaphoreTakeRecursive(mutex, portMAX_DELAY);
    sendEvent((struct UAVOBase *)obj_handle, 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))
{
    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.
 */
static 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,
    };

    // 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.lastQueueErrorID = UAVObjGetID(obj);
                    ++stats.eventQueueErrors;
                }
            }

            // Invoke callback (from event task) if a valid one is registered
            if (event->cb) {
                // invoke callback from the event task, will not block
                if (EventCallbackDispatch(&msg, event->cb) != pdTRUE) {
                    ++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 (UAVObjIsSingleInstance(&(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 *)pvPortMalloc(size);
    if (!instEntry) {
        return NULL;
    }
    memset(instEntry, 0, size);
    LL_APPEND(((struct UAVOMulti *)obj)->instance0.next, instEntry);

    ((struct UAVOMulti *)obj)->num_instances++;

    // Fire event
    UAVObjInstanceUpdated((UAVObjHandle)obj, instId);

    // Done
    return InstanceDataOffset(instEntry);
}

/**
 * Get the instance information or NULL if the instance does not exist
 */
static InstanceHandle getInstance(struct UAVOData *obj, uint16_t instId)
{
    if (UAVObjIsMetaobject(&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 (UAVObjIsSingleInstance(&(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)
{
    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;
            return 0;
        }
    }

    // Add queue to list
    event = (struct ObjectEventEntry *)pvPortMalloc(sizeof(struct ObjectEventEntry));
    if (event == NULL) {
        return -1;
    }
    event->queue     = queue;
    event->cb        = cb;
    event->eventMask = eventMask;
    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;
}

#if defined(PIOS_USE_SETTINGS_ON_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(UAVObjHandle obj_handle, uint8_t *filename)
{
    customSPrintf(filename, (uint8_t *)"%X.obj", UAVObjGetID(obj_handle));
}
#endif /* PIOS_USE_SETTINGS_ON_SDCARD */