mirror of
https://github.com/google/pebble.git
synced 2025-07-15 18:46:41 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
512
src/fw/services/normal/protobuf_log/protobuf_log.c
Normal file
512
src/fw/services/normal/protobuf_log/protobuf_log.c
Normal file
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "protobuf_log.h"
|
||||
#include "protobuf_log_private.h"
|
||||
#include "protobuf_log_util.h"
|
||||
|
||||
#include "applib/data_logging.h"
|
||||
#include "drivers/rtc.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "mfg/mfg_serials.h"
|
||||
#include "os/mutex.h"
|
||||
#include "pb.h"
|
||||
#include "pb_decode.h"
|
||||
#include "pb_encode.h"
|
||||
#include "services/normal/data_logging/data_logging_service.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/version.h"
|
||||
#include "util/math.h"
|
||||
#include "util/size.h"
|
||||
#include "util/time/time.h"
|
||||
|
||||
#include "util/uuid.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// These headers auto-generated from the measurements.proto
|
||||
#include "nanopb/common.pb.h"
|
||||
#include "nanopb/event.pb.h"
|
||||
#include "nanopb/measurements.pb.h"
|
||||
#include "nanopb/payload.pb.h"
|
||||
|
||||
#define PROTOBUF_LOG_DEBUG(fmt, args...) \
|
||||
PBL_LOG_D(LOG_DOMAIN_PROTOBUF, LOG_LEVEL_DEBUG, fmt, ## args)
|
||||
|
||||
#define MLOG_MAX_VARINT_ENCODED_SIZE 5
|
||||
|
||||
// Our globals
|
||||
typedef struct PLogState {
|
||||
PebbleMutex *mutex;
|
||||
DataLoggingSession *dls_session;
|
||||
} PLogState;
|
||||
static PLogState s_plog_state;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Get the data logging session. Creating it if not already created
|
||||
static DataLoggingSession *prv_get_dls_session(void) {
|
||||
if (s_plog_state.dls_session == NULL) {
|
||||
const bool buffered = true;
|
||||
const bool resume = false;
|
||||
Uuid system_uuid = UUID_SYSTEM;
|
||||
s_plog_state.dls_session = dls_create(DlsSystemTagProtobufLogSession,
|
||||
DATA_LOGGING_BYTE_ARRAY, PLOG_DLS_RECORD_SIZE, buffered,
|
||||
resume, &system_uuid);
|
||||
if (!s_plog_state.dls_session) {
|
||||
// This can happen when you are not connected to the phone and have rebooted a number of
|
||||
// times because each time you reboot, you get new sessions created and reach the limit
|
||||
// of the max # of sessions allowed.
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Error creating activity logging session");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return s_plog_state.dls_session;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Our default transport, which sends the data over data logging
|
||||
static bool prv_dls_transport(uint8_t *buffer, size_t buf_size) {
|
||||
bool success = false;
|
||||
|
||||
mutex_lock(s_plog_state.mutex);
|
||||
{
|
||||
DataLoggingSession *dls_session = prv_get_dls_session();
|
||||
if (!dls_session) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
// Log the data now, padding with 0's
|
||||
PBL_ASSERTN(buf_size <= PLOG_DLS_RECORD_SIZE);
|
||||
memset(buffer + buf_size, 0, PLOG_DLS_RECORD_SIZE - buf_size);
|
||||
DataLoggingResult result = dls_log(dls_session, buffer, 1);
|
||||
if (result == DATA_LOGGING_SUCCESS) {
|
||||
success = true;
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Error %d while logging data", (int)result);
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(s_plog_state.mutex);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Encode a struct `msg` with the field number and fields passed.
|
||||
static bool prv_encode_struct(pb_ostream_t *stream, uint32_t field_number,
|
||||
const pb_msgdesc_t * fields, const void *msg) {
|
||||
// Encode the field tag and data type
|
||||
if (!pb_encode_tag(stream, PB_WT_STRING, field_number)) {
|
||||
return false;
|
||||
}
|
||||
return pb_encode_submessage(stream, fields, msg);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Encode a payload containing the data blob passed in
|
||||
static bool prv_populate_payload(ProtobufLogConfig *config, size_t buffer_len, uint8_t *buffer,
|
||||
pb_ostream_t *stream) {
|
||||
PLogBufferEncoderArg ms_encoder_arg = {
|
||||
.len = buffer_len,
|
||||
.buffer = buffer,
|
||||
};
|
||||
|
||||
// Version and Patch
|
||||
unsigned int v_major, v_minor;
|
||||
const char *version_patch_ptr;
|
||||
version_get_major_minor_patch(&v_major, &v_minor, &version_patch_ptr);
|
||||
|
||||
// Sender Id
|
||||
const char *watch_serial = mfg_get_serial_number();
|
||||
|
||||
pebble_pipeline_Payload payload = {
|
||||
.sender = {
|
||||
.type = {
|
||||
.funcs.encode = protobuf_log_util_encode_string,
|
||||
.arg = (void *)PLOG_PAYLOAD_SENDER_TYPE,
|
||||
},
|
||||
.id = {
|
||||
.funcs.encode = protobuf_log_util_encode_string,
|
||||
.arg = (void *)watch_serial,
|
||||
},
|
||||
.has_version = true,
|
||||
.version = {
|
||||
.major = v_major,
|
||||
.minor = v_minor,
|
||||
.patch = {
|
||||
.funcs.encode = protobuf_log_util_encode_string,
|
||||
.arg = (void *)version_patch_ptr,
|
||||
},
|
||||
},
|
||||
},
|
||||
.send_time_utc = rtc_get_time(),
|
||||
};
|
||||
|
||||
// NOTE: A Payload is the master protobuf struct that we send to the phone.
|
||||
// Events have already been encoded for Payloads (in `protobuf_log_session_add_event`)
|
||||
// MeasurementSets have not. They are currently only written to the stream as a self standing
|
||||
// object, not for a payload.
|
||||
// This results in encoding them a bit differently at the end.
|
||||
// For Events, just write the exact buffer.
|
||||
// For MeasurementSets, encode them for the Payload.
|
||||
switch (config->type) {
|
||||
case ProtobufLogType_Events:
|
||||
pb_write(stream, ms_encoder_arg.buffer, ms_encoder_arg.len);
|
||||
break;
|
||||
case ProtobufLogType_Measurements:
|
||||
payload.measurement_sets = (pb_callback_t) {
|
||||
.funcs.encode = protobuf_log_util_encode_buffer,
|
||||
.arg = &ms_encoder_arg,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
bool success = pb_encode(stream, pebble_pipeline_Payload_fields, &payload);
|
||||
if (!success) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Error encoding payload");
|
||||
}
|
||||
|
||||
// PBL-43622: Will revert later
|
||||
PBL_LOG(LOG_LEVEL_INFO, "Logged protobuf payload type: %d, utc:%"PRIu32, config->type,
|
||||
payload.send_time_utc);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Free all memory associated with a session
|
||||
static void prv_session_free(PLogSession *session) {
|
||||
kernel_free(session->msg_buffer);
|
||||
kernel_free(session->data_buffer);
|
||||
kernel_free(session);
|
||||
}
|
||||
|
||||
|
||||
bool protobuf_log_init(void) {
|
||||
s_plog_state.mutex = mutex_create();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// How much space is needed in the allocation of the PLogSession (useful for storing extra data).
|
||||
static size_t prv_session_extra_space_needed(const ProtobufLogConfig *config) {
|
||||
switch (config->type) {
|
||||
case ProtobufLogType_Measurements:
|
||||
return (config->measurements.num_types * sizeof(ProtobufLogMeasurementType));
|
||||
case ProtobufLogType_Events:
|
||||
return 0;
|
||||
}
|
||||
WTF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Starts/restarts a measurement session.
|
||||
static bool prv_session_measurement_encode_start(PLogSession *session) {
|
||||
const ProtobufLogConfig *config = &session->config;
|
||||
|
||||
// Set the types pointer to the space allocated right after the PLogSession
|
||||
ProtobufLogMeasurementType *types_copy = (ProtobufLogMeasurementType *) (session + 1);
|
||||
const size_t extra_space = prv_session_extra_space_needed(config);
|
||||
// Copy the types array directly after the Session bytes.
|
||||
memcpy(types_copy, config->measurements.types, extra_space);
|
||||
|
||||
// Generate a new UUID
|
||||
Uuid uuid;
|
||||
uuid_generate(&uuid);
|
||||
|
||||
PLogTypesEncoderArg types_encoder_arg = {
|
||||
.num_types = session->config.measurements.num_types,
|
||||
.types = session->config.measurements.types,
|
||||
};
|
||||
|
||||
pebble_pipeline_MeasurementSet msg = {
|
||||
.uuid = {
|
||||
.funcs.encode = protobuf_log_util_encode_uuid,
|
||||
.arg = &uuid,
|
||||
},
|
||||
.time_utc = session->start_utc,
|
||||
.utc_to_local = time_util_utc_to_local_offset(),
|
||||
.types = {
|
||||
.funcs.encode = protobuf_log_util_encode_measurement_types,
|
||||
.arg = &types_encoder_arg,
|
||||
},
|
||||
};
|
||||
|
||||
return pb_encode(&session->data_stream, pebble_pipeline_MeasurementSet_fields, &msg);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Starts/restarts a session. Allows each type to setup what they need to setup.
|
||||
static bool prv_session_encode_start(PLogSession *session) {
|
||||
const ProtobufLogConfig *config = &session->config;
|
||||
|
||||
// New session start time
|
||||
session->start_utc = rtc_get_time();
|
||||
|
||||
// Create a new stream, reserving space for the header in front
|
||||
session->data_stream = pb_ostream_from_buffer(session->data_buffer, session->max_data_size);
|
||||
|
||||
switch (config->type) {
|
||||
case ProtobufLogType_Measurements: {
|
||||
return prv_session_measurement_encode_start(session);
|
||||
}
|
||||
case ProtobufLogType_Events:
|
||||
return true;
|
||||
}
|
||||
WTF;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Calculates how much space an empty Payload will consume in our buffer. Useful for seeing how
|
||||
// much *other* data we can store in a fixed sized DataLogging packet
|
||||
static uint32_t prv_get_hdr_reserved_size(ProtobufLogConfig *config) {
|
||||
// Figure out how much space we need to reserve for the payload structure in each record
|
||||
pb_ostream_t substream = PB_OSTREAM_SIZING;
|
||||
// Encode a payload with a 0 length data blob.
|
||||
bool success = prv_populate_payload(config, 0, NULL, &substream);
|
||||
PBL_ASSERT(success, "error encoding payload");
|
||||
|
||||
// Save enough room for us to encode the length of the data buffer
|
||||
return substream.bytes_written + MLOG_MAX_VARINT_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
|
||||
ProtobufLogRef protobuf_log_create(ProtobufLogConfig *config,
|
||||
ProtobufLogTransportCB transport,
|
||||
size_t max_msg_size) {
|
||||
// Error check the passed in max encoded message size
|
||||
PBL_ASSERTN(max_msg_size <= PLOG_DLS_RECORD_SIZE);
|
||||
if (max_msg_size == 0) {
|
||||
max_msg_size = PLOG_DLS_RECORD_SIZE;
|
||||
}
|
||||
|
||||
// Default transport
|
||||
if (!transport) {
|
||||
transport = prv_dls_transport;
|
||||
}
|
||||
|
||||
// Create a buffer for the final fully-formed record. Since we send it out through data logging,
|
||||
// make it the size of a data logging record
|
||||
uint8_t *msg_buffer = kernel_zalloc(PLOG_DLS_RECORD_SIZE);
|
||||
if (!msg_buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Number of bytes that are needed to encode the payload structure
|
||||
// (not including the data blob)
|
||||
const uint32_t payload_hdr_size = prv_get_hdr_reserved_size(config);
|
||||
PROTOBUF_LOG_DEBUG("Creating payload session with hdr size of %"PRIu32, payload_hdr_size);
|
||||
|
||||
// Create a buffer for the encoded data blob. We form this first as the caller calls
|
||||
// protobuf_log_session_add_* repeatedly. Once it's filled up, we grab it as the
|
||||
// data blob portion of the payload that's formed in msg_buffer.
|
||||
uint32_t max_data_size = max_msg_size - payload_hdr_size - sizeof(PLogMessageHdr);
|
||||
PROTOBUF_LOG_DEBUG("Max data buffer size: %"PRIu32, max_data_size);
|
||||
uint8_t *data_buffer = kernel_zalloc(max_data_size);
|
||||
if (!data_buffer) {
|
||||
kernel_free(msg_buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Extra space needed for each config to store some variables and information.
|
||||
// e.g. Measurement needs to store an array of types.
|
||||
const size_t extra_size = prv_session_extra_space_needed(config);
|
||||
PLogSession *session = kernel_zalloc(sizeof(PLogSession) + extra_size);
|
||||
if (!session) {
|
||||
kernel_free(msg_buffer);
|
||||
kernel_free(data_buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*session = (PLogSession) {
|
||||
.config = *config,
|
||||
.msg_buffer = msg_buffer,
|
||||
.data_buffer = data_buffer,
|
||||
.max_msg_size = max_msg_size,
|
||||
.max_data_size = max_data_size,
|
||||
.transport = transport,
|
||||
};
|
||||
|
||||
// Start a new encoding
|
||||
const bool success = prv_session_encode_start(session);
|
||||
if (!success) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Error encoding msg");
|
||||
prv_session_free(session);
|
||||
session = NULL;
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Sets the stream to PB_OSTREAM_SIZING and calculates the size of the protobuf structs
|
||||
static uint32_t prv_get_encoded_struct_size(uint32_t field_number, const pb_msgdesc_t * fields,
|
||||
const void *msg) {
|
||||
pb_ostream_t stream = PB_OSTREAM_SIZING;
|
||||
prv_encode_struct(&stream, field_number, fields, msg);
|
||||
return stream.bytes_written;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Takes a generic protobuf struct, calculates the size, and writes it out to the internal buffer.
|
||||
// If it is full, flush then log it.
|
||||
static bool prv_log_struct(PLogSession *session, uint32_t field_number,
|
||||
const pb_msgdesc_t * fields, const void *msg) {
|
||||
// Calculate the size of our struct encoded on wire
|
||||
const uint32_t calc_size = prv_get_encoded_struct_size(field_number, fields, msg);
|
||||
// Calculate our data blob buffer size if we add this struct to it
|
||||
const uint32_t size_if_added = session->data_stream.bytes_written + calc_size;
|
||||
|
||||
// If it fits, add it. If it doesn't, flush first.
|
||||
if (size_if_added > session->max_data_size) {
|
||||
// We would be over capacity if we added this message. Let's flush first.
|
||||
PROTOBUF_LOG_DEBUG("Session: 0x%x - Would have been over limit at size %"PRIu32", flushing",
|
||||
(int)session, size_if_added);
|
||||
protobuf_log_session_flush(session);
|
||||
}
|
||||
|
||||
// Encode the struct into the message
|
||||
bool success = prv_encode_struct(&session->data_stream, field_number, fields, msg);
|
||||
if (!success) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Error adding sample, resetting session");
|
||||
return prv_session_encode_start(session);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool protobuf_log_session_add_measurements(ProtobufLogRef session_ref, time_t sample_utc,
|
||||
uint32_t num_values, uint32_t *values) {
|
||||
PBL_ASSERTN(session_ref != NULL);
|
||||
PLogSession *session = (PLogSession *)session_ref;
|
||||
PBL_ASSERTN(session->config.type == ProtobufLogType_Measurements);
|
||||
int32_t offset_sec = sample_utc - session->start_utc;
|
||||
offset_sec = MAX(0, offset_sec);
|
||||
|
||||
// error check
|
||||
PBL_ASSERT(num_values == session->config.measurements.num_types, "Wrong number of values passed");
|
||||
|
||||
PROTOBUF_LOG_DEBUG("Session: 0x%x - Adding measurement sample with %"PRIu32" values",
|
||||
(int)session_ref, num_values);
|
||||
|
||||
// Encode the Measurement
|
||||
PLogPackedVarintsEncoderArg packed_varint_encoder_arg = {
|
||||
.num_values = num_values,
|
||||
.values = values,
|
||||
};
|
||||
pebble_pipeline_Measurement msg = {
|
||||
.offset_sec = offset_sec,
|
||||
.data = {
|
||||
.funcs.encode = protobuf_log_util_encode_packed_varints,
|
||||
.arg = &packed_varint_encoder_arg,
|
||||
},
|
||||
};
|
||||
|
||||
bool success = prv_log_struct(session,
|
||||
pebble_pipeline_MeasurementSet_measurements_tag,
|
||||
pebble_pipeline_Measurement_fields,
|
||||
&msg);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool protobuf_log_session_add_event(ProtobufLogRef session_ref, pebble_pipeline_Event *event) {
|
||||
PBL_ASSERTN(session_ref != NULL);
|
||||
PLogSession *session = (PLogSession *)session_ref;
|
||||
PBL_ASSERTN(session->config.type == ProtobufLogType_Events);
|
||||
|
||||
// Generate a new UUID
|
||||
Uuid uuid;
|
||||
uuid_generate(&uuid);
|
||||
|
||||
// Don't use {} notation because we won't want to overwrite the data that is already set in
|
||||
// the event.
|
||||
event->created_time_utc = rtc_get_time();
|
||||
event->has_created_time_utc = true;
|
||||
event->utc_to_local = time_util_utc_to_local_offset();
|
||||
event->uuid = (pb_callback_t) {
|
||||
.funcs.encode = protobuf_log_util_encode_uuid,
|
||||
.arg = &uuid,
|
||||
};
|
||||
|
||||
PROTOBUF_LOG_DEBUG("Session: 0x%x - Adding event with type: %d", (int)session_ref, event->type);
|
||||
|
||||
bool success = prv_log_struct(session,
|
||||
pebble_pipeline_Payload_events_tag,
|
||||
pebble_pipeline_Event_fields,
|
||||
event);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool protobuf_log_session_flush(ProtobufLogRef session_ref) {
|
||||
PBL_ASSERTN(session_ref != NULL);
|
||||
PLogSession *session = (PLogSession *)session_ref;
|
||||
bool encode_success = false;
|
||||
|
||||
// Encode the buffer into a Payload
|
||||
const size_t hdr_size = sizeof(PLogMessageHdr);
|
||||
pb_ostream_t stream = pb_ostream_from_buffer(session->msg_buffer + hdr_size,
|
||||
session->max_msg_size - hdr_size);
|
||||
|
||||
bool success = prv_populate_payload(&session->config, session->data_stream.bytes_written,
|
||||
session->data_buffer, &stream);
|
||||
if (!success) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Error encoding payload");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Fill in the message header now
|
||||
PLogMessageHdr *hdr = (PLogMessageHdr *)session->msg_buffer;
|
||||
*hdr = (PLogMessageHdr) {
|
||||
.msg_size = stream.bytes_written,
|
||||
};
|
||||
|
||||
// Send it out now
|
||||
PROTOBUF_LOG_DEBUG("Session: 0x%x - Flushing %d bytes", (int)session_ref, hdr->msg_size);
|
||||
success = (session->transport)(session->msg_buffer, hdr->msg_size + sizeof(PLogMessageHdr));
|
||||
if (!success) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Failure when sending encoded message, resetting session");
|
||||
}
|
||||
|
||||
// TODO: Call a success callback so the clients know exactly which data has been sent.
|
||||
// If we don't do this an we crash without pushing to datalogging, we'll lose data
|
||||
|
||||
exit:
|
||||
encode_success = prv_session_encode_start(session);
|
||||
return (success && encode_success);
|
||||
}
|
||||
|
||||
|
||||
bool protobuf_log_session_delete(ProtobufLogRef session_ref) {
|
||||
PROTOBUF_LOG_DEBUG("Session: 0x%x - Deleting", (int)session_ref);
|
||||
|
||||
if (session_ref == NULL) {
|
||||
return true;
|
||||
}
|
||||
protobuf_log_session_flush(session_ref);
|
||||
prv_session_free(session_ref);
|
||||
return true;
|
||||
}
|
156
src/fw/services/normal/protobuf_log/protobuf_log.h
Normal file
156
src/fw/services/normal/protobuf_log/protobuf_log.h
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! This module handles the collection and sending of periodic protobuf payloads to the phone
|
||||
//! using the protobuf schema defined at src/fw/idl/nanopb/*.proto and sent to the phone via
|
||||
//! data logging.
|
||||
|
||||
#include "services/common/hrm/hrm_manager.h"
|
||||
#include "services/normal/data_logging/dls_private.h"
|
||||
#include "system/version.h"
|
||||
#include "util/uuid.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Auto generated header produced by compiling the .proto files in src/fw/idl
|
||||
#include "nanopb/measurements.pb.h"
|
||||
#include "nanopb/event.pb.h"
|
||||
|
||||
// Create an alias typedef for the auto-generated name
|
||||
typedef pebble_pipeline_MeasurementSet_Type ProtobufLogMeasurementType;
|
||||
typedef pebble_pipeline_ActivityType_InternalType ProtobufLogActivityType;
|
||||
|
||||
#define ProtobufLogMeasurementType_TimeMS pebble_pipeline_MeasurementSet_Type_TimeMS
|
||||
#define ProtobufLogMeasurementType_VMC pebble_pipeline_MeasurementSet_Type_VMC
|
||||
#define ProtobufLogMeasurementType_Steps pebble_pipeline_MeasurementSet_Type_Steps
|
||||
#define ProtobufLogMeasurementType_DistanceCM pebble_pipeline_MeasurementSet_Type_DistanceCM
|
||||
#define ProtobufLogMeasurementType_RestingGCalories pebble_pipeline_MeasurementSet_Type_RestingGCalories
|
||||
#define ProtobufLogMeasurementType_ActiveGCalories pebble_pipeline_MeasurementSet_Type_ActiveGCalories
|
||||
#define ProtobufLogMeasurementType_BPM pebble_pipeline_MeasurementSet_Type_BPM
|
||||
#define ProtobufLogMeasurementType_RR pebble_pipeline_MeasurementSet_Type_RR
|
||||
#define ProtobufLogMeasurementType_Orientation pebble_pipeline_MeasurementSet_Type_Orientation
|
||||
#define ProtobufLogMeasurementType_Light pebble_pipeline_MeasurementSet_Type_Light
|
||||
#define ProtobufLogMeasurementType_Temperature pebble_pipeline_MeasurementSet_Type_Temperature
|
||||
#define ProtobufLogMeasurementType_HRQuality pebble_pipeline_MeasurementSet_Type_HRQuality
|
||||
|
||||
#define ProtobufLogActivityType_UnknownType pebble_pipeline_ActivityType_InternalType_UnknownType
|
||||
#define ProtobufLogActivityType_Sleep pebble_pipeline_ActivityType_InternalType_Sleep
|
||||
#define ProtobufLogActivityType_DeepSleep pebble_pipeline_ActivityType_InternalType_DeepSleep
|
||||
#define ProtobufLogActivityType_Nap pebble_pipeline_ActivityType_InternalType_Nap
|
||||
#define ProtobufLogActivityType_DeepNap pebble_pipeline_ActivityType_InternalType_DeepNap
|
||||
#define ProtobufLogActivityType_Walk pebble_pipeline_ActivityType_InternalType_Walk
|
||||
#define ProtobufLogActivityType_Run pebble_pipeline_ActivityType_InternalType_Run
|
||||
#define ProtobufLogActivityType_Open pebble_pipeline_ActivityType_InternalType_Open
|
||||
|
||||
#define PLOG_MAX_SENDER_ID_LEN 64
|
||||
#define PLOG_MAX_SENDER_TYPE_LEN 64
|
||||
#define PLOG_MAX_SENDER_VERSION_PATCH_LEN FW_METADATA_VERSION_TAG_BYTES
|
||||
#define PLOG_PAYLOAD_SENDER_TYPE "watch"
|
||||
|
||||
// Size of the data logging records we use
|
||||
#define PLOG_DLS_RECORD_SIZE DLS_SESSION_MAX_BUFFERED_ITEM_SIZE
|
||||
|
||||
// Currently supported Payload types
|
||||
//
|
||||
// MEASUREMENTS
|
||||
// - Used today for logging HR bpm and quality for each sample
|
||||
// - Can also be used for logging minute level data with steps, lights, orientation, etc.
|
||||
// - How to use:
|
||||
// - Create a `ProtobufLogConfig` struct with type `ProtobufLogType_Measurements`, the number of
|
||||
// types each sample will contain and the array of types.
|
||||
// - Call `protobuf_log_create` with the config.
|
||||
// - Call `protobuf_log_session_add_measurements` repeatedly with new samples, each containing
|
||||
// the same number of measurements, the number that as set in the `ProtobufLogConfig`.
|
||||
|
||||
// EVENTS
|
||||
// - Used today for logging ActivitySession events
|
||||
// - How to use:
|
||||
// - Create a `ProtobufLogConfig` struct with type `ProtobufLogType_Events`.
|
||||
// - Call `protobuf_log_create` with the config.
|
||||
// - Call `protobuf_log_add_event` repeatedly with new pebble_pipeline_Event's.
|
||||
|
||||
typedef enum ProtobufLogType {
|
||||
ProtobufLogType_Measurements,
|
||||
ProtobufLogType_Events,
|
||||
} ProtobufLogType;
|
||||
|
||||
typedef struct ProtobufLogConfig {
|
||||
ProtobufLogType type;
|
||||
union {
|
||||
struct {
|
||||
uint8_t num_types; // number of readings in each measurement
|
||||
ProtobufLogMeasurementType *types; // Array of measurement types.
|
||||
} measurements;
|
||||
struct {
|
||||
// empty for now
|
||||
} events;
|
||||
};
|
||||
} ProtobufLogConfig;
|
||||
|
||||
// Handle returned when a new protobuf log session is created
|
||||
typedef void *ProtobufLogRef;
|
||||
|
||||
// Signature of the transport callback that can be optionally provided to protobuf_log_create()
|
||||
typedef bool (*ProtobufLogTransportCB)(uint8_t *buffer, size_t buf_size);
|
||||
|
||||
|
||||
// Init the service
|
||||
// @return true if successful
|
||||
bool protobuf_log_init(void);
|
||||
|
||||
// Create a new protobuf log session.
|
||||
// @param[in] config `ProtobufLogConfig` for the type of protobuf log session to create
|
||||
// @param[in] transport optional callback that will be used to send the encoded data out. If
|
||||
// NULL, the data will be sent over data logging by default
|
||||
// @param[in] max_msg_size optional max message size. This should almost always be 0 so that
|
||||
// a default size is used that fills the data logging record as fully as possible. Non-zero
|
||||
// values are mostly used for unit tests.
|
||||
// @return new session pointer, or NULL if error occurred
|
||||
ProtobufLogRef protobuf_log_create(ProtobufLogConfig *config,
|
||||
ProtobufLogTransportCB transport,
|
||||
size_t max_encoded_msg_size);
|
||||
|
||||
// Add a new measurement sample to the session. Once the amount of accumulated measurement data
|
||||
// gets large enough, it will be automatically encoded and sent out.
|
||||
// @param[in] session created by protobuf_log_create
|
||||
// @param[in] sample_utc the UTC timestamp of this sample
|
||||
// @param[in] num_values the number of values in the 'values' array. This must match
|
||||
// the 'num_types' value that was passed to protobuf_log_create() when the session was created
|
||||
// @param[in] values array of measurement values for this sample
|
||||
// @return true on success, false on failure
|
||||
bool protobuf_log_session_add_measurements(ProtobufLogRef session, time_t sample_utc,
|
||||
uint32_t num_values, uint32_t *values);
|
||||
|
||||
// Add a new `pebble_pipeline_Event` to the session. Once the amount of accumulated event data
|
||||
// gets large enough, it will be automatically encoded and sent out.
|
||||
// @param[in] session created by protobuf_log_create
|
||||
// @param[in] event the event to be added
|
||||
// @return true on success, false on failure
|
||||
bool protobuf_log_session_add_event(ProtobufLogRef session_ref, pebble_pipeline_Event *event);
|
||||
|
||||
// Immediately encode and send all payload data accumulated so far.
|
||||
// @param[in] session created by protobuf_log_create
|
||||
// @return true on success, false on failure
|
||||
bool protobuf_log_session_flush(ProtobufLogRef session);
|
||||
|
||||
// Delete a session. This will first issue a protobuf_log_session_flush before deleting the
|
||||
// session.
|
||||
// @param[in] session created by protobuf_log_create
|
||||
// @return true on success, false on failure
|
||||
bool protobuf_log_session_delete(ProtobufLogRef session);
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "protobuf_log_activity_sessions.h"
|
||||
#include "protobuf_log.h"
|
||||
#include "protobuf_log_private.h"
|
||||
#include "protobuf_log_util.h"
|
||||
|
||||
#include "services/common/hrm/hrm_manager.h"
|
||||
#include "services/normal/activity/activity.h"
|
||||
|
||||
#include "nanopb/event.pb.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <util/size.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Convert ActivitySessionType to the internal protobuf representation.
|
||||
static ActivitySessionType prv_proto_type_to_activity_type(ProtobufLogActivityType type) {
|
||||
switch (type) {
|
||||
case ProtobufLogActivityType_UnknownType:
|
||||
return ActivitySessionType_None;
|
||||
case ProtobufLogActivityType_Sleep:
|
||||
return ActivitySessionType_Sleep;
|
||||
case ProtobufLogActivityType_DeepSleep:
|
||||
return ActivitySessionType_RestfulSleep;
|
||||
case ProtobufLogActivityType_Nap:
|
||||
return ActivitySessionType_Nap;
|
||||
case ProtobufLogActivityType_DeepNap:
|
||||
return ActivitySessionType_RestfulNap;
|
||||
case ProtobufLogActivityType_Walk:
|
||||
return ActivitySessionType_Walk;
|
||||
case ProtobufLogActivityType_Run:
|
||||
return ActivitySessionType_Run;
|
||||
case ProtobufLogActivityType_Open:
|
||||
return ActivitySessionType_Open;
|
||||
}
|
||||
WTF;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Convert ActivitySessionType to the internal protobuf representation.
|
||||
static ProtobufLogActivityType prv_activity_type_to_proto_type(ActivitySessionType type) {
|
||||
switch (type) {
|
||||
case ActivitySessionType_None:
|
||||
return ProtobufLogActivityType_UnknownType;
|
||||
case ActivitySessionType_Sleep:
|
||||
return ProtobufLogActivityType_Sleep;
|
||||
case ActivitySessionType_RestfulSleep:
|
||||
return ProtobufLogActivityType_DeepSleep;
|
||||
case ActivitySessionType_Nap:
|
||||
return ProtobufLogActivityType_Nap;
|
||||
case ActivitySessionType_RestfulNap:
|
||||
return ProtobufLogActivityType_DeepNap;
|
||||
case ActivitySessionType_Walk:
|
||||
return ProtobufLogActivityType_Walk;
|
||||
case ActivitySessionType_Run:
|
||||
return ProtobufLogActivityType_Run;
|
||||
case ActivitySessionType_Open:
|
||||
return ProtobufLogActivityType_Open;
|
||||
case ActivitySessionTypeCount:
|
||||
break;
|
||||
}
|
||||
WTF;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to stuff in the sender.type field of a payload
|
||||
// TODO: Don't force it to be one interval
|
||||
static bool prv_encode_intervals(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
|
||||
if (!pb_encode_tag(stream, PB_WT_STRING, pebble_pipeline_ActivitySession_intervals_tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ActivitySession *session = *arg;
|
||||
pebble_pipeline_ActivityInterval msg = {
|
||||
.offset_sec = 0,
|
||||
.duration_sec = session->length_min
|
||||
};
|
||||
return pb_encode_submessage(stream, pebble_pipeline_ActivityInterval_fields, &msg);
|
||||
}
|
||||
|
||||
ProtobufLogRef protobuf_log_activity_sessions_create(void) {
|
||||
ProtobufLogConfig log_config = {
|
||||
.type = ProtobufLogType_Events,
|
||||
.events = {}
|
||||
};
|
||||
|
||||
return protobuf_log_create(&log_config, NULL /*transport*/, 0 /*max_encoded_msg_size*/);
|
||||
}
|
||||
|
||||
// TODO: Actually make sense of this. It is completely wrong.
|
||||
bool protobuf_log_activity_sessions_add(ProtobufLogRef ref, time_t sample_utc,
|
||||
ActivitySession *session) {
|
||||
pebble_pipeline_Event event = {
|
||||
.type = pebble_pipeline_Event_Type_ActivitySessionEvent,
|
||||
.created_time_utc = sample_utc,
|
||||
.duration = session->length_min,
|
||||
.time_utc = session->start_utc,
|
||||
.activity_session = {
|
||||
.type = {
|
||||
// TODO: Custom types
|
||||
.which_type = pebble_pipeline_ActivityType_internal_type_tag,
|
||||
.type = {
|
||||
.internal_type = prv_activity_type_to_proto_type(session->type),
|
||||
}
|
||||
},
|
||||
.start_reason = (session->manual) ? pebble_pipeline_ActivitySession_StartReason_Manual
|
||||
: pebble_pipeline_ActivitySession_StartReason_Automatic,
|
||||
.intervals = {
|
||||
.funcs.encode = prv_encode_intervals,
|
||||
.arg = session,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Also make this exactly to spec
|
||||
bool protobuf_log_activity_sessions_decode(pebble_pipeline_Event *event_in,
|
||||
ActivitySession *session_out) {
|
||||
pebble_pipeline_ActivitySession *activity = &event_in->activity_session;
|
||||
|
||||
*session_out = (ActivitySession) {
|
||||
.start_utc = event_in->time_utc,
|
||||
.type = prv_proto_type_to_activity_type(activity->type.type.internal_type),
|
||||
.length_min = event_in->duration,
|
||||
.manual = (activity->start_reason == pebble_pipeline_ActivitySession_StartReason_Manual),
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "protobuf_log.h"
|
||||
|
||||
#include "services/common/hrm/hrm_manager.h"
|
||||
#include "services/normal/activity/activity.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
ProtobufLogRef protobuf_log_activity_sessions_create(void);
|
||||
|
||||
bool protobuf_log_activity_sessions_add(ProtobufLogRef ref, time_t sample_utc,
|
||||
ActivitySession *session);
|
||||
|
||||
bool protobuf_log_activity_sessions_decode(pebble_pipeline_Event *event_in,
|
||||
ActivitySession *session_out);
|
77
src/fw/services/normal/protobuf_log/protobuf_log_hr.c
Normal file
77
src/fw/services/normal/protobuf_log/protobuf_log_hr.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "protobuf_log_hr.h"
|
||||
#include "protobuf_log.h"
|
||||
|
||||
#include "services/common/hrm/hrm_manager.h"
|
||||
|
||||
#include "nanopb/measurements.pb.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <util/size.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Convert HRMQuality to the internal protobuf representation.
|
||||
T_STATIC uint32_t prv_hr_quality_int(HRMQuality quality) {
|
||||
switch (quality) {
|
||||
case HRMQuality_NoAccel:
|
||||
return pebble_pipeline_MeasurementSet_HeartRateQuality_NoAccel;
|
||||
case HRMQuality_OffWrist:
|
||||
return pebble_pipeline_MeasurementSet_HeartRateQuality_OffWrist;
|
||||
case HRMQuality_NoSignal:
|
||||
return pebble_pipeline_MeasurementSet_HeartRateQuality_NoSignal;
|
||||
case HRMQuality_Worst:
|
||||
return pebble_pipeline_MeasurementSet_HeartRateQuality_Worst;
|
||||
case HRMQuality_Poor:
|
||||
return pebble_pipeline_MeasurementSet_HeartRateQuality_Poor;
|
||||
case HRMQuality_Acceptable:
|
||||
return pebble_pipeline_MeasurementSet_HeartRateQuality_Acceptable;
|
||||
case HRMQuality_Good:
|
||||
return pebble_pipeline_MeasurementSet_HeartRateQuality_Good;
|
||||
case HRMQuality_Excellent:
|
||||
return pebble_pipeline_MeasurementSet_HeartRateQuality_Excellent;
|
||||
}
|
||||
WTF; // Should never get here
|
||||
return 0;
|
||||
}
|
||||
|
||||
ProtobufLogRef protobuf_log_hr_create(ProtobufLogTransportCB transport) {
|
||||
// Create a measure log session, which we use to send heart rate readings to the phone
|
||||
ProtobufLogMeasurementType measure_types[] = {
|
||||
ProtobufLogMeasurementType_BPM,
|
||||
ProtobufLogMeasurementType_HRQuality,
|
||||
};
|
||||
|
||||
ProtobufLogConfig log_config = {
|
||||
.type = ProtobufLogType_Measurements,
|
||||
.measurements = {
|
||||
.types = measure_types,
|
||||
.num_types = ARRAY_LENGTH(measure_types),
|
||||
},
|
||||
};
|
||||
|
||||
return protobuf_log_create(&log_config, transport, 0 /*max_encoded_msg_size*/);
|
||||
}
|
||||
|
||||
bool protobuf_log_hr_add_sample(ProtobufLogRef ref, time_t sample_utc, uint8_t bpm,
|
||||
HRMQuality quality) {
|
||||
uint32_t values[] = {bpm, prv_hr_quality_int(quality)};
|
||||
return protobuf_log_session_add_measurements(ref, sample_utc, ARRAY_LENGTH(values), values);
|
||||
}
|
30
src/fw/services/normal/protobuf_log/protobuf_log_hr.h
Normal file
30
src/fw/services/normal/protobuf_log/protobuf_log_hr.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "protobuf_log.h"
|
||||
|
||||
#include "services/common/hrm/hrm_manager.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
ProtobufLogRef protobuf_log_hr_create(ProtobufLogTransportCB transport);
|
||||
|
||||
bool protobuf_log_hr_add_sample(ProtobufLogRef ref, time_t sample_utc, uint8_t bpm,
|
||||
HRMQuality quality);
|
49
src/fw/services/normal/protobuf_log/protobuf_log_private.h
Normal file
49
src/fw/services/normal/protobuf_log/protobuf_log_private.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pb.h"
|
||||
#include "pb_decode.h"
|
||||
#include "pb_encode.h"
|
||||
|
||||
#include "protobuf_log.h"
|
||||
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// This fixed size header is placed at the beginning of the buffer, before the protobuf
|
||||
// encoded message
|
||||
typedef struct PACKED {
|
||||
uint16_t msg_size;
|
||||
} PLogMessageHdr;
|
||||
|
||||
// Internal structure of a protobuf log session
|
||||
typedef struct PLogSession {
|
||||
// TODO Change comments from MeasurementSet to MeasurementSet/Events
|
||||
|
||||
ProtobufLogConfig config;
|
||||
uint8_t *msg_buffer; // allocated buffer for the final record: PLogMessageHdr + Payload
|
||||
uint8_t *data_buffer; // allocated buffer for the encoded data blob. (e.g. MeasurementSet)
|
||||
// We form the MeasurementSet first, and then after it's complete we
|
||||
// copy it into msg_buffer inside of a Payload
|
||||
size_t max_msg_size; // max # of bytes to use in the allocated buffer
|
||||
size_t max_data_size; // max allowed size of the encoded data blob
|
||||
pb_ostream_t data_stream; // output stream we are writing the data blob to
|
||||
time_t start_utc; // UTC time when session was created
|
||||
ProtobufLogTransportCB transport;
|
||||
} PLogSession;
|
386
src/fw/services/normal/protobuf_log/protobuf_log_test.c
Normal file
386
src/fw/services/normal/protobuf_log/protobuf_log_test.c
Normal file
|
@ -0,0 +1,386 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "protobuf_log_test.h"
|
||||
|
||||
#include "protobuf_log.h"
|
||||
#include "protobuf_log_private.h"
|
||||
#include "protobuf_log_activity_sessions.h"
|
||||
|
||||
#include "pb.h"
|
||||
#include "pb_encode.h"
|
||||
#include "pb_decode.h"
|
||||
|
||||
#include "nanopb/payload.pb.h"
|
||||
#include "nanopb/measurements.pb.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <util/uuid.h>
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode uuid
|
||||
static bool prv_decode_uuid(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
Uuid *ret_uuid = *(Uuid **)arg;
|
||||
size_t uuid_len = stream->bytes_left;
|
||||
PBL_ASSERTN(uuid_len == sizeof(Uuid));
|
||||
|
||||
return pb_read(stream, (uint8_t *)ret_uuid, uuid_len);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode ActivitySession
|
||||
static bool prv_decode_activity_session(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
ActivitySession *ret_uuid = *(ActivitySession **)arg;
|
||||
// TODO: Do something here
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode types
|
||||
typedef struct PLogTypesDecoderArg {
|
||||
uint32_t max_num_types;
|
||||
uint32_t *num_types;
|
||||
ProtobufLogMeasurementType *types;
|
||||
} PLogTypesDecoderArg;
|
||||
|
||||
static bool prv_decode_types(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
PLogTypesDecoderArg *decoder_info = *(PLogTypesDecoderArg **)arg;
|
||||
|
||||
uint64_t value;
|
||||
bool success = pb_decode_varint(stream, &value);
|
||||
if (*decoder_info->num_types < decoder_info->max_num_types) {
|
||||
decoder_info->types[(*decoder_info->num_types)++] = value;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
typedef struct PLogMeasurementsDecoderArg {
|
||||
uint32_t max_num_samples;
|
||||
uint32_t *num_samples;
|
||||
uint32_t *offset_sec;
|
||||
uint32_t max_num_values;
|
||||
uint32_t *num_values;
|
||||
uint32_t *values;
|
||||
} PLogMeasurementsDecoderArg;
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode the packed data in a measurement
|
||||
static bool prv_decode_packed_measurement_data(pb_istream_t *stream, const pb_field_t *field,
|
||||
void **arg) {
|
||||
PLogMeasurementsDecoderArg *decoder_info = *(PLogMeasurementsDecoderArg **)arg;
|
||||
|
||||
while (stream->bytes_left) {
|
||||
uint64_t value;
|
||||
if (!pb_decode_varint(stream, &value)) {
|
||||
return false;
|
||||
}
|
||||
if (*decoder_info->num_values < decoder_info->max_num_values) {
|
||||
decoder_info->values[(*decoder_info->num_values)++] = value;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode measurements. Called once for each measurement
|
||||
static bool prv_decode_measurements(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
PLogMeasurementsDecoderArg *decoder_info = *(PLogMeasurementsDecoderArg **)arg;
|
||||
|
||||
pebble_pipeline_Measurement msg = {
|
||||
.data = {
|
||||
.funcs.decode = prv_decode_packed_measurement_data,
|
||||
.arg = decoder_info,
|
||||
}
|
||||
};
|
||||
|
||||
if (!pb_decode(stream, pebble_pipeline_Measurement_fields, &msg)) {
|
||||
return false;
|
||||
}
|
||||
if (*decoder_info->num_samples < decoder_info->max_num_samples) {
|
||||
decoder_info->offset_sec[(*decoder_info->num_samples)++] = msg.offset_sec;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct PLogMeasurementSetDecoderArg {
|
||||
Uuid *uuid;
|
||||
PLogTypesDecoderArg *types_decoder_arg;
|
||||
PLogMeasurementsDecoderArg *measurements_decoder_arg;
|
||||
uint32_t *time_utc;
|
||||
uint32_t *time_end_utc;
|
||||
int32_t *utc_to_local;
|
||||
} PLogMeasurementSetDecoderArg;
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode a MeasurementSet. Called once for each MeasurementSet
|
||||
static bool prv_decode_measurement_set(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
PLogMeasurementSetDecoderArg *decoder_info = *(PLogMeasurementSetDecoderArg **)arg;
|
||||
|
||||
pebble_pipeline_MeasurementSet mset = {
|
||||
.uuid = {
|
||||
.funcs.decode = prv_decode_uuid,
|
||||
.arg = decoder_info->uuid,
|
||||
},
|
||||
.types = {
|
||||
.funcs.decode = prv_decode_types,
|
||||
.arg = decoder_info->types_decoder_arg,
|
||||
},
|
||||
.measurements = {
|
||||
.funcs.decode = prv_decode_measurements,
|
||||
.arg = decoder_info->measurements_decoder_arg,
|
||||
},
|
||||
};
|
||||
|
||||
bool success = pb_decode(stream, pebble_pipeline_MeasurementSet_fields, &mset);
|
||||
*decoder_info->time_utc = mset.time_utc;
|
||||
*decoder_info->time_end_utc = mset.time_end_utc;
|
||||
*decoder_info->utc_to_local = mset.utc_to_local;
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
typedef struct PLogEventsDecoderArg {
|
||||
uint32_t max_num_events;
|
||||
uint32_t *num_events;
|
||||
pebble_pipeline_Event *events;
|
||||
Uuid *event_uuids;
|
||||
uint32_t max_num_sessions;
|
||||
uint32_t *num_sessions;
|
||||
ActivitySession *sessions;
|
||||
} PLogEventsDecoderArg;
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode a pebble_pipeline_Event. Called once for each Event
|
||||
static bool prv_decode_events(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
PLogEventsDecoderArg *decoder_info = *(PLogEventsDecoderArg **)arg;
|
||||
|
||||
const uint32_t event_idx = *decoder_info->num_events;
|
||||
|
||||
bool success;
|
||||
pebble_pipeline_Event event = {
|
||||
.uuid = {
|
||||
.funcs.decode = prv_decode_uuid,
|
||||
.arg = &decoder_info->event_uuids[event_idx],
|
||||
},
|
||||
};
|
||||
|
||||
success = pb_decode(stream, pebble_pipeline_Event_fields, &event);
|
||||
if (event.type == pebble_pipeline_Event_Type_ActivitySessionEvent) {
|
||||
protobuf_log_activity_sessions_decode(&event, &decoder_info->sessions[event_idx]);
|
||||
(*decoder_info->num_sessions)++;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
decoder_info->events[event_idx] = event;
|
||||
(*decoder_info->num_events)++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode the payload sender type
|
||||
static bool prv_decode_sender_type(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
char *ret_sender_type = *(char **)arg;
|
||||
size_t str_len = stream->bytes_left;
|
||||
PBL_ASSERTN(str_len <= PLOG_MAX_SENDER_TYPE_LEN);
|
||||
|
||||
return pb_read(stream, (uint8_t *)ret_sender_type, str_len);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode the payload sender id
|
||||
static bool prv_decode_sender_id(pb_istream_t *stream, const pb_field_t *field, void **arg) {
|
||||
char *ret_sender_id = *(char **)arg;
|
||||
size_t str_len = stream->bytes_left;
|
||||
PBL_ASSERTN(str_len <= PLOG_MAX_SENDER_ID_LEN);
|
||||
|
||||
return pb_read(stream, (uint8_t *)ret_sender_id, str_len);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to decode the payload sender version patch
|
||||
static bool prv_decode_sender_version_patch(pb_istream_t *stream, const pb_field_t *field,
|
||||
void **arg) {
|
||||
char *ret_sender_version_patch = *(char **)arg;
|
||||
size_t str_len = stream->bytes_left;
|
||||
PBL_ASSERTN(str_len <= PLOG_MAX_SENDER_VERSION_PATCH_LEN);
|
||||
|
||||
return pb_read(stream, (uint8_t *)ret_sender_version_patch, str_len);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Decode an encoded message. Used for debugging and unit tests.
|
||||
bool protobuf_log_private_mset_decode(ProtobufLogType *type,
|
||||
void *encoded_buf,
|
||||
uint32_t encoded_buf_size,
|
||||
char payload_sender_type[PLOG_MAX_SENDER_TYPE_LEN],
|
||||
char payload_sender_id[PLOG_MAX_SENDER_ID_LEN],
|
||||
char payload_sender_version_patch[FW_METADATA_VERSION_TAG_BYTES],
|
||||
uint32_t *payload_send_time,
|
||||
uint32_t *payload_sender_v_major,
|
||||
uint32_t *payload_sender_v_minor,
|
||||
Uuid *uuid,
|
||||
uint32_t *time_utc,
|
||||
uint32_t *time_end_utc,
|
||||
int32_t *utc_to_local,
|
||||
uint32_t *num_types,
|
||||
ProtobufLogMeasurementType *types,
|
||||
uint32_t *num_samples,
|
||||
uint32_t *offset_sec,
|
||||
uint32_t *num_values,
|
||||
uint32_t *values) {
|
||||
pb_istream_t stream = pb_istream_from_buffer(encoded_buf, encoded_buf_size);
|
||||
|
||||
const uint32_t max_num_types = *num_types;
|
||||
const uint32_t max_num_values = *num_values;
|
||||
const uint32_t max_num_samples = *num_samples;
|
||||
|
||||
*num_values = 0;
|
||||
*num_samples = 0;
|
||||
*num_types = 0;
|
||||
|
||||
PLogTypesDecoderArg types_decoder_arg = {
|
||||
.max_num_types = max_num_types,
|
||||
.num_types = num_types,
|
||||
.types = types,
|
||||
};
|
||||
|
||||
PLogMeasurementsDecoderArg measurements_decoder_arg = {
|
||||
.max_num_samples = max_num_samples,
|
||||
.num_samples = num_samples,
|
||||
.offset_sec = offset_sec,
|
||||
.max_num_values = max_num_values,
|
||||
.num_values = num_values,
|
||||
.values = values,
|
||||
};
|
||||
|
||||
PLogMeasurementSetDecoderArg mset_decoder_arg = {
|
||||
.uuid = uuid,
|
||||
.types_decoder_arg = &types_decoder_arg,
|
||||
.measurements_decoder_arg = &measurements_decoder_arg,
|
||||
.time_utc = time_utc,
|
||||
.time_end_utc = time_end_utc,
|
||||
.utc_to_local = utc_to_local,
|
||||
};
|
||||
|
||||
pebble_pipeline_Payload payload = {
|
||||
.sender = {
|
||||
.type = {
|
||||
.funcs.decode = prv_decode_sender_type,
|
||||
.arg = payload_sender_type,
|
||||
},
|
||||
.id = {
|
||||
.funcs.decode = prv_decode_sender_id,
|
||||
.arg = payload_sender_id,
|
||||
},
|
||||
.version = {
|
||||
.patch = {
|
||||
.funcs.decode = prv_decode_sender_version_patch,
|
||||
.arg = payload_sender_version_patch,
|
||||
},
|
||||
},
|
||||
},
|
||||
.measurement_sets = {
|
||||
.funcs.decode = prv_decode_measurement_set,
|
||||
.arg = &mset_decoder_arg,
|
||||
}
|
||||
};
|
||||
|
||||
bool success = pb_decode(&stream, pebble_pipeline_Payload_fields, &payload);
|
||||
*payload_send_time = payload.send_time_utc;
|
||||
if (payload.sender.has_version && payload_sender_v_major && payload_sender_v_minor) {
|
||||
*payload_sender_v_major = payload.sender.version.major;
|
||||
*payload_sender_v_minor = payload.sender.version.minor;
|
||||
}
|
||||
*type = ProtobufLogType_Measurements;
|
||||
return success;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
bool protobuf_log_private_events_decode(ProtobufLogType *type,
|
||||
void *encoded_buf,
|
||||
uint32_t encoded_buf_size,
|
||||
char payload_sender_type[PLOG_MAX_SENDER_TYPE_LEN],
|
||||
char payload_sender_id[PLOG_MAX_SENDER_ID_LEN],
|
||||
char payload_sender_version_patch[FW_METADATA_VERSION_TAG_BYTES],
|
||||
uint32_t *payload_send_time,
|
||||
uint32_t *payload_sender_v_major,
|
||||
uint32_t *payload_sender_v_minor,
|
||||
uint32_t *num_events,
|
||||
pebble_pipeline_Event *events,
|
||||
Uuid *event_uuids,
|
||||
uint32_t *num_sessions,
|
||||
ActivitySession *sessions) {
|
||||
pb_istream_t stream = pb_istream_from_buffer(encoded_buf, encoded_buf_size);
|
||||
|
||||
const uint32_t max_num_events = *num_events;
|
||||
const uint32_t max_num_sessions = *num_sessions;
|
||||
|
||||
*num_events = 0;
|
||||
*num_sessions = 0;
|
||||
|
||||
PLogEventsDecoderArg event_arg = {
|
||||
.max_num_events = max_num_events,
|
||||
.num_events = num_events,
|
||||
.events = events,
|
||||
.event_uuids = event_uuids,
|
||||
.max_num_sessions = max_num_sessions,
|
||||
.num_sessions = num_sessions,
|
||||
.sessions = sessions,
|
||||
};
|
||||
|
||||
pebble_pipeline_Payload payload = {
|
||||
.sender = {
|
||||
.type = {
|
||||
.funcs.decode = prv_decode_sender_type,
|
||||
.arg = payload_sender_type,
|
||||
},
|
||||
.id = {
|
||||
.funcs.decode = prv_decode_sender_id,
|
||||
.arg = payload_sender_id,
|
||||
},
|
||||
.version = {
|
||||
.patch = {
|
||||
.funcs.decode = prv_decode_sender_version_patch,
|
||||
.arg = payload_sender_version_patch,
|
||||
},
|
||||
},
|
||||
},
|
||||
.events = {
|
||||
.funcs.decode = prv_decode_events,
|
||||
.arg = &event_arg
|
||||
}
|
||||
};
|
||||
|
||||
bool success = pb_decode(&stream, pebble_pipeline_Payload_fields, &payload);
|
||||
*payload_send_time = payload.send_time_utc;
|
||||
if (payload.sender.has_version && payload_sender_v_major && payload_sender_v_minor) {
|
||||
*payload_sender_v_major = payload.sender.version.major;
|
||||
*payload_sender_v_minor = payload.sender.version.minor;
|
||||
}
|
||||
*type = ProtobufLogType_Events;
|
||||
return success;
|
||||
}
|
104
src/fw/services/normal/protobuf_log/protobuf_log_test.h
Normal file
104
src/fw/services/normal/protobuf_log/protobuf_log_test.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "protobuf_log.h"
|
||||
#include "services/normal/activity/activity.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Decode an encoded payload with measurementsets. Used for debugging and unit tests.
|
||||
// @param[in] encoded_buf pointer to encoded data
|
||||
// @param[in] encoded_buf_size size of encoded data
|
||||
// @param[out] payload_sender_type returned payload sender type string
|
||||
// @param[out] payload_sender_id returned payload sender id string
|
||||
// @param[out] payload_sender_version_patch returned payload sender version patch string
|
||||
// @param[out] payload_send_time returned payload send time
|
||||
// @param[out] payload_sender_v_major returned payload sender version major digit
|
||||
// @param[out] payload_sender_v_minor returned payload sender version minor digit
|
||||
// @param[out] uuid returned UUID field
|
||||
// @param[out] time_utc returned time_utc field
|
||||
// @param[out] time_end_utc returned time_end_utc field
|
||||
// @param[out] utc_to_local returned utc_to_local field
|
||||
// @param[in:out] num_types on entry, length of the types array; on exit, returned number
|
||||
// of measurement types in the types array
|
||||
// @param[out] types returned array of measurement types
|
||||
// @param[in:out] num_samples on entry, length of the offset_sec array; on exit, returned
|
||||
// number of values in the offset_sec array
|
||||
// @param[out] offset_sec returned offset_sec value for each measurement
|
||||
// @param[in|out] num_values on entry, number of entries available in the values array; On exit,
|
||||
// the actual number of values written to the values array
|
||||
// @param[out] values returned measurement values here. This will contain
|
||||
// num_types * num_measurements values if num_values on entry was large enough
|
||||
// @param[out]
|
||||
// @return true on success, false on failure
|
||||
bool protobuf_log_private_mset_decode(ProtobufLogType *type,
|
||||
void *encoded_buf,
|
||||
uint32_t encoded_buf_size,
|
||||
char payload_sender_type[PLOG_MAX_SENDER_TYPE_LEN],
|
||||
char payload_sender_id[PLOG_MAX_SENDER_ID_LEN],
|
||||
char payload_sender_version_patch[FW_METADATA_VERSION_TAG_BYTES],
|
||||
uint32_t *payload_send_time,
|
||||
uint32_t *payload_sender_v_major,
|
||||
uint32_t *payload_sender_v_minor,
|
||||
Uuid *uuid,
|
||||
uint32_t *time_utc,
|
||||
uint32_t *time_end_utc,
|
||||
int32_t *utc_to_local,
|
||||
uint32_t *num_types,
|
||||
ProtobufLogMeasurementType *types,
|
||||
uint32_t *num_samples,
|
||||
uint32_t *offset_sec,
|
||||
uint32_t *num_values,
|
||||
uint32_t *values);
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Decode an encoded payload with events. Used for debugging and unit tests.
|
||||
// @param[in] encoded_buf pointer to encoded data
|
||||
// @param[in] encoded_buf_size size of encoded data
|
||||
// @param[out] payload_sender_type returned payload sender type string
|
||||
// @param[out] payload_sender_id returned payload sender id string
|
||||
// @param[out] payload_sender_version_patch returned payload sender version patch string
|
||||
// @param[out] payload_send_time returned payload send time
|
||||
// @param[out] payload_sender_v_major returned payload sender version major digit
|
||||
// @param[out] payload_sender_v_minor returned payload sender version minor digit
|
||||
// @param[out] num_events on entry, length of events array; on exit, returned number
|
||||
// of events in the events array
|
||||
// @param[out] events array of events
|
||||
// @param[out] event_uuids array of uuids for events
|
||||
// @param[out] num_sessions on entry, length of sessions array; on exit, returned number
|
||||
// of sessions in the sessions array
|
||||
// @param[out] sessions array of ActivitySessions for events. Indexed by events.
|
||||
// e.g. If there are three events and the first two are Unknown events and the third is an
|
||||
// of type ActivitySession, then it's activity session will be at sessions[2].
|
||||
// @param[out]
|
||||
// @return true on success, false on failure
|
||||
bool protobuf_log_private_events_decode(ProtobufLogType *type,
|
||||
void *encoded_buf,
|
||||
uint32_t encoded_buf_size,
|
||||
char payload_sender_type[PLOG_MAX_SENDER_TYPE_LEN],
|
||||
char payload_sender_id[PLOG_MAX_SENDER_ID_LEN],
|
||||
char payload_sender_version_patch[FW_METADATA_VERSION_TAG_BYTES],
|
||||
uint32_t *payload_send_time,
|
||||
uint32_t *payload_sender_v_major,
|
||||
uint32_t *payload_sender_v_minor,
|
||||
uint32_t *num_events,
|
||||
pebble_pipeline_Event *events,
|
||||
Uuid *event_uuids,
|
||||
uint32_t *num_sessions,
|
||||
ActivitySession *sessions);
|
105
src/fw/services/normal/protobuf_log/protobuf_log_util.c
Normal file
105
src/fw/services/normal/protobuf_log/protobuf_log_util.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "protobuf_log.h"
|
||||
#include "protobuf_log_util.h"
|
||||
|
||||
#include "pb_encode.h"
|
||||
|
||||
#include <util/uuid.h>
|
||||
|
||||
|
||||
|
||||
bool protobuf_log_util_encode_uuid(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg) {
|
||||
if (!pb_encode_tag_for_field(stream, field)) {
|
||||
return false;
|
||||
}
|
||||
Uuid *uuid_p = *(Uuid **)arg;
|
||||
return pb_encode_string(stream, (uint8_t *)uuid_p, sizeof(Uuid));
|
||||
}
|
||||
|
||||
bool protobuf_log_util_encode_string(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg) {
|
||||
if (!pb_encode_tag_for_field(stream, field)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *str = *(char **)arg;
|
||||
return pb_encode_string(stream, (uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
||||
bool protobuf_log_util_encode_packed_varints(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg) {
|
||||
PLogPackedVarintsEncoderArg *encoder_arg = *(PLogPackedVarintsEncoderArg **)arg;
|
||||
|
||||
// We need to figure out the size of the packed array of varints first
|
||||
pb_ostream_t substream = PB_OSTREAM_SIZING;
|
||||
for (unsigned i = 0; i < encoder_arg->num_values; i++) {
|
||||
if (!pb_encode_varint(&substream, encoder_arg->values[i])) {
|
||||
stream->errmsg = substream.errmsg;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
size_t packed_array_size = substream.bytes_written;
|
||||
|
||||
// Encode the tag and wiretype
|
||||
if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Encode the size
|
||||
if (!pb_encode_varint(stream, packed_array_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if just being called to size it up, the stream callback will be NULL
|
||||
if (!stream->callback) {
|
||||
return pb_write(stream, NULL, packed_array_size);
|
||||
}
|
||||
|
||||
// Write out each of the values
|
||||
for (unsigned i = 0; i < encoder_arg->num_values; i++) {
|
||||
if (!pb_encode_varint(stream, encoder_arg->values[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protobuf_log_util_encode_measurement_types(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg) {
|
||||
PLogTypesEncoderArg *encoder_arg = *(PLogTypesEncoderArg **)arg;
|
||||
for (unsigned i = 0; i < encoder_arg->num_types; i++) {
|
||||
if (!pb_encode_tag_for_field(stream, field)) {
|
||||
return false;
|
||||
}
|
||||
if (!pb_encode_varint(stream, encoder_arg->types[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protobuf_log_util_encode_buffer(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg) {
|
||||
PLogBufferEncoderArg *encoder_arg = *(PLogBufferEncoderArg **)arg;
|
||||
if (!pb_encode_tag_for_field(stream, field)) {
|
||||
return false;
|
||||
}
|
||||
return pb_encode_string(stream, encoder_arg->buffer, encoder_arg->len);
|
||||
}
|
59
src/fw/services/normal/protobuf_log/protobuf_log_util.h
Normal file
59
src/fw/services/normal/protobuf_log/protobuf_log_util.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pb.h"
|
||||
|
||||
typedef struct PLogPackedVarintsEncoderArg {
|
||||
uint16_t num_values;
|
||||
uint32_t *values;
|
||||
} PLogPackedVarintsEncoderArg;
|
||||
|
||||
typedef struct PLogTypesEncoderArg {
|
||||
uint16_t num_types;
|
||||
ProtobufLogMeasurementType *types;
|
||||
} PLogTypesEncoderArg;
|
||||
|
||||
typedef struct PLogBufferEncoderArg {
|
||||
uint16_t len;
|
||||
uint8_t *buffer;
|
||||
} PLogBufferEncoderArg;
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to stuff in the uuid
|
||||
bool protobuf_log_util_encode_uuid(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to stuff in a string
|
||||
bool protobuf_log_util_encode_string(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to stuff in a packed array of varints
|
||||
bool protobuf_log_util_encode_packed_varints(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to stuff in the array of types
|
||||
bool protobuf_log_util_encode_measurement_types(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Callback used to stuff in a data buffer. Useful for MeasurementSets or Events
|
||||
bool protobuf_log_util_encode_buffer(pb_ostream_t *stream, const pb_field_t *field,
|
||||
void * const *arg);
|
Loading…
Add table
Add a link
Reference in a new issue