mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-02-21 11:54:15 +01:00
Merge remote-tracking branch 'origin/amorale/OP-1395_flight_log_enhancements' into next
This commit is contained in:
commit
6884e9f378
@ -49,6 +49,9 @@ static xSemaphoreHandle mutex = 0;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool logging_enabled = false;
|
static bool logging_enabled = false;
|
||||||
|
#define MAX_CONSECUTIVE_FAILS_COUNT 10
|
||||||
|
static bool log_is_full = false;
|
||||||
|
static uint8_t fails_count = 0;
|
||||||
static uint16_t flightnum = 0;
|
static uint16_t flightnum = 0;
|
||||||
static uint16_t lognum = 0;
|
static uint16_t lognum = 0;
|
||||||
static DebugLogEntryData *buffer = 0;
|
static DebugLogEntryData *buffer = 0;
|
||||||
@ -56,8 +59,16 @@ static DebugLogEntryData *buffer = 0;
|
|||||||
static DebugLogEntryData staticbuffer;
|
static DebugLogEntryData staticbuffer;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Private Function Prototypes */
|
#define LOG_ENTRY_MAX_DATA_SIZE (sizeof(((DebugLogEntryData *)0)->Data))
|
||||||
|
#define LOG_ENTRY_HEADER_SIZE (sizeof(DebugLogEntryData) - LOG_ENTRY_MAX_DATA_SIZE)
|
||||||
|
// build the obj_id as a DEBUGLOGENTRY ID with least significant byte zeroed and filled with flight number
|
||||||
|
#define LOG_GET_FLIGHT_OBJID(x) ((DEBUGLOGENTRY_OBJID & ~0xFF) | (x & 0xFF))
|
||||||
|
|
||||||
|
static uint32_t used_buffer_space = 0;
|
||||||
|
|
||||||
|
/* Private Function Prototypes */
|
||||||
|
static void enqueue_data(uint32_t objid, uint16_t instid, size_t size, uint8_t *data);
|
||||||
|
static bool write_current_buffer();
|
||||||
/**
|
/**
|
||||||
* @brief Initialize the log facility
|
* @brief Initialize the log facility
|
||||||
*/
|
*/
|
||||||
@ -75,9 +86,12 @@ void PIOS_DEBUGLOG_Initialize()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mutexlock();
|
mutexlock();
|
||||||
lognum = 0;
|
lognum = 0;
|
||||||
flightnum = 0;
|
flightnum = 0;
|
||||||
while (PIOS_FLASHFS_ObjLoad(pios_user_fs_id, flightnum * 256, lognum, (uint8_t *)buffer, sizeof(DebugLogEntryData)) == 0) {
|
fails_count = 0;
|
||||||
|
used_buffer_space = 0;
|
||||||
|
log_is_full = false;
|
||||||
|
while (PIOS_FLASHFS_ObjLoad(pios_user_fs_id, LOG_GET_FLIGHT_OBJID(flightnum), lognum, (uint8_t *)buffer, sizeof(DebugLogEntryData)) == 0) {
|
||||||
flightnum++;
|
flightnum++;
|
||||||
}
|
}
|
||||||
mutexunlock();
|
mutexunlock();
|
||||||
@ -90,6 +104,11 @@ void PIOS_DEBUGLOG_Initialize()
|
|||||||
*/
|
*/
|
||||||
void PIOS_DEBUGLOG_Enable(uint8_t enabled)
|
void PIOS_DEBUGLOG_Enable(uint8_t enabled)
|
||||||
{
|
{
|
||||||
|
// increase the flight num as soon as logging is disabled
|
||||||
|
if (logging_enabled && !enabled) {
|
||||||
|
flightnum++;
|
||||||
|
lognum = 0;
|
||||||
|
}
|
||||||
logging_enabled = enabled;
|
logging_enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,30 +122,13 @@ void PIOS_DEBUGLOG_Enable(uint8_t enabled)
|
|||||||
*/
|
*/
|
||||||
void PIOS_DEBUGLOG_UAVObject(uint32_t objid, uint16_t instid, size_t size, uint8_t *data)
|
void PIOS_DEBUGLOG_UAVObject(uint32_t objid, uint16_t instid, size_t size, uint8_t *data)
|
||||||
{
|
{
|
||||||
if (!logging_enabled || !buffer) {
|
if (!logging_enabled || !buffer || log_is_full) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mutexlock();
|
mutexlock();
|
||||||
buffer->Flight = flightnum;
|
|
||||||
#if defined(PIOS_INCLUDE_FREERTOS)
|
|
||||||
buffer->FlightTime = xTaskGetTickCount() * portTICK_RATE_MS;
|
|
||||||
#else
|
|
||||||
buffer->FlightTime = 0;
|
|
||||||
#endif
|
|
||||||
buffer->Entry = lognum;
|
|
||||||
buffer->Type = DEBUGLOGENTRY_TYPE_UAVOBJECT;
|
|
||||||
buffer->ObjectID = objid;
|
|
||||||
buffer->InstanceID = instid;
|
|
||||||
if (size > sizeof(buffer->Data)) {
|
|
||||||
size = sizeof(buffer->Data);
|
|
||||||
}
|
|
||||||
buffer->Size = size;
|
|
||||||
memset(buffer->Data, 0xff, sizeof(buffer->Data));
|
|
||||||
memcpy(buffer->Data, data, size);
|
|
||||||
|
|
||||||
if (PIOS_FLASHFS_ObjSave(pios_user_fs_id, flightnum * 256, lognum, (uint8_t *)buffer, sizeof(DebugLogEntryData)) == 0) {
|
enqueue_data(objid, instid, size, data);
|
||||||
lognum++;
|
|
||||||
}
|
|
||||||
mutexunlock();
|
mutexunlock();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -137,27 +139,30 @@ void PIOS_DEBUGLOG_UAVObject(uint32_t objid, uint16_t instid, size_t size, uint8
|
|||||||
*/
|
*/
|
||||||
void PIOS_DEBUGLOG_Printf(char *format, ...)
|
void PIOS_DEBUGLOG_Printf(char *format, ...)
|
||||||
{
|
{
|
||||||
if (!logging_enabled || !buffer) {
|
if (!logging_enabled || !buffer || log_is_full) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
mutexlock();
|
mutexlock();
|
||||||
|
// flush any pending buffer before writing debug text
|
||||||
|
if (used_buffer_space) {
|
||||||
|
write_current_buffer();
|
||||||
|
}
|
||||||
memset(buffer->Data, 0xff, sizeof(buffer->Data));
|
memset(buffer->Data, 0xff, sizeof(buffer->Data));
|
||||||
vsnprintf((char *)buffer->Data, sizeof(buffer->Data), (char *)format, args);
|
vsnprintf((char *)buffer->Data, sizeof(buffer->Data), (char *)format, args);
|
||||||
buffer->Flight = flightnum;
|
buffer->Flight = flightnum;
|
||||||
#if defined(PIOS_INCLUDE_FREERTOS)
|
|
||||||
buffer->FlightTime = xTaskGetTickCount() * portTICK_RATE_MS;
|
buffer->FlightTime = PIOS_DELAY_GetuS();
|
||||||
#else
|
|
||||||
buffer->FlightTime = 0;
|
|
||||||
#endif
|
|
||||||
buffer->Entry = lognum;
|
buffer->Entry = lognum;
|
||||||
buffer->Type = DEBUGLOGENTRY_TYPE_TEXT;
|
buffer->Type = DEBUGLOGENTRY_TYPE_TEXT;
|
||||||
buffer->ObjectID = 0;
|
buffer->ObjectID = 0;
|
||||||
buffer->InstanceID = 0;
|
buffer->InstanceID = 0;
|
||||||
buffer->Size = strlen((const char *)buffer->Data);
|
buffer->Size = strlen((const char *)buffer->Data);
|
||||||
|
|
||||||
if (PIOS_FLASHFS_ObjSave(pios_user_fs_id, flightnum * 256, lognum, (uint8_t *)buffer, sizeof(DebugLogEntryData)) == 0) {
|
if (PIOS_FLASHFS_ObjSave(pios_user_fs_id, LOG_GET_FLIGHT_OBJID(flightnum), lognum, (uint8_t *)buffer, sizeof(DebugLogEntryData)) == 0) {
|
||||||
lognum++;
|
lognum++;
|
||||||
}
|
}
|
||||||
mutexunlock();
|
mutexunlock();
|
||||||
@ -179,7 +184,7 @@ void PIOS_DEBUGLOG_Printf(char *format, ...)
|
|||||||
int32_t PIOS_DEBUGLOG_Read(void *mybuffer, uint16_t flight, uint16_t inst)
|
int32_t PIOS_DEBUGLOG_Read(void *mybuffer, uint16_t flight, uint16_t inst)
|
||||||
{
|
{
|
||||||
PIOS_Assert(mybuffer);
|
PIOS_Assert(mybuffer);
|
||||||
return PIOS_FLASHFS_ObjLoad(pios_user_fs_id, flight * 256, inst, (uint8_t *)mybuffer, sizeof(DebugLogEntryData));
|
return PIOS_FLASHFS_ObjLoad(pios_user_fs_id, LOG_GET_FLIGHT_OBJID(flight), inst, (uint8_t *)mybuffer, sizeof(DebugLogEntryData));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,11 +219,68 @@ void PIOS_DEBUGLOG_Format(void)
|
|||||||
{
|
{
|
||||||
mutexlock();
|
mutexlock();
|
||||||
PIOS_FLASHFS_Format(pios_user_fs_id);
|
PIOS_FLASHFS_Format(pios_user_fs_id);
|
||||||
lognum = 0;
|
lognum = 0;
|
||||||
flightnum = 0;
|
flightnum = 0;
|
||||||
|
log_is_full = false;
|
||||||
|
fails_count = 0;
|
||||||
|
used_buffer_space = 0;
|
||||||
mutexunlock();
|
mutexunlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enqueue_data(uint32_t objid, uint16_t instid, size_t size, uint8_t *data)
|
||||||
|
{
|
||||||
|
DebugLogEntryData *entry;
|
||||||
|
|
||||||
|
// start a new block
|
||||||
|
if (!used_buffer_space) {
|
||||||
|
entry = buffer;
|
||||||
|
memset(buffer->Data, 0xff, sizeof(buffer->Data));
|
||||||
|
used_buffer_space += size;
|
||||||
|
} else {
|
||||||
|
// if an instance is being filled and there is enough space, does enqueues new data.
|
||||||
|
if (used_buffer_space + size + LOG_ENTRY_HEADER_SIZE > LOG_ENTRY_MAX_DATA_SIZE) {
|
||||||
|
buffer->Type = DEBUGLOGENTRY_TYPE_MULTIPLEUAVOBJECTS;
|
||||||
|
if (!write_current_buffer()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entry = buffer;
|
||||||
|
memset(buffer->Data, 0xff, sizeof(buffer->Data));
|
||||||
|
used_buffer_space += size;
|
||||||
|
} else {
|
||||||
|
entry = (DebugLogEntryData *)&buffer->Data[used_buffer_space];
|
||||||
|
used_buffer_space += size + LOG_ENTRY_HEADER_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->Flight = flightnum;
|
||||||
|
entry->FlightTime = PIOS_DELAY_GetuS();
|
||||||
|
entry->Entry = lognum;
|
||||||
|
entry->Type = DEBUGLOGENTRY_TYPE_UAVOBJECT;
|
||||||
|
entry->ObjectID = objid;
|
||||||
|
entry->InstanceID = instid;
|
||||||
|
if (size > sizeof(buffer->Data)) {
|
||||||
|
size = sizeof(buffer->Data);
|
||||||
|
}
|
||||||
|
entry->Size = size;
|
||||||
|
|
||||||
|
memcpy(entry->Data, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write_current_buffer()
|
||||||
|
{
|
||||||
|
// not enough space, write the block and start a new one
|
||||||
|
if (PIOS_FLASHFS_ObjSave(pios_user_fs_id, LOG_GET_FLIGHT_OBJID(flightnum), lognum, (uint8_t *)buffer, sizeof(DebugLogEntryData)) == 0) {
|
||||||
|
lognum++;
|
||||||
|
fails_count = 0;
|
||||||
|
used_buffer_space = 0;
|
||||||
|
} else {
|
||||||
|
if (fails_count++ > MAX_CONSECUTIVE_FAILS_COUNT) {
|
||||||
|
log_is_full = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
* @}
|
* @}
|
||||||
|
@ -75,13 +75,16 @@ struct jedec_flash_dev {
|
|||||||
enum pios_jedec_dev_magic magic;
|
enum pios_jedec_dev_magic magic;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define FLASH_FAST_PRESCALER PIOS_SPI_PRESCALER_2
|
||||||
|
#define FLASH_PRESCALER PIOS_SPI_PRESCALER_4
|
||||||
|
|
||||||
// ! Private functions
|
// ! Private functions
|
||||||
static int32_t PIOS_Flash_Jedec_Validate(struct jedec_flash_dev *flash_dev);
|
static int32_t PIOS_Flash_Jedec_Validate(struct jedec_flash_dev *flash_dev);
|
||||||
static struct jedec_flash_dev *PIOS_Flash_Jedec_alloc(void);
|
static struct jedec_flash_dev *PIOS_Flash_Jedec_alloc(void);
|
||||||
|
|
||||||
static int32_t PIOS_Flash_Jedec_ReadID(struct jedec_flash_dev *flash_dev);
|
static int32_t PIOS_Flash_Jedec_ReadID(struct jedec_flash_dev *flash_dev);
|
||||||
static int32_t PIOS_Flash_Jedec_ReadStatus(struct jedec_flash_dev *flash_dev);
|
static int32_t PIOS_Flash_Jedec_ReadStatus(struct jedec_flash_dev *flash_dev);
|
||||||
static int32_t PIOS_Flash_Jedec_ClaimBus(struct jedec_flash_dev *flash_dev);
|
static int32_t PIOS_Flash_Jedec_ClaimBus(struct jedec_flash_dev *flash_dev, bool fast);
|
||||||
static int32_t PIOS_Flash_Jedec_ReleaseBus(struct jedec_flash_dev *flash_dev);
|
static int32_t PIOS_Flash_Jedec_ReleaseBus(struct jedec_flash_dev *flash_dev);
|
||||||
static int32_t PIOS_Flash_Jedec_WriteEnable(struct jedec_flash_dev *flash_dev);
|
static int32_t PIOS_Flash_Jedec_WriteEnable(struct jedec_flash_dev *flash_dev);
|
||||||
static int32_t PIOS_Flash_Jedec_Busy(struct jedec_flash_dev *flash_dev);
|
static int32_t PIOS_Flash_Jedec_Busy(struct jedec_flash_dev *flash_dev);
|
||||||
@ -166,12 +169,12 @@ int32_t PIOS_Flash_Jedec_Init(uintptr_t *flash_id, uint32_t spi_id, uint32_t sla
|
|||||||
* @brief Claim the SPI bus for flash use and assert CS pin
|
* @brief Claim the SPI bus for flash use and assert CS pin
|
||||||
* @return 0 for sucess, -1 for failure to get semaphore
|
* @return 0 for sucess, -1 for failure to get semaphore
|
||||||
*/
|
*/
|
||||||
static int32_t PIOS_Flash_Jedec_ClaimBus(struct jedec_flash_dev *flash_dev)
|
static int32_t PIOS_Flash_Jedec_ClaimBus(struct jedec_flash_dev *flash_dev, bool fast)
|
||||||
{
|
{
|
||||||
if (PIOS_SPI_ClaimBus(flash_dev->spi_id) < 0) {
|
if (PIOS_SPI_ClaimBus(flash_dev->spi_id) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
PIOS_SPI_SetClockSpeed(flash_dev->spi_id, fast ? FLASH_FAST_PRESCALER : FLASH_PRESCALER);
|
||||||
PIOS_SPI_RC_PinSet(flash_dev->spi_id, flash_dev->slave_num, 0);
|
PIOS_SPI_RC_PinSet(flash_dev->spi_id, flash_dev->slave_num, 0);
|
||||||
flash_dev->claimed = true;
|
flash_dev->claimed = true;
|
||||||
|
|
||||||
@ -209,7 +212,7 @@ static int32_t PIOS_Flash_Jedec_Busy(struct jedec_flash_dev *flash_dev)
|
|||||||
*/
|
*/
|
||||||
static int32_t PIOS_Flash_Jedec_WriteEnable(struct jedec_flash_dev *flash_dev)
|
static int32_t PIOS_Flash_Jedec_WriteEnable(struct jedec_flash_dev *flash_dev)
|
||||||
{
|
{
|
||||||
if (PIOS_Flash_Jedec_ClaimBus(flash_dev) != 0) {
|
if (PIOS_Flash_Jedec_ClaimBus(flash_dev, true) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +229,7 @@ static int32_t PIOS_Flash_Jedec_WriteEnable(struct jedec_flash_dev *flash_dev)
|
|||||||
*/
|
*/
|
||||||
static int32_t PIOS_Flash_Jedec_ReadStatus(struct jedec_flash_dev *flash_dev)
|
static int32_t PIOS_Flash_Jedec_ReadStatus(struct jedec_flash_dev *flash_dev)
|
||||||
{
|
{
|
||||||
if (PIOS_Flash_Jedec_ClaimBus(flash_dev) < 0) {
|
if (PIOS_Flash_Jedec_ClaimBus(flash_dev, true) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +250,7 @@ static int32_t PIOS_Flash_Jedec_ReadStatus(struct jedec_flash_dev *flash_dev)
|
|||||||
*/
|
*/
|
||||||
static int32_t PIOS_Flash_Jedec_ReadID(struct jedec_flash_dev *flash_dev)
|
static int32_t PIOS_Flash_Jedec_ReadID(struct jedec_flash_dev *flash_dev)
|
||||||
{
|
{
|
||||||
if (PIOS_Flash_Jedec_ClaimBus(flash_dev) < 0) {
|
if (PIOS_Flash_Jedec_ClaimBus(flash_dev, true) < 0) {
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,7 +357,7 @@ static int32_t PIOS_Flash_Jedec_EraseSector(uintptr_t flash_id, uint32_t addr)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PIOS_Flash_Jedec_ClaimBus(flash_dev) != 0) {
|
if (PIOS_Flash_Jedec_ClaimBus(flash_dev, true) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,7 +371,7 @@ static int32_t PIOS_Flash_Jedec_EraseSector(uintptr_t flash_id, uint32_t addr)
|
|||||||
// Keep polling when bus is busy too
|
// Keep polling when bus is busy too
|
||||||
while (PIOS_Flash_Jedec_Busy(flash_dev) != 0) {
|
while (PIOS_Flash_Jedec_Busy(flash_dev) != 0) {
|
||||||
#if defined(FLASH_FREERTOS)
|
#if defined(FLASH_FREERTOS)
|
||||||
vTaskDelay(1);
|
vTaskDelay(2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,7 +397,7 @@ static int32_t PIOS_Flash_Jedec_EraseChip(uintptr_t flash_id)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PIOS_Flash_Jedec_ClaimBus(flash_dev) != 0) {
|
if (PIOS_Flash_Jedec_ClaimBus(flash_dev, true) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,16 +458,14 @@ static int32_t PIOS_Flash_Jedec_WriteData(uintptr_t flash_id, uint32_t addr, uin
|
|||||||
if (((addr & 0xff) + len) > 0x100) {
|
if (((addr & 0xff) + len) > 0x100) {
|
||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = PIOS_Flash_Jedec_WriteEnable(flash_dev)) != 0) {
|
if ((ret = PIOS_Flash_Jedec_WriteEnable(flash_dev)) != 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Execute write page command and clock in address. Keep CS asserted */
|
/* Execute write page command and clock in address. Keep CS asserted */
|
||||||
if (PIOS_Flash_Jedec_ClaimBus(flash_dev) != 0) {
|
if (PIOS_Flash_Jedec_ClaimBus(flash_dev, true) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PIOS_SPI_TransferBlock(flash_dev->spi_id, out, NULL, sizeof(out), NULL) < 0) {
|
if (PIOS_SPI_TransferBlock(flash_dev->spi_id, out, NULL, sizeof(out), NULL) < 0) {
|
||||||
PIOS_Flash_Jedec_ReleaseBus(flash_dev);
|
PIOS_Flash_Jedec_ReleaseBus(flash_dev);
|
||||||
return -1;
|
return -1;
|
||||||
@ -486,7 +487,7 @@ static int32_t PIOS_Flash_Jedec_WriteData(uintptr_t flash_id, uint32_t addr, uin
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
// Query status this way to prevent accel chip locking us out
|
// Query status this way to prevent accel chip locking us out
|
||||||
if (PIOS_Flash_Jedec_ClaimBus(flash_dev) < 0) {
|
if (PIOS_Flash_Jedec_ClaimBus(flash_dev, true) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,13 +537,12 @@ static int32_t PIOS_Flash_Jedec_WriteChunks(uintptr_t flash_id, uint32_t addr, s
|
|||||||
if (((addr & 0xff) + len) > 0x100) {
|
if (((addr & 0xff) + len) > 0x100) {
|
||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = PIOS_Flash_Jedec_WriteEnable(flash_dev)) != 0) {
|
if ((ret = PIOS_Flash_Jedec_WriteEnable(flash_dev)) != 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Execute write page command and clock in address. Keep CS asserted */
|
/* Execute write page command and clock in address. Keep CS asserted */
|
||||||
if (PIOS_Flash_Jedec_ClaimBus(flash_dev) != 0) {
|
if (PIOS_Flash_Jedec_ClaimBus(flash_dev, true) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,17 +582,29 @@ static int32_t PIOS_Flash_Jedec_ReadData(uintptr_t flash_id, uint32_t addr, uint
|
|||||||
if (PIOS_Flash_Jedec_Validate(flash_dev) != 0) {
|
if (PIOS_Flash_Jedec_Validate(flash_dev) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
bool fast_read = flash_dev->cfg->fast_read != 0;
|
||||||
if (PIOS_Flash_Jedec_ClaimBus(flash_dev) == -1) {
|
if (PIOS_Flash_Jedec_ClaimBus(flash_dev, fast_read) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Execute read command and clock in address. Keep CS asserted */
|
/* Execute read command and clock in address. Keep CS asserted */
|
||||||
uint8_t out[] = { JEDEC_READ_DATA, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff };
|
if (!fast_read) {
|
||||||
|
uint8_t out[] = { JEDEC_READ_DATA, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff };
|
||||||
if (PIOS_SPI_TransferBlock(flash_dev->spi_id, out, NULL, sizeof(out), NULL) < 0) {
|
if (PIOS_SPI_TransferBlock(flash_dev->spi_id, out, NULL, sizeof(out), NULL) < 0) {
|
||||||
PIOS_Flash_Jedec_ReleaseBus(flash_dev);
|
PIOS_Flash_Jedec_ReleaseBus(flash_dev);
|
||||||
return -2;
|
return -2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint8_t cmdlen = flash_dev->cfg->fast_read_dummy_bytes + 4;
|
||||||
|
uint8_t out[cmdlen];
|
||||||
|
memset(out, 0x0, cmdlen);
|
||||||
|
out[0] = flash_dev->cfg->fast_read;
|
||||||
|
out[1] = (addr >> 16) & 0xff;
|
||||||
|
out[2] = (addr >> 8) & 0xff;
|
||||||
|
out[3] = addr & 0xff;
|
||||||
|
if (PIOS_SPI_TransferBlock(flash_dev->spi_id, out, NULL, cmdlen, NULL) < 0) {
|
||||||
|
PIOS_Flash_Jedec_ReleaseBus(flash_dev);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the transfer data to the buffer */
|
/* Copy the transfer data to the buffer */
|
||||||
|
@ -40,29 +40,37 @@ const struct pios_flash_jedec_cfg pios_flash_jedec_catalog[] =
|
|||||||
.expect_manufacturer = JEDEC_MANUFACTURER_ST,
|
.expect_manufacturer = JEDEC_MANUFACTURER_ST,
|
||||||
.expect_memorytype = 0x20,
|
.expect_memorytype = 0x20,
|
||||||
.expect_capacity = 0x15,
|
.expect_capacity = 0x15,
|
||||||
.sector_erase = 0xD8,
|
.sector_erase = 0xD8,
|
||||||
.chip_erase = 0xC7,
|
.chip_erase = 0xC7,
|
||||||
|
.fast_read = 0x0B,
|
||||||
|
.fast_read_dummy_bytes = 1,
|
||||||
},
|
},
|
||||||
{ // m25px16
|
{ // m25px16
|
||||||
.expect_manufacturer = JEDEC_MANUFACTURER_ST,
|
.expect_manufacturer = JEDEC_MANUFACTURER_ST,
|
||||||
.expect_memorytype = 0x71,
|
.expect_memorytype = 0x71,
|
||||||
.expect_capacity = 0x15,
|
.expect_capacity = 0x15,
|
||||||
.sector_erase = 0xD8,
|
.sector_erase = 0xD8,
|
||||||
.chip_erase = 0xC7,
|
.chip_erase = 0xC7,
|
||||||
|
.fast_read = 0x0B,
|
||||||
|
.fast_read_dummy_bytes = 1,
|
||||||
},
|
},
|
||||||
{ // w25x
|
{ // w25x
|
||||||
.expect_manufacturer = JEDEC_MANUFACTURER_WINBOND,
|
.expect_manufacturer = JEDEC_MANUFACTURER_WINBOND,
|
||||||
.expect_memorytype = 0x30,
|
.expect_memorytype = 0x30,
|
||||||
.expect_capacity = 0x13,
|
.expect_capacity = 0x13,
|
||||||
.sector_erase = 0x20,
|
.sector_erase = 0x20,
|
||||||
.chip_erase = 0x60
|
.chip_erase = 0x60,
|
||||||
|
.fast_read = 0x0B,
|
||||||
|
.fast_read_dummy_bytes = 1,
|
||||||
},
|
},
|
||||||
{ // 25q16
|
{ // 25q16
|
||||||
.expect_manufacturer = JEDEC_MANUFACTURER_WINBOND,
|
.expect_manufacturer = JEDEC_MANUFACTURER_WINBOND,
|
||||||
.expect_memorytype = 0x40,
|
.expect_memorytype = 0x40,
|
||||||
.expect_capacity = 0x15,
|
.expect_capacity = 0x15,
|
||||||
.sector_erase = 0x20,
|
.sector_erase = 0x20,
|
||||||
.chip_erase = 0x60
|
.chip_erase = 0x60,
|
||||||
|
.fast_read = 0x0B,
|
||||||
|
.fast_read_dummy_bytes = 1,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const uint32_t pios_flash_jedec_catalog_size = NELEMENTS(pios_flash_jedec_catalog);
|
const uint32_t pios_flash_jedec_catalog_size = NELEMENTS(pios_flash_jedec_catalog);
|
||||||
|
@ -40,11 +40,13 @@ extern const struct pios_flash_driver pios_jedec_flash_driver;
|
|||||||
#define JEDEC_MANUFACTURER_WINBOND 0xEF
|
#define JEDEC_MANUFACTURER_WINBOND 0xEF
|
||||||
|
|
||||||
struct pios_flash_jedec_cfg {
|
struct pios_flash_jedec_cfg {
|
||||||
uint8_t expect_manufacturer;
|
uint8_t expect_manufacturer;
|
||||||
uint8_t expect_memorytype;
|
uint8_t expect_memorytype;
|
||||||
uint8_t expect_capacity;
|
uint8_t expect_capacity;
|
||||||
uint32_t sector_erase;
|
uint8_t sector_erase;
|
||||||
uint32_t chip_erase;
|
uint8_t chip_erase;
|
||||||
|
uint8_t fast_read;
|
||||||
|
uint8_t fast_read_dummy_bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
int32_t PIOS_Flash_Jedec_Init(uintptr_t *flash_id, uint32_t spi_id, uint32_t slave_num);
|
int32_t PIOS_Flash_Jedec_Init(uintptr_t *flash_id, uint32_t spi_id, uint32_t slave_num);
|
||||||
|
@ -585,6 +585,9 @@ static int32_t SPI_DMA_TransferBlock(uint32_t spi_id, const uint8_t *send_buffer
|
|||||||
|
|
||||||
/* Wait until all bytes have been transmitted/received */
|
/* Wait until all bytes have been transmitted/received */
|
||||||
while (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) {
|
while (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) {
|
||||||
|
#if defined(PIOS_INCLUDE_FREERTOS)
|
||||||
|
vTaskDelay(0);
|
||||||
|
#endif
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import org.openpilot 1.0
|
|||||||
import "functions.js" as Functions
|
import "functions.js" as Functions
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 600
|
width: 700
|
||||||
height: 400
|
height: 400
|
||||||
id: root
|
id: root
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@ -59,11 +59,11 @@ Rectangle {
|
|||||||
delegate:
|
delegate:
|
||||||
Text {
|
Text {
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
text: Functions.millisToTime(styleData.value)
|
text: Functions.microsToTime(styleData.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TableViewColumn {
|
TableViewColumn {
|
||||||
role: "Type"; title: "Type"; width: 60;
|
role: "Type"; title: "Type"; width: 50;
|
||||||
delegate:
|
delegate:
|
||||||
Text {
|
Text {
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
@ -72,6 +72,7 @@ Rectangle {
|
|||||||
case 0 : text: qsTr("Empty"); break;
|
case 0 : text: qsTr("Empty"); break;
|
||||||
case 1 : text: qsTr("Text"); break;
|
case 1 : text: qsTr("Text"); break;
|
||||||
case 2 : text: qsTr("UAVO"); break;
|
case 2 : text: qsTr("UAVO"); break;
|
||||||
|
case 3 : text: qsTr("UAVO(P)"); break;
|
||||||
default: text: qsTr("Unknown"); break;
|
default: text: qsTr("Unknown"); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,12 +94,16 @@ Rectangle {
|
|||||||
spacing: 10
|
spacing: 10
|
||||||
Text {
|
Text {
|
||||||
id: totalFlights
|
id: totalFlights
|
||||||
text: "<b>" + qsTr("Flights recorded: ") + "</b>" + (logStatus.Flight + 1)
|
text: "<b>" + qsTr("Flights recorded:") + "</b> " + (logStatus.Flight + 1)
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: totalSlots
|
||||||
|
text: "<b>" + qsTr("Slots used/free:") + "</b> " +
|
||||||
|
logStatus.UsedSlots + "/" + logStatus.FreeSlots
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
id: totalEntries
|
id: totalEntries
|
||||||
text: "<b>" + qsTr("Entries logged (free): ") + "</b>" +
|
text: "<b>" + qsTr("Entries downloaded:") + "</b> " + logManager.logEntriesCount
|
||||||
logStatus.UsedSlots + " (" + logStatus.FreeSlots + ")"
|
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
@ -205,21 +205,47 @@ void FlightLogManager::retrieveLogs(int flightToRetrieve)
|
|||||||
for (int flight = startFlight; flight <= endFlight; flight++) {
|
for (int flight = startFlight; flight <= endFlight; flight++) {
|
||||||
m_flightLogControl->setFlight(flight);
|
m_flightLogControl->setFlight(flight);
|
||||||
bool gotLast = false;
|
bool gotLast = false;
|
||||||
int entry = 0;
|
int slot = 0;
|
||||||
while (!gotLast) {
|
while (!gotLast) {
|
||||||
// Send request for loading flight entry on flight side and wait for ack/nack
|
// Send request for loading flight entry on flight side and wait for ack/nack
|
||||||
m_flightLogControl->setEntry(entry);
|
m_flightLogControl->setEntry(slot);
|
||||||
|
|
||||||
if (updateHelper.doObjectAndWait(m_flightLogControl, UAVTALK_TIMEOUT) == UAVObjectUpdaterHelper::SUCCESS &&
|
if (updateHelper.doObjectAndWait(m_flightLogControl, UAVTALK_TIMEOUT) == UAVObjectUpdaterHelper::SUCCESS &&
|
||||||
requestHelper.doObjectAndWait(m_flightLogEntry, UAVTALK_TIMEOUT) == UAVObjectUpdaterHelper::SUCCESS) {
|
requestHelper.doObjectAndWait(m_flightLogEntry, UAVTALK_TIMEOUT) == UAVObjectUpdaterHelper::SUCCESS) {
|
||||||
if (m_flightLogEntry->getType() != DebugLogEntry::TYPE_EMPTY) {
|
if (m_flightLogEntry->getType() != DebugLogEntry::TYPE_EMPTY) {
|
||||||
// Ok, we retrieved the entry, and it was the correct one. clone it and add it to the list
|
// Ok, we retrieved the entry, and it was the correct one. clone it and add it to the list
|
||||||
ExtendedDebugLogEntry *logEntry = new ExtendedDebugLogEntry();
|
ExtendedDebugLogEntry *logEntry = new ExtendedDebugLogEntry();
|
||||||
|
|
||||||
logEntry->setData(m_flightLogEntry->getData(), m_objectManager);
|
logEntry->setData(m_flightLogEntry->getData(), m_objectManager);
|
||||||
m_logEntries << logEntry;
|
m_logEntries << logEntry;
|
||||||
|
if (logEntry->getData().Type == DebugLogEntry::TYPE_MULTIPLEUAVOBJECTS) {
|
||||||
|
const quint32 total_len = sizeof(DebugLogEntry::DataFields);
|
||||||
|
const quint32 data_len = sizeof(((DebugLogEntry::DataFields *)0)->Data);
|
||||||
|
const quint32 header_len = total_len - data_len;
|
||||||
|
|
||||||
|
DebugLogEntry::DataFields fields;
|
||||||
|
quint32 start = logEntry->getData().Size;
|
||||||
|
|
||||||
|
// cycle until there is space for another object
|
||||||
|
while (start + header_len + 1 < data_len) {
|
||||||
|
memset(&fields, 0xFF, total_len);
|
||||||
|
memcpy(&fields, &logEntry->getData().Data[start], header_len);
|
||||||
|
// check wether a packed object is found
|
||||||
|
// note that empty data blocks are set as 0xFF in flight side to minimize flash wearing
|
||||||
|
// thus as soon as this read outside of used area, the test will fail as lenght would be 0xFFFF
|
||||||
|
quint32 toread = header_len + fields.Size;
|
||||||
|
if (!(toread + start > data_len)) {
|
||||||
|
memcpy(&fields, &logEntry->getData().Data[start], toread);
|
||||||
|
ExtendedDebugLogEntry *subEntry = new ExtendedDebugLogEntry();
|
||||||
|
subEntry->setData(fields, m_objectManager);
|
||||||
|
m_logEntries << subEntry;
|
||||||
|
}
|
||||||
|
start += toread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Increment to get next entry from flight side
|
// Increment to get next entry from flight side
|
||||||
entry++;
|
slot++;
|
||||||
} else {
|
} else {
|
||||||
// We are done, not more entries on this flight
|
// We are done, not more entries on this flight
|
||||||
gotLast = true;
|
gotLast = true;
|
||||||
@ -280,7 +306,7 @@ void FlightLogManager::exportToOPL(QString fileName)
|
|||||||
ExtendedDebugLogEntry *entry = m_logEntries[currentEntry];
|
ExtendedDebugLogEntry *entry = m_logEntries[currentEntry];
|
||||||
|
|
||||||
// Only log uavobjects
|
// Only log uavobjects
|
||||||
if (entry->getType() == ExtendedDebugLogEntry::TYPE_UAVOBJECT) {
|
if (entry->getType() == ExtendedDebugLogEntry::TYPE_UAVOBJECT || entry->getType() == ExtendedDebugLogEntry::TYPE_MULTIPLEUAVOBJECTS) {
|
||||||
// Set timestamp that should be logged for this entry
|
// Set timestamp that should be logged for this entry
|
||||||
logFile.setNextTimeStamp(entry->getFlightTime() - adjustedBaseTime);
|
logFile.setNextTimeStamp(entry->getFlightTime() - adjustedBaseTime);
|
||||||
|
|
||||||
@ -615,7 +641,7 @@ QString ExtendedDebugLogEntry::getLogString()
|
|||||||
{
|
{
|
||||||
if (getType() == DebugLogEntry::TYPE_TEXT) {
|
if (getType() == DebugLogEntry::TYPE_TEXT) {
|
||||||
return QString((const char *)getData().Data);
|
return QString((const char *)getData().Data);
|
||||||
} else if (getType() == DebugLogEntry::TYPE_UAVOBJECT) {
|
} else if (getType() == DebugLogEntry::TYPE_UAVOBJECT || getType() == DebugLogEntry::TYPE_MULTIPLEUAVOBJECTS) {
|
||||||
return m_object->toString().replace("\n", " ").replace("\t", " ");
|
return m_object->toString().replace("\n", " ").replace("\t", " ");
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
@ -631,7 +657,7 @@ void ExtendedDebugLogEntry::toXML(QXmlStreamWriter *xmlWriter, quint32 baseTime)
|
|||||||
if (getType() == DebugLogEntry::TYPE_TEXT) {
|
if (getType() == DebugLogEntry::TYPE_TEXT) {
|
||||||
xmlWriter->writeAttribute("type", "text");
|
xmlWriter->writeAttribute("type", "text");
|
||||||
xmlWriter->writeTextElement("message", QString((const char *)getData().Data));
|
xmlWriter->writeTextElement("message", QString((const char *)getData().Data));
|
||||||
} else if (getType() == DebugLogEntry::TYPE_UAVOBJECT) {
|
} else if (getType() == DebugLogEntry::TYPE_UAVOBJECT || getType() == DebugLogEntry::TYPE_MULTIPLEUAVOBJECTS) {
|
||||||
xmlWriter->writeAttribute("type", "uavobject");
|
xmlWriter->writeAttribute("type", "uavobject");
|
||||||
m_object->toXML(xmlWriter);
|
m_object->toXML(xmlWriter);
|
||||||
}
|
}
|
||||||
@ -644,7 +670,7 @@ void ExtendedDebugLogEntry::toCSV(QTextStream *csvStream, quint32 baseTime)
|
|||||||
|
|
||||||
if (getType() == DebugLogEntry::TYPE_TEXT) {
|
if (getType() == DebugLogEntry::TYPE_TEXT) {
|
||||||
data = QString((const char *)getData().Data);
|
data = QString((const char *)getData().Data);
|
||||||
} else if (getType() == DebugLogEntry::TYPE_UAVOBJECT) {
|
} else if (getType() == DebugLogEntry::TYPE_UAVOBJECT || getType() == DebugLogEntry::TYPE_MULTIPLEUAVOBJECTS) {
|
||||||
data = m_object->toString().replace("\n", "").replace("\t", "");
|
data = m_object->toString().replace("\n", "").replace("\t", "");
|
||||||
}
|
}
|
||||||
*csvStream << QString::number(getFlight() + 1) << '\t' << QString::number(getFlightTime() - baseTime) << '\t' << QString::number(getEntry()) << '\t' << data << '\n';
|
*csvStream << QString::number(getFlight() + 1) << '\t' << QString::number(getFlightTime() - baseTime) << '\t' << QString::number(getEntry()) << '\t' << data << '\n';
|
||||||
@ -654,7 +680,7 @@ void ExtendedDebugLogEntry::setData(const DebugLogEntry::DataFields &data, UAVOb
|
|||||||
{
|
{
|
||||||
DebugLogEntry::setData(data);
|
DebugLogEntry::setData(data);
|
||||||
|
|
||||||
if (getType() == DebugLogEntry::TYPE_UAVOBJECT) {
|
if (getType() == DebugLogEntry::TYPE_UAVOBJECT || getType() == DebugLogEntry::TYPE_MULTIPLEUAVOBJECTS) {
|
||||||
UAVDataObject *object = (UAVDataObject *)objectManager->getObject(getObjectID(), getInstanceID());
|
UAVDataObject *object = (UAVDataObject *)objectManager->getObject(getObjectID(), getInstanceID());
|
||||||
Q_ASSERT(object);
|
Q_ASSERT(object);
|
||||||
m_object = object->clone(getInstanceID());
|
m_object = object->clone(getInstanceID());
|
||||||
|
@ -94,6 +94,10 @@ public slots:
|
|||||||
setDirty(true);
|
setDirty(true);
|
||||||
if (m_setting != 1 && m_setting != 3) {
|
if (m_setting != 1 && m_setting != 3) {
|
||||||
setPeriod(0);
|
setPeriod(0);
|
||||||
|
} else {
|
||||||
|
if (!period()) {
|
||||||
|
setPeriod(500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
emit settingChanged(setting);
|
emit settingChanged(setting);
|
||||||
}
|
}
|
||||||
@ -178,7 +182,7 @@ class FlightLogManager : public QObject {
|
|||||||
Q_PROPERTY(QStringList logSettings READ logSettings NOTIFY logSettingsChanged)
|
Q_PROPERTY(QStringList logSettings READ logSettings NOTIFY logSettingsChanged)
|
||||||
Q_PROPERTY(QStringList logStatuses READ logStatuses NOTIFY logStatusesChanged)
|
Q_PROPERTY(QStringList logStatuses READ logStatuses NOTIFY logStatusesChanged)
|
||||||
Q_PROPERTY(int loggingEnabled READ loggingEnabled WRITE setLoggingEnabled NOTIFY loggingEnabledChanged)
|
Q_PROPERTY(int loggingEnabled READ loggingEnabled WRITE setLoggingEnabled NOTIFY loggingEnabledChanged)
|
||||||
|
Q_PROPERTY(int logEntriesCount READ logEntriesCount NOTIFY logEntriesChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FlightLogManager(QObject *parent = 0);
|
explicit FlightLogManager(QObject *parent = 0);
|
||||||
@ -240,7 +244,10 @@ public:
|
|||||||
{
|
{
|
||||||
return m_loggingEnabled;
|
return m_loggingEnabled;
|
||||||
}
|
}
|
||||||
|
int logEntriesCount()
|
||||||
|
{
|
||||||
|
return m_logEntries.count();
|
||||||
|
}
|
||||||
signals:
|
signals:
|
||||||
void logEntriesChanged();
|
void logEntriesChanged();
|
||||||
void flightEntriesChanged();
|
void flightEntriesChanged();
|
||||||
|
@ -11,6 +11,12 @@ function millisToTime(ms) {
|
|||||||
return pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + ":" + pad(msleft, 3);
|
return pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + ":" + pad(msleft, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function microsToTime(us) {
|
||||||
|
var ms = Math.floor(us / 1000);
|
||||||
|
return millisToTime(ms);
|
||||||
|
}
|
||||||
|
|
||||||
function pad(number, length) {
|
function pad(number, length) {
|
||||||
var str = '' + number;
|
var str = '' + number;
|
||||||
while (str.length < length) {
|
while (str.length < length) {
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
<object name="DebugLogEntry" singleinstance="true" settings="false" category="System">
|
<object name="DebugLogEntry" singleinstance="true" settings="false" category="System">
|
||||||
<description>Log Entry in Flash</description>
|
<description>Log Entry in Flash</description>
|
||||||
<field name="Flight" units="" type="uint16" elements="1" />
|
<field name="Flight" units="" type="uint16" elements="1" />
|
||||||
<field name="FlightTime" units="ms" type="uint32" elements="1" />
|
<field name="FlightTime" units="us" type="uint32" elements="1" />
|
||||||
<field name="Entry" units="" type="uint16" elements="1" />
|
<field name="Entry" units="" type="uint16" elements="1" />
|
||||||
<field name="Type" units="" type="enum" elements="1" options="Empty, Text, UAVObject" />
|
<field name="Type" units="" type="enum" elements="1" options="Empty, Text, UAVObject, MultipleUAVObjects" />
|
||||||
<field name="ObjectID" units="" type="uint32" elements="1"/>
|
<field name="ObjectID" units="" type="uint32" elements="1"/>
|
||||||
<field name="InstanceID" units="" type="uint16" elements="1"/>
|
<field name="InstanceID" units="" type="uint16" elements="1"/>
|
||||||
<field name="Size" units="" type="uint16" elements="1" />
|
<field name="Size" units="" type="uint16" elements="1" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user