mirror of
https://github.com/esphome/esphome.git
synced 2026-05-28 04:55:48 +08:00
[api] Devirtualize protobuf encode/calculate_size (#14449)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -129,7 +129,7 @@ class APIConnection final : public APIServerConnectionBase {
|
|||||||
void send_homeassistant_action(const HomeassistantActionRequest &call) {
|
void send_homeassistant_action(const HomeassistantActionRequest &call) {
|
||||||
if (!this->flags_.service_call_subscription)
|
if (!this->flags_.service_call_subscription)
|
||||||
return;
|
return;
|
||||||
this->send_message(call, HomeassistantActionRequest::MESSAGE_TYPE);
|
this->send_message(call);
|
||||||
}
|
}
|
||||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
||||||
void on_homeassistant_action_response(const HomeassistantActionResponse &msg) override;
|
void on_homeassistant_action_response(const HomeassistantActionResponse &msg) override;
|
||||||
@@ -153,7 +153,7 @@ class APIConnection final : public APIServerConnectionBase {
|
|||||||
#ifdef USE_HOMEASSISTANT_TIME
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
void send_time_request() {
|
void send_time_request() {
|
||||||
GetTimeRequest req;
|
GetTimeRequest req;
|
||||||
this->send_message(req, GetTimeRequest::MESSAGE_TYPE);
|
this->send_message(req);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -263,7 +263,19 @@ class APIConnection final : public APIServerConnectionBase {
|
|||||||
|
|
||||||
void on_fatal_error() override;
|
void on_fatal_error() override;
|
||||||
void on_no_setup_connection() override;
|
void on_no_setup_connection() override;
|
||||||
bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) override;
|
|
||||||
|
// Function pointer type for type-erased message encoding
|
||||||
|
using MessageEncodeFn = void (*)(const void *, ProtoWriteBuffer &);
|
||||||
|
// Function pointer type for type-erased size calculation
|
||||||
|
using CalculateSizeFn = uint32_t (*)(const void *);
|
||||||
|
|
||||||
|
template<typename T> bool send_message(const T &msg) {
|
||||||
|
if constexpr (T::ESTIMATED_SIZE == 0) {
|
||||||
|
return this->send_message_(0, T::MESSAGE_TYPE, &encode_msg_noop, &msg);
|
||||||
|
} else {
|
||||||
|
return this->send_message_(msg.calculate_size(), T::MESSAGE_TYPE, &proto_encode_msg<T>, &msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void prepare_first_message_buffer(std::vector<uint8_t> &shared_buf, size_t header_padding, size_t total_size) {
|
void prepare_first_message_buffer(std::vector<uint8_t> &shared_buf, size_t header_padding, size_t total_size) {
|
||||||
shared_buf.clear();
|
shared_buf.clear();
|
||||||
@@ -318,28 +330,68 @@ class APIConnection final : public APIServerConnectionBase {
|
|||||||
void process_state_subscriptions_();
|
void process_state_subscriptions_();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Non-template helper to encode any ProtoMessage
|
// Size thunk — converts void* back to concrete type for direct calculate_size() call
|
||||||
static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint8_t message_type, APIConnection *conn,
|
template<typename T> static uint32_t calc_size(const void *msg) {
|
||||||
uint32_t remaining_size);
|
return static_cast<const T *>(msg)->calculate_size();
|
||||||
|
|
||||||
// Helper to fill entity state base and encode message
|
|
||||||
static uint16_t fill_and_encode_entity_state(EntityBase *entity, StateResponseProtoMessage &msg, uint8_t message_type,
|
|
||||||
APIConnection *conn, uint32_t remaining_size) {
|
|
||||||
msg.key = entity->get_object_id_hash();
|
|
||||||
#ifdef USE_DEVICES
|
|
||||||
msg.device_id = entity->get_device_id();
|
|
||||||
#endif
|
|
||||||
return encode_message_to_buffer(msg, message_type, conn, remaining_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to fill entity info base and encode message
|
// Shared no-op encode thunk for empty messages (ESTIMATED_SIZE == 0)
|
||||||
static uint16_t fill_and_encode_entity_info(EntityBase *entity, InfoResponseProtoMessage &msg, uint8_t message_type,
|
static void encode_msg_noop(const void *, ProtoWriteBuffer &) {}
|
||||||
APIConnection *conn, uint32_t remaining_size);
|
|
||||||
|
|
||||||
// Wrapper for entity types that have a device_class field
|
// Non-template buffer management for send_message
|
||||||
|
bool send_message_(uint32_t payload_size, uint8_t message_type, MessageEncodeFn encode_fn, const void *msg);
|
||||||
|
|
||||||
|
// Non-template buffer management for batch encoding
|
||||||
|
static uint16_t encode_to_buffer(uint32_t calculated_size, MessageEncodeFn encode_fn, const void *msg,
|
||||||
|
APIConnection *conn, uint32_t remaining_size);
|
||||||
|
|
||||||
|
// Thin template wrapper — computes size, delegates buffer work to non-template helper
|
||||||
|
template<typename T> static uint16_t encode_message_to_buffer(T &msg, APIConnection *conn, uint32_t remaining_size) {
|
||||||
|
if constexpr (T::ESTIMATED_SIZE == 0) {
|
||||||
|
return encode_to_buffer(0, &encode_msg_noop, &msg, conn, remaining_size);
|
||||||
|
} else {
|
||||||
|
return encode_to_buffer(msg.calculate_size(), &proto_encode_msg<T>, &msg, conn, remaining_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-template core — fills state fields and encodes
|
||||||
|
static uint16_t fill_and_encode_entity_state(EntityBase *entity, StateResponseProtoMessage &msg,
|
||||||
|
CalculateSizeFn size_fn, MessageEncodeFn encode_fn, APIConnection *conn,
|
||||||
|
uint32_t remaining_size);
|
||||||
|
|
||||||
|
// Thin template wrapper
|
||||||
|
template<typename T>
|
||||||
|
static uint16_t fill_and_encode_entity_state(EntityBase *entity, T &msg, APIConnection *conn,
|
||||||
|
uint32_t remaining_size) {
|
||||||
|
return fill_and_encode_entity_state(entity, msg, &calc_size<T>, &proto_encode_msg<T>, conn, remaining_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-template core — fills info fields, allocates buffers, and encodes
|
||||||
|
static uint16_t fill_and_encode_entity_info(EntityBase *entity, InfoResponseProtoMessage &msg,
|
||||||
|
CalculateSizeFn size_fn, MessageEncodeFn encode_fn, APIConnection *conn,
|
||||||
|
uint32_t remaining_size);
|
||||||
|
|
||||||
|
// Thin template wrapper
|
||||||
|
template<typename T>
|
||||||
|
static uint16_t fill_and_encode_entity_info(EntityBase *entity, T &msg, APIConnection *conn,
|
||||||
|
uint32_t remaining_size) {
|
||||||
|
return fill_and_encode_entity_info(entity, msg, &calc_size<T>, &proto_encode_msg<T>, conn, remaining_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-template core — fills device_class, then delegates to fill_and_encode_entity_info
|
||||||
static uint16_t fill_and_encode_entity_info_with_device_class(EntityBase *entity, InfoResponseProtoMessage &msg,
|
static uint16_t fill_and_encode_entity_info_with_device_class(EntityBase *entity, InfoResponseProtoMessage &msg,
|
||||||
StringRef &device_class_field, uint8_t message_type,
|
StringRef &device_class_field, CalculateSizeFn size_fn,
|
||||||
APIConnection *conn, uint32_t remaining_size);
|
MessageEncodeFn encode_fn, APIConnection *conn,
|
||||||
|
uint32_t remaining_size);
|
||||||
|
|
||||||
|
// Thin template wrapper
|
||||||
|
template<typename T>
|
||||||
|
static uint16_t fill_and_encode_entity_info_with_device_class(EntityBase *entity, T &msg,
|
||||||
|
StringRef &device_class_field, APIConnection *conn,
|
||||||
|
uint32_t remaining_size) {
|
||||||
|
return fill_and_encode_entity_info_with_device_class(entity, msg, device_class_field, &calc_size<T>,
|
||||||
|
&proto_encode_msg<T>, conn, remaining_size);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
// Helper to check voice assistant validity and connection ownership
|
// Helper to check voice assistant validity and connection ownership
|
||||||
|
|||||||
+828
-608
File diff suppressed because it is too large
Load Diff
+176
-176
File diff suppressed because it is too large
Load Diff
@@ -19,14 +19,6 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
public:
|
public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool send_message(const ProtoMessage &msg, uint8_t message_type) {
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
|
||||||
DumpBuffer dump_buf;
|
|
||||||
this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));
|
|
||||||
#endif
|
|
||||||
return this->send_message_impl(msg, message_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void on_hello_request(const HelloRequest &value){};
|
virtual void on_hello_request(const HelloRequest &value){};
|
||||||
|
|
||||||
virtual void on_disconnect_request(){};
|
virtual void on_disconnect_request(){};
|
||||||
|
|||||||
@@ -359,11 +359,11 @@ void APIServer::on_update(update::UpdateEntity *obj) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ZWAVE_PROXY
|
#ifdef USE_ZWAVE_PROXY
|
||||||
void APIServer::on_zwave_proxy_request(const esphome::api::ProtoMessage &msg) {
|
void APIServer::on_zwave_proxy_request(const ZWaveProxyRequest &msg) {
|
||||||
// We could add code to manage a second subscription type, but, since this message type is
|
// We could add code to manage a second subscription type, but, since this message type is
|
||||||
// very infrequent and small, we simply send it to all clients
|
// very infrequent and small, we simply send it to all clients
|
||||||
for (auto &c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_message(msg, api::ZWaveProxyRequest::MESSAGE_TYPE);
|
c->send_message(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -531,7 +531,7 @@ bool APIServer::update_noise_psk_(const SavedNoisePsk &new_psk, const LogString
|
|||||||
this->set_noise_psk(active_psk);
|
this->set_noise_psk(active_psk);
|
||||||
for (auto &c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
DisconnectRequest req;
|
DisconnectRequest req;
|
||||||
c->send_message(req, DisconnectRequest::MESSAGE_TYPE);
|
c->send_message(req);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -631,7 +631,7 @@ void APIServer::on_shutdown() {
|
|||||||
// Send disconnect requests to all connected clients
|
// Send disconnect requests to all connected clients
|
||||||
for (auto &c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
DisconnectRequest req;
|
DisconnectRequest req;
|
||||||
if (!c->send_message(req, DisconnectRequest::MESSAGE_TYPE)) {
|
if (!c->send_message(req)) {
|
||||||
// If we can't send the disconnect request directly (tx_buffer full),
|
// If we can't send the disconnect request directly (tx_buffer full),
|
||||||
// schedule it at the front of the batch so it will be sent with priority
|
// schedule it at the front of the batch so it will be sent with priority
|
||||||
c->schedule_message_front_(nullptr, DisconnectRequest::MESSAGE_TYPE, DisconnectRequest::ESTIMATED_SIZE);
|
c->schedule_message_front_(nullptr, DisconnectRequest::MESSAGE_TYPE, DisconnectRequest::ESTIMATED_SIZE);
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ class APIServer : public Component,
|
|||||||
void on_update(update::UpdateEntity *obj) override;
|
void on_update(update::UpdateEntity *obj) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ZWAVE_PROXY
|
#ifdef USE_ZWAVE_PROXY
|
||||||
void on_zwave_proxy_request(const esphome::api::ProtoMessage &msg);
|
void on_zwave_proxy_request(const ZWaveProxyRequest &msg);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_IR_RF
|
#ifdef USE_IR_RF
|
||||||
void send_infrared_rf_receive_event(uint32_t device_id, uint32_t key, const std::vector<int32_t> *timings);
|
void send_infrared_rf_receive_event(uint32_t device_id, uint32_t key, const std::vector<int32_t> *timings);
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(clie
|
|||||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||||
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
|
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
|
||||||
auto resp = service->encode_list_service_response();
|
auto resp = service->encode_list_service_response();
|
||||||
return this->client_->send_message(resp, ListEntitiesServicesResponse::MESSAGE_TYPE);
|
return this->client_->send_message(resp);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
+74
-347
@@ -364,7 +364,11 @@ class ProtoWriteBuffer {
|
|||||||
/// Encode a packed repeated sint32 field (zero-copy from vector)
|
/// Encode a packed repeated sint32 field (zero-copy from vector)
|
||||||
void encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values);
|
void encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values);
|
||||||
/// Encode a nested message field (force=true for repeated, false for singular)
|
/// Encode a nested message field (force=true for repeated, false for singular)
|
||||||
void encode_message(uint32_t field_id, const ProtoMessage &value, bool force = true);
|
/// Templated so concrete message type is preserved for direct encode/calculate_size calls.
|
||||||
|
template<typename T> void encode_message(uint32_t field_id, const T &value, bool force = true);
|
||||||
|
// Non-template core for encode_message — all buffer work happens here
|
||||||
|
void encode_message(uint32_t field_id, uint32_t msg_length_bytes, const void *value,
|
||||||
|
void (*encode_fn)(const void *, ProtoWriteBuffer &), bool force);
|
||||||
std::vector<uint8_t> *get_buffer() const { return buffer_; }
|
std::vector<uint8_t> *get_buffer() const { return buffer_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -452,20 +456,20 @@ class DumpBuffer {
|
|||||||
|
|
||||||
class ProtoMessage {
|
class ProtoMessage {
|
||||||
public:
|
public:
|
||||||
// Default implementation for messages with no fields
|
// Non-virtual defaults for messages with no fields.
|
||||||
virtual void encode(ProtoWriteBuffer &buffer) const {}
|
// Concrete message classes hide these with their own implementations.
|
||||||
// Default implementation for messages with no fields
|
// All call sites use templates to preserve the concrete type, so virtual
|
||||||
virtual void calculate_size(ProtoSize &size) const {}
|
// dispatch is not needed. This eliminates per-message vtable entries for
|
||||||
// Convenience: calculate and return size directly (defined after ProtoSize)
|
// encode/calculate_size, saving ~1.3 KB of flash across all message types.
|
||||||
uint32_t calculated_size() const;
|
void encode(ProtoWriteBuffer &buffer) const {}
|
||||||
|
uint32_t calculate_size() const { return 0; }
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
virtual const char *dump_to(DumpBuffer &out) const = 0;
|
virtual const char *dump_to(DumpBuffer &out) const = 0;
|
||||||
virtual const char *message_name() const { return "unknown"; }
|
virtual const char *message_name() const { return "unknown"; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Non-virtual: messages are never deleted polymorphically.
|
// Non-virtual destructor is protected to prevent polymorphic deletion.
|
||||||
// Protected prevents accidental `delete base_ptr` (compile error).
|
|
||||||
~ProtoMessage() = default;
|
~ProtoMessage() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -494,32 +498,7 @@ class ProtoDecodableMessage : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class ProtoSize {
|
class ProtoSize {
|
||||||
private:
|
|
||||||
uint32_t total_size_ = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* @brief ProtoSize class for Protocol Buffer serialization size calculation
|
|
||||||
*
|
|
||||||
* This class provides methods to calculate the exact byte counts needed
|
|
||||||
* for encoding various Protocol Buffer field types. The class now uses an
|
|
||||||
* object-based approach to reduce parameter passing overhead while keeping
|
|
||||||
* varint calculation methods static for external use.
|
|
||||||
*
|
|
||||||
* Implements Protocol Buffer encoding size calculation according to:
|
|
||||||
* https://protobuf.dev/programming-guides/encoding/
|
|
||||||
*
|
|
||||||
* Key features:
|
|
||||||
* - Object-based approach reduces flash usage by eliminating parameter passing
|
|
||||||
* - Early-return optimization for zero/default values
|
|
||||||
* - Static varint methods for external callers
|
|
||||||
* - Specialized handling for different field types according to protobuf spec
|
|
||||||
*/
|
|
||||||
|
|
||||||
ProtoSize() = default;
|
|
||||||
|
|
||||||
uint32_t get_size() const { return total_size_; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
|
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
|
||||||
*
|
*
|
||||||
@@ -616,320 +595,77 @@ class ProtoSize {
|
|||||||
return varint(tag);
|
return varint(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Static methods that RETURN size contribution (no ProtoSize object needed).
|
||||||
* @brief Common parameters for all add_*_field methods
|
// Used by generated calculate_size() methods to accumulate into a plain uint32_t register.
|
||||||
*
|
static constexpr uint32_t calc_int32(uint32_t field_id_size, int32_t value) {
|
||||||
* All add_*_field methods follow these common patterns:
|
return value ? field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value))) : 0;
|
||||||
* * @param field_id_size Pre-calculated size of the field ID in bytes
|
|
||||||
* @param value The value to calculate size for (type varies)
|
|
||||||
* @param force Whether to calculate size even if the value is default/zero/empty
|
|
||||||
*
|
|
||||||
* Each method follows this implementation pattern:
|
|
||||||
* 1. Skip calculation if value is default (0, false, empty) and not forced
|
|
||||||
* 2. Calculate the size based on the field's encoding rules
|
|
||||||
* 3. Add the field_id_size + calculated value size to total_size
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of an int32 field to the total message size
|
|
||||||
*/
|
|
||||||
inline void add_int32(uint32_t field_id_size, int32_t value) {
|
|
||||||
if (value != 0) {
|
|
||||||
add_int32_force(field_id_size, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_int32_force(uint32_t field_id_size, int32_t value) {
|
||||||
/**
|
return field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value)));
|
||||||
* @brief Calculates and adds the size of an int32 field to the total message size (force version)
|
|
||||||
*/
|
|
||||||
inline void add_int32_force(uint32_t field_id_size, int32_t value) {
|
|
||||||
// Always calculate size when forced
|
|
||||||
// Negative values are encoded as 10-byte varints in protobuf
|
|
||||||
total_size_ += field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value)));
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_uint32(uint32_t field_id_size, uint32_t value) {
|
||||||
/**
|
return value ? field_id_size + varint(value) : 0;
|
||||||
* @brief Calculates and adds the size of a uint32 field to the total message size
|
|
||||||
*/
|
|
||||||
inline void add_uint32(uint32_t field_id_size, uint32_t value) {
|
|
||||||
if (value != 0) {
|
|
||||||
add_uint32_force(field_id_size, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_uint32_force(uint32_t field_id_size, uint32_t value) {
|
||||||
/**
|
return field_id_size + varint(value);
|
||||||
* @brief Calculates and adds the size of a uint32 field to the total message size (force version)
|
|
||||||
*/
|
|
||||||
inline void add_uint32_force(uint32_t field_id_size, uint32_t value) {
|
|
||||||
// Always calculate size when force is true
|
|
||||||
total_size_ += field_id_size + varint(value);
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_bool(uint32_t field_id_size, bool value) { return value ? field_id_size + 1 : 0; }
|
||||||
/**
|
static constexpr uint32_t calc_bool_force(uint32_t field_id_size) { return field_id_size + 1; }
|
||||||
* @brief Calculates and adds the size of a boolean field to the total message size
|
static constexpr uint32_t calc_float(uint32_t field_id_size, float value) {
|
||||||
*/
|
return value != 0.0f ? field_id_size + 4 : 0;
|
||||||
inline void add_bool(uint32_t field_id_size, bool value) {
|
|
||||||
if (value) {
|
|
||||||
// Boolean fields always use 1 byte when true
|
|
||||||
total_size_ += field_id_size + 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_fixed32(uint32_t field_id_size, uint32_t value) {
|
||||||
/**
|
return value ? field_id_size + 4 : 0;
|
||||||
* @brief Calculates and adds the size of a boolean field to the total message size (force version)
|
|
||||||
*/
|
|
||||||
inline void add_bool_force(uint32_t field_id_size, bool value) {
|
|
||||||
// Always calculate size when force is true
|
|
||||||
// Boolean fields always use 1 byte
|
|
||||||
total_size_ += field_id_size + 1;
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_sfixed32(uint32_t field_id_size, int32_t value) {
|
||||||
/**
|
return value ? field_id_size + 4 : 0;
|
||||||
* @brief Calculates and adds the size of a float field to the total message size
|
|
||||||
*/
|
|
||||||
inline void add_float(uint32_t field_id_size, float value) {
|
|
||||||
if (value != 0.0f) {
|
|
||||||
total_size_ += field_id_size + 4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_sint32(uint32_t field_id_size, int32_t value) {
|
||||||
// NOTE: add_double_field removed - wire type 1 (64-bit: double) not supported
|
return value ? field_id_size + varint(encode_zigzag32(value)) : 0;
|
||||||
// to reduce overhead on embedded systems
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of a fixed32 field to the total message size
|
|
||||||
*/
|
|
||||||
inline void add_fixed32(uint32_t field_id_size, uint32_t value) {
|
|
||||||
if (value != 0) {
|
|
||||||
total_size_ += field_id_size + 4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_sint32_force(uint32_t field_id_size, int32_t value) {
|
||||||
// NOTE: add_fixed64_field removed - wire type 1 (64-bit: fixed64) not supported
|
return field_id_size + varint(encode_zigzag32(value));
|
||||||
// to reduce overhead on embedded systems
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of a sfixed32 field to the total message size
|
|
||||||
*/
|
|
||||||
inline void add_sfixed32(uint32_t field_id_size, int32_t value) {
|
|
||||||
if (value != 0) {
|
|
||||||
total_size_ += field_id_size + 4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_int64(uint32_t field_id_size, int64_t value) {
|
||||||
// NOTE: add_sfixed64_field removed - wire type 1 (64-bit: sfixed64) not supported
|
return value ? field_id_size + varint(value) : 0;
|
||||||
// to reduce overhead on embedded systems
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of a sint32 field to the total message size
|
|
||||||
*
|
|
||||||
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
|
|
||||||
*/
|
|
||||||
inline void add_sint32(uint32_t field_id_size, int32_t value) {
|
|
||||||
if (value != 0) {
|
|
||||||
add_sint32_force(field_id_size, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_int64_force(uint32_t field_id_size, int64_t value) {
|
||||||
/**
|
return field_id_size + varint(value);
|
||||||
* @brief Calculates and adds the size of a sint32 field to the total message size (force version)
|
|
||||||
*
|
|
||||||
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
|
|
||||||
*/
|
|
||||||
inline void add_sint32_force(uint32_t field_id_size, int32_t value) {
|
|
||||||
// Always calculate size when force is true
|
|
||||||
// ZigZag encoding for sint32
|
|
||||||
total_size_ += field_id_size + varint(encode_zigzag32(value));
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_uint64(uint32_t field_id_size, uint64_t value) {
|
||||||
/**
|
return value ? field_id_size + varint(value) : 0;
|
||||||
* @brief Calculates and adds the size of an int64 field to the total message size
|
|
||||||
*/
|
|
||||||
inline void add_int64(uint32_t field_id_size, int64_t value) {
|
|
||||||
if (value != 0) {
|
|
||||||
add_int64_force(field_id_size, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_uint64_force(uint32_t field_id_size, uint64_t value) {
|
||||||
/**
|
return field_id_size + varint(value);
|
||||||
* @brief Calculates and adds the size of an int64 field to the total message size (force version)
|
|
||||||
*/
|
|
||||||
inline void add_int64_force(uint32_t field_id_size, int64_t value) {
|
|
||||||
// Always calculate size when force is true
|
|
||||||
total_size_ += field_id_size + varint(value);
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_length(uint32_t field_id_size, size_t len) {
|
||||||
/**
|
return len ? field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len) : 0;
|
||||||
* @brief Calculates and adds the size of a uint64 field to the total message size
|
|
||||||
*/
|
|
||||||
inline void add_uint64(uint32_t field_id_size, uint64_t value) {
|
|
||||||
if (value != 0) {
|
|
||||||
add_uint64_force(field_id_size, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_length_force(uint32_t field_id_size, size_t len) {
|
||||||
/**
|
return field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
|
||||||
* @brief Calculates and adds the size of a uint64 field to the total message size (force version)
|
|
||||||
*/
|
|
||||||
inline void add_uint64_force(uint32_t field_id_size, uint64_t value) {
|
|
||||||
// Always calculate size when force is true
|
|
||||||
total_size_ += field_id_size + varint(value);
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_sint64(uint32_t field_id_size, int64_t value) {
|
||||||
// NOTE: sint64 support functions (add_sint64_field, add_sint64_field_force) removed
|
return value ? field_id_size + varint(encode_zigzag64(value)) : 0;
|
||||||
// sint64 type is not supported by ESPHome API to reduce overhead on embedded systems
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of a length-delimited field (string/bytes) to the total message size
|
|
||||||
*/
|
|
||||||
inline void add_length(uint32_t field_id_size, size_t len) {
|
|
||||||
if (len != 0) {
|
|
||||||
add_length_force(field_id_size, len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_sint64_force(uint32_t field_id_size, int64_t value) {
|
||||||
/**
|
return field_id_size + varint(encode_zigzag64(value));
|
||||||
* @brief Calculates and adds the size of a length-delimited field (string/bytes) to the total message size (repeated
|
|
||||||
* field version)
|
|
||||||
*/
|
|
||||||
inline void add_length_force(uint32_t field_id_size, size_t len) {
|
|
||||||
// Always calculate size when force is true
|
|
||||||
// Field ID + length varint + data bytes
|
|
||||||
total_size_ += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_fixed64(uint32_t field_id_size, uint64_t value) {
|
||||||
/**
|
return value ? field_id_size + 8 : 0;
|
||||||
* @brief Adds a pre-calculated size directly to the total
|
|
||||||
*
|
|
||||||
* This is used when we can calculate the total size by multiplying the number
|
|
||||||
* of elements by the bytes per element (for repeated fixed-size types like float, fixed32, etc.)
|
|
||||||
*
|
|
||||||
* @param size The pre-calculated total size to add
|
|
||||||
*/
|
|
||||||
inline void add_precalculated_size(uint32_t size) { total_size_ += size; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of a nested message field to the total message size
|
|
||||||
*
|
|
||||||
* This helper function directly updates the total_size reference if the nested size
|
|
||||||
* is greater than zero.
|
|
||||||
*
|
|
||||||
* @param nested_size The pre-calculated size of the nested message
|
|
||||||
*/
|
|
||||||
inline void add_message_field(uint32_t field_id_size, uint32_t nested_size) {
|
|
||||||
if (nested_size != 0) {
|
|
||||||
add_message_field_force(field_id_size, nested_size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_sfixed64(uint32_t field_id_size, int64_t value) {
|
||||||
/**
|
return value ? field_id_size + 8 : 0;
|
||||||
* @brief Calculates and adds the size of a nested message field to the total message size (force version)
|
|
||||||
*
|
|
||||||
* @param nested_size The pre-calculated size of the nested message
|
|
||||||
*/
|
|
||||||
inline void add_message_field_force(uint32_t field_id_size, uint32_t nested_size) {
|
|
||||||
// Always calculate size when force is true
|
|
||||||
// Field ID + length varint + nested message content
|
|
||||||
total_size_ += field_id_size + varint(nested_size) + nested_size;
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_message(uint32_t field_id_size, uint32_t nested_size) {
|
||||||
/**
|
return nested_size ? field_id_size + varint(nested_size) + nested_size : 0;
|
||||||
* @brief Calculates and adds the size of a nested message field to the total message size
|
|
||||||
*
|
|
||||||
* This version takes a ProtoMessage object, calculates its size internally,
|
|
||||||
* and updates the total_size reference. This eliminates the need for a temporary variable
|
|
||||||
* at the call site.
|
|
||||||
*
|
|
||||||
* @param message The nested message object
|
|
||||||
*/
|
|
||||||
inline void add_message_object(uint32_t field_id_size, const ProtoMessage &message) {
|
|
||||||
// Calculate nested message size by creating a temporary ProtoSize
|
|
||||||
ProtoSize nested_calc;
|
|
||||||
message.calculate_size(nested_calc);
|
|
||||||
uint32_t nested_size = nested_calc.get_size();
|
|
||||||
|
|
||||||
// Use the base implementation with the calculated nested_size
|
|
||||||
add_message_field(field_id_size, nested_size);
|
|
||||||
}
|
}
|
||||||
|
static constexpr uint32_t calc_message_force(uint32_t field_id_size, uint32_t nested_size) {
|
||||||
/**
|
return field_id_size + varint(nested_size) + nested_size;
|
||||||
* @brief Calculates and adds the size of a nested message field to the total message size (force version)
|
|
||||||
*
|
|
||||||
* @param message The nested message object
|
|
||||||
*/
|
|
||||||
inline void add_message_object_force(uint32_t field_id_size, const ProtoMessage &message) {
|
|
||||||
// Calculate nested message size by creating a temporary ProtoSize
|
|
||||||
ProtoSize nested_calc;
|
|
||||||
message.calculate_size(nested_calc);
|
|
||||||
uint32_t nested_size = nested_calc.get_size();
|
|
||||||
|
|
||||||
// Use the base implementation with the calculated nested_size
|
|
||||||
add_message_field_force(field_id_size, nested_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the sizes of all messages in a repeated field to the total message size
|
|
||||||
*
|
|
||||||
* This helper processes a vector of message objects, calculating the size for each message
|
|
||||||
* and adding it to the total size.
|
|
||||||
*
|
|
||||||
* @tparam MessageType The type of the nested messages in the vector
|
|
||||||
* @param messages Vector of message objects
|
|
||||||
*/
|
|
||||||
template<typename MessageType>
|
|
||||||
inline void add_repeated_message(uint32_t field_id_size, const std::vector<MessageType> &messages) {
|
|
||||||
// Skip if the vector is empty
|
|
||||||
if (!messages.empty()) {
|
|
||||||
// Use the force version for all messages in the repeated field
|
|
||||||
for (const auto &message : messages) {
|
|
||||||
add_message_object_force(field_id_size, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the sizes of all messages in a repeated field to the total message size (FixedVector
|
|
||||||
* version)
|
|
||||||
*
|
|
||||||
* @tparam MessageType The type of the nested messages in the FixedVector
|
|
||||||
* @param messages FixedVector of message objects
|
|
||||||
*/
|
|
||||||
template<typename MessageType>
|
|
||||||
inline void add_repeated_message(uint32_t field_id_size, const FixedVector<MessageType> &messages) {
|
|
||||||
// Skip if the fixed vector is empty
|
|
||||||
if (!messages.empty()) {
|
|
||||||
// Use the force version for all messages in the repeated field
|
|
||||||
for (const auto &message : messages) {
|
|
||||||
add_message_object_force(field_id_size, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate size of a packed repeated sint32 field
|
|
||||||
*/
|
|
||||||
inline void add_packed_sint32(uint32_t field_id_size, const std::vector<int32_t> &values) {
|
|
||||||
if (values.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
size_t packed_size = 0;
|
|
||||||
for (int value : values) {
|
|
||||||
packed_size += varint(encode_zigzag32(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
// field_id + length varint + packed data
|
|
||||||
total_size_ += field_id_size + varint(static_cast<uint32_t>(packed_size)) + static_cast<uint32_t>(packed_size);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Implementation of methods that depend on ProtoSize being fully defined
|
// Implementation of methods that depend on ProtoSize being fully defined
|
||||||
|
|
||||||
inline uint32_t ProtoMessage::calculated_size() const {
|
|
||||||
ProtoSize size;
|
|
||||||
this->calculate_size(size);
|
|
||||||
return size.get_size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation of encode_packed_sint32 - must be after ProtoSize is defined
|
// Implementation of encode_packed_sint32 - must be after ProtoSize is defined
|
||||||
inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values) {
|
inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values) {
|
||||||
if (values.empty())
|
if (values.empty())
|
||||||
@@ -949,31 +685,30 @@ inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of encode_message - must be after ProtoMessage is defined
|
// Encode thunk — converts void* back to concrete type for direct encode() call
|
||||||
inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value, bool force) {
|
template<typename T> void proto_encode_msg(const void *msg, ProtoWriteBuffer &buf) {
|
||||||
// Calculate the message size first
|
static_cast<const T *>(msg)->encode(buf);
|
||||||
ProtoSize msg_size;
|
}
|
||||||
value.calculate_size(msg_size);
|
|
||||||
uint32_t msg_length_bytes = msg_size.get_size();
|
|
||||||
|
|
||||||
// Skip empty singular messages (matches add_message_field which skips when nested_size == 0)
|
// Implementation of encode_message - must be after ProtoMessage is defined
|
||||||
// Repeated messages (force=true) are always encoded since an empty item is meaningful
|
template<typename T> inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const T &value, bool force) {
|
||||||
|
this->encode_message(field_id, value.calculate_size(), &value, &proto_encode_msg<T>, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-template core for encode_message
|
||||||
|
inline void ProtoWriteBuffer::encode_message(uint32_t field_id, uint32_t msg_length_bytes, const void *value,
|
||||||
|
void (*encode_fn)(const void *, ProtoWriteBuffer &), bool force) {
|
||||||
if (msg_length_bytes == 0 && !force)
|
if (msg_length_bytes == 0 && !force)
|
||||||
return;
|
return;
|
||||||
|
this->encode_field_raw(field_id, 2);
|
||||||
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
|
|
||||||
|
|
||||||
// Write the length varint directly through pos_
|
|
||||||
this->encode_varint_raw(msg_length_bytes);
|
this->encode_varint_raw(msg_length_bytes);
|
||||||
|
|
||||||
// Encode nested message - pos_ advances directly through the reference
|
|
||||||
#ifdef ESPHOME_DEBUG_API
|
#ifdef ESPHOME_DEBUG_API
|
||||||
uint8_t *start = this->pos_;
|
uint8_t *start = this->pos_;
|
||||||
value.encode(*this);
|
encode_fn(value, *this);
|
||||||
if (static_cast<uint32_t>(this->pos_ - start) != msg_length_bytes)
|
if (static_cast<uint32_t>(this->pos_ - start) != msg_length_bytes)
|
||||||
this->debug_check_encode_size_(field_id, msg_length_bytes, this->pos_ - start);
|
this->debug_check_encode_size_(field_id, msg_length_bytes, this->pos_ - start);
|
||||||
#else
|
#else
|
||||||
value.encode(*this);
|
encode_fn(value, *this);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -993,14 +728,6 @@ class ProtoService {
|
|||||||
virtual void on_no_setup_connection() = 0;
|
virtual void on_no_setup_connection() = 0;
|
||||||
virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0;
|
virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0;
|
||||||
virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0;
|
virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0;
|
||||||
/**
|
|
||||||
* Send a protobuf message by calculating its size, allocating a buffer, encoding, and sending.
|
|
||||||
* This is the implementation method - callers should use send_message() which adds logging.
|
|
||||||
* @param msg The protobuf message to send.
|
|
||||||
* @param message_type The message type identifier.
|
|
||||||
* @return True if the message was sent successfully, false otherwise.
|
|
||||||
*/
|
|
||||||
virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) = 0;
|
|
||||||
|
|
||||||
// Authentication helper methods
|
// Authentication helper methods
|
||||||
inline bool check_connection_setup_() {
|
inline bool check_connection_setup_() {
|
||||||
|
|||||||
@@ -183,10 +183,7 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
static constexpr size_t MAX_PACKET_SIZE = 1360;
|
static constexpr size_t MAX_PACKET_SIZE = 1360;
|
||||||
|
|
||||||
// Keep running total of actual message size
|
// Keep running total of actual message size
|
||||||
size_t current_size = 0;
|
size_t current_size = resp.calculate_size();
|
||||||
api::ProtoSize size;
|
|
||||||
resp.calculate_size(size);
|
|
||||||
current_size = size.get_size();
|
|
||||||
|
|
||||||
while (this->send_service_ < this->service_count_) {
|
while (this->send_service_ < this->service_count_) {
|
||||||
esp_gattc_service_elem_t service_result;
|
esp_gattc_service_elem_t service_result;
|
||||||
@@ -302,9 +299,7 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
} // end if (total_char_count > 0)
|
} // end if (total_char_count > 0)
|
||||||
|
|
||||||
// Calculate the actual size of just this service
|
// Calculate the actual size of just this service
|
||||||
api::ProtoSize service_sizer;
|
size_t service_size = service_resp.calculate_size() + 1; // +1 for field tag
|
||||||
service_resp.calculate_size(service_sizer);
|
|
||||||
size_t service_size = service_sizer.get_size() + 1; // +1 for field tag
|
|
||||||
|
|
||||||
// Check if adding this service would exceed the limit
|
// Check if adding this service would exceed the limit
|
||||||
if (current_size + service_size > MAX_PACKET_SIZE) {
|
if (current_size + service_size > MAX_PACKET_SIZE) {
|
||||||
@@ -333,7 +328,7 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the message with dynamically batched services
|
// Send the message with dynamically batched services
|
||||||
api_conn->send_message(resp, api::BluetoothGATTGetServicesResponse::MESSAGE_TYPE);
|
api_conn->send_message(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothConnection::log_connection_error_(const char *operation, esp_gatt_status_t status) {
|
void BluetoothConnection::log_connection_error_(const char *operation, esp_gatt_status_t status) {
|
||||||
@@ -422,7 +417,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
|||||||
resp.address = this->address_;
|
resp.address = this->address_;
|
||||||
resp.handle = param->read.handle;
|
resp.handle = param->read.handle;
|
||||||
resp.set_data(param->read.value, param->read.value_len);
|
resp.set_data(param->read.value, param->read.value_len);
|
||||||
api_connection->send_message(resp, api::BluetoothGATTReadResponse::MESSAGE_TYPE);
|
api_connection->send_message(resp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_WRITE_CHAR_EVT:
|
case ESP_GATTC_WRITE_CHAR_EVT:
|
||||||
@@ -438,7 +433,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
|||||||
api::BluetoothGATTWriteResponse resp;
|
api::BluetoothGATTWriteResponse resp;
|
||||||
resp.address = this->address_;
|
resp.address = this->address_;
|
||||||
resp.handle = param->write.handle;
|
resp.handle = param->write.handle;
|
||||||
api_connection->send_message(resp, api::BluetoothGATTWriteResponse::MESSAGE_TYPE);
|
api_connection->send_message(resp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
|
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
|
||||||
@@ -454,7 +449,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
|||||||
api::BluetoothGATTNotifyResponse resp;
|
api::BluetoothGATTNotifyResponse resp;
|
||||||
resp.address = this->address_;
|
resp.address = this->address_;
|
||||||
resp.handle = param->unreg_for_notify.handle;
|
resp.handle = param->unreg_for_notify.handle;
|
||||||
api_connection->send_message(resp, api::BluetoothGATTNotifyResponse::MESSAGE_TYPE);
|
api_connection->send_message(resp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
@@ -470,7 +465,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
|||||||
api::BluetoothGATTNotifyResponse resp;
|
api::BluetoothGATTNotifyResponse resp;
|
||||||
resp.address = this->address_;
|
resp.address = this->address_;
|
||||||
resp.handle = param->reg_for_notify.handle;
|
resp.handle = param->reg_for_notify.handle;
|
||||||
api_connection->send_message(resp, api::BluetoothGATTNotifyResponse::MESSAGE_TYPE);
|
api_connection->send_message(resp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_NOTIFY_EVT: {
|
case ESP_GATTC_NOTIFY_EVT: {
|
||||||
@@ -483,7 +478,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
|||||||
resp.address = this->address_;
|
resp.address = this->address_;
|
||||||
resp.handle = param->notify.handle;
|
resp.handle = param->notify.handle;
|
||||||
resp.set_data(param->notify.value, param->notify.value_len);
|
resp.set_data(param->notify.value, param->notify.value_len);
|
||||||
api_connection->send_message(resp, api::BluetoothGATTNotifyDataResponse::MESSAGE_TYPE);
|
api_connection->send_message(resp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta
|
|||||||
resp.configured_mode = this->configured_scan_active_
|
resp.configured_mode = this->configured_scan_active_
|
||||||
? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
|
? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
|
||||||
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
|
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
|
||||||
this->api_connection_->send_message(resp, api::BluetoothScannerStateResponse::MESSAGE_TYPE);
|
this->api_connection_->send_message(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothProxy::log_connection_request_ignored_(BluetoothConnection *connection, espbt::ClientState state) {
|
void BluetoothProxy::log_connection_request_ignored_(BluetoothConnection *connection, espbt::ClientState state) {
|
||||||
@@ -112,7 +112,7 @@ void BluetoothProxy::flush_pending_advertisements() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Send the message
|
// Send the message
|
||||||
this->api_connection_->send_message(this->response_, api::BluetoothLERawAdvertisementsResponse::MESSAGE_TYPE);
|
this->api_connection_->send_message(this->response_);
|
||||||
|
|
||||||
ESP_LOGV(TAG, "Sent batch of %u BLE advertisements", this->response_.advertisements_len);
|
ESP_LOGV(TAG, "Sent batch of %u BLE advertisements", this->response_.advertisements_len);
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
|
|||||||
call.success = ret == ESP_OK;
|
call.success = ret == ESP_OK;
|
||||||
call.error = ret;
|
call.error = ret;
|
||||||
|
|
||||||
this->api_connection_->send_message(call, api::BluetoothDeviceClearCacheResponse::MESSAGE_TYPE);
|
this->api_connection_->send_message(call);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -389,7 +389,7 @@ void BluetoothProxy::send_device_connection(uint64_t address, bool connected, ui
|
|||||||
call.connected = connected;
|
call.connected = connected;
|
||||||
call.mtu = mtu;
|
call.mtu = mtu;
|
||||||
call.error = error;
|
call.error = error;
|
||||||
this->api_connection_->send_message(call, api::BluetoothDeviceConnectionResponse::MESSAGE_TYPE);
|
this->api_connection_->send_message(call);
|
||||||
}
|
}
|
||||||
void BluetoothProxy::send_connections_free() {
|
void BluetoothProxy::send_connections_free() {
|
||||||
if (this->api_connection_ != nullptr) {
|
if (this->api_connection_ != nullptr) {
|
||||||
@@ -398,7 +398,7 @@ void BluetoothProxy::send_connections_free() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothProxy::send_connections_free(api::APIConnection *api_connection) {
|
void BluetoothProxy::send_connections_free(api::APIConnection *api_connection) {
|
||||||
api_connection->send_message(this->connections_free_response_, api::BluetoothConnectionsFreeResponse::MESSAGE_TYPE);
|
api_connection->send_message(this->connections_free_response_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothProxy::send_gatt_services_done(uint64_t address) {
|
void BluetoothProxy::send_gatt_services_done(uint64_t address) {
|
||||||
@@ -406,7 +406,7 @@ void BluetoothProxy::send_gatt_services_done(uint64_t address) {
|
|||||||
return;
|
return;
|
||||||
api::BluetoothGATTGetServicesDoneResponse call;
|
api::BluetoothGATTGetServicesDoneResponse call;
|
||||||
call.address = address;
|
call.address = address;
|
||||||
this->api_connection_->send_message(call, api::BluetoothGATTGetServicesDoneResponse::MESSAGE_TYPE);
|
this->api_connection_->send_message(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
|
void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
|
||||||
@@ -416,7 +416,7 @@ void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_
|
|||||||
call.address = address;
|
call.address = address;
|
||||||
call.handle = handle;
|
call.handle = handle;
|
||||||
call.error = error;
|
call.error = error;
|
||||||
this->api_connection_->send_message(call, api::BluetoothGATTWriteResponse::MESSAGE_TYPE);
|
this->api_connection_->send_message(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_t error) {
|
void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_t error) {
|
||||||
@@ -427,7 +427,7 @@ void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_
|
|||||||
call.paired = paired;
|
call.paired = paired;
|
||||||
call.error = error;
|
call.error = error;
|
||||||
|
|
||||||
this->api_connection_->send_message(call, api::BluetoothDevicePairingResponse::MESSAGE_TYPE);
|
this->api_connection_->send_message(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_err_t error) {
|
void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_err_t error) {
|
||||||
@@ -438,7 +438,7 @@ void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_e
|
|||||||
call.success = success;
|
call.success = success;
|
||||||
call.error = error;
|
call.error = error;
|
||||||
|
|
||||||
this->api_connection_->send_message(call, api::BluetoothDeviceUnpairingResponse::MESSAGE_TYPE);
|
this->api_connection_->send_message(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothProxy::bluetooth_scanner_set_mode(bool active) {
|
void BluetoothProxy::bluetooth_scanner_set_mode(bool active) {
|
||||||
|
|||||||
@@ -251,8 +251,7 @@ void VoiceAssistant::loop() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (this->api_client_ == nullptr ||
|
if (this->api_client_ == nullptr || !this->api_client_->send_message(msg)) {
|
||||||
!this->api_client_->send_message(msg, api::VoiceAssistantRequest::MESSAGE_TYPE)) {
|
|
||||||
ESP_LOGW(TAG, "Could not request start");
|
ESP_LOGW(TAG, "Could not request start");
|
||||||
this->error_trigger_.trigger("not-connected", "Could not request start");
|
this->error_trigger_.trigger("not-connected", "Could not request start");
|
||||||
this->continuous_ = false;
|
this->continuous_ = false;
|
||||||
@@ -275,7 +274,7 @@ void VoiceAssistant::loop() {
|
|||||||
api::VoiceAssistantAudio msg;
|
api::VoiceAssistantAudio msg;
|
||||||
msg.data = this->send_buffer_;
|
msg.data = this->send_buffer_;
|
||||||
msg.data_len = read_bytes;
|
msg.data_len = read_bytes;
|
||||||
this->api_client_->send_message(msg, api::VoiceAssistantAudio::MESSAGE_TYPE);
|
this->api_client_->send_message(msg);
|
||||||
} else {
|
} else {
|
||||||
if (!this->udp_socket_running_) {
|
if (!this->udp_socket_running_) {
|
||||||
if (!this->start_udp_socket_()) {
|
if (!this->start_udp_socket_()) {
|
||||||
@@ -354,7 +353,7 @@ void VoiceAssistant::loop() {
|
|||||||
|
|
||||||
api::VoiceAssistantAnnounceFinished msg;
|
api::VoiceAssistantAnnounceFinished msg;
|
||||||
msg.success = true;
|
msg.success = true;
|
||||||
this->api_client_->send_message(msg, api::VoiceAssistantAnnounceFinished::MESSAGE_TYPE);
|
this->api_client_->send_message(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -612,7 +611,7 @@ void VoiceAssistant::signal_stop_() {
|
|||||||
ESP_LOGD(TAG, "Signaling stop");
|
ESP_LOGD(TAG, "Signaling stop");
|
||||||
api::VoiceAssistantRequest msg;
|
api::VoiceAssistantRequest msg;
|
||||||
msg.start = false;
|
msg.start = false;
|
||||||
this->api_client_->send_message(msg, api::VoiceAssistantRequest::MESSAGE_TYPE);
|
this->api_client_->send_message(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoiceAssistant::start_playback_timeout_() {
|
void VoiceAssistant::start_playback_timeout_() {
|
||||||
@@ -622,7 +621,7 @@ void VoiceAssistant::start_playback_timeout_() {
|
|||||||
|
|
||||||
api::VoiceAssistantAnnounceFinished msg;
|
api::VoiceAssistantAnnounceFinished msg;
|
||||||
msg.success = true;
|
msg.success = true;
|
||||||
this->api_client_->send_message(msg, api::VoiceAssistantAnnounceFinished::MESSAGE_TYPE);
|
this->api_client_->send_message(msg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ void ZWaveProxy::process_uart_() {
|
|||||||
// If this is a data frame, use frame length indicator + 2 (for SoF + checksum), else assume 1 for ACK/NAK/CAN
|
// If this is a data frame, use frame length indicator + 2 (for SoF + checksum), else assume 1 for ACK/NAK/CAN
|
||||||
this->outgoing_proto_msg_.data_len = this->buffer_[0] == ZWAVE_FRAME_TYPE_START ? this->buffer_[1] + 2 : 1;
|
this->outgoing_proto_msg_.data_len = this->buffer_[0] == ZWAVE_FRAME_TYPE_START ? this->buffer_[1] + 2 : 1;
|
||||||
}
|
}
|
||||||
this->api_connection_->send_message(this->outgoing_proto_msg_, api::ZWaveProxyFrame::MESSAGE_TYPE);
|
this->api_connection_->send_message(this->outgoing_proto_msg_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,7 +209,7 @@ void ZWaveProxy::send_homeid_changed_msg_(api::APIConnection *conn) {
|
|||||||
msg.data_len = this->home_id_.size();
|
msg.data_len = this->home_id_.size();
|
||||||
if (conn != nullptr) {
|
if (conn != nullptr) {
|
||||||
// Send to specific connection
|
// Send to specific connection
|
||||||
conn->send_message(msg, api::ZWaveProxyRequest::MESSAGE_TYPE);
|
conn->send_message(msg);
|
||||||
} else if (api::global_api_server != nullptr) {
|
} else if (api::global_api_server != nullptr) {
|
||||||
// We could add code to manage a second subscription type, but, since this message is
|
// We could add code to manage a second subscription type, but, since this message is
|
||||||
// very infrequent and small, we simply send it to all clients
|
// very infrequent and small, we simply send it to all clients
|
||||||
@@ -346,7 +346,7 @@ void ZWaveProxy::parse_start_(uint8_t byte) {
|
|||||||
this->buffer_[0] = byte;
|
this->buffer_[0] = byte;
|
||||||
this->outgoing_proto_msg_.data = this->buffer_.data();
|
this->outgoing_proto_msg_.data = this->buffer_.data();
|
||||||
this->outgoing_proto_msg_.data_len = 1;
|
this->outgoing_proto_msg_.data_len = 1;
|
||||||
this->api_connection_->send_message(this->outgoing_proto_msg_, api::ZWaveProxyFrame::MESSAGE_TYPE);
|
this->api_connection_->send_message(this->outgoing_proto_msg_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -270,18 +270,21 @@ class TypeInfo(ABC):
|
|||||||
def _get_simple_size_calculation(
|
def _get_simple_size_calculation(
|
||||||
self, name: str, force: bool, base_method: str, value_expr: str = None
|
self, name: str, force: bool, base_method: str, value_expr: str = None
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Helper for simple size calculations.
|
"""Helper for simple size calculations using static ProtoSize methods.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: Field name
|
name: Field name
|
||||||
force: Whether this is for a repeated field
|
force: Whether this is for a repeated field
|
||||||
base_method: Base method name (e.g., "add_int32")
|
base_method: Base method name (e.g., "int32")
|
||||||
value_expr: Optional value expression (defaults to name)
|
value_expr: Optional value expression (defaults to name)
|
||||||
"""
|
"""
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
method = f"{base_method}_force" if force else base_method
|
method = f"calc_{base_method}_force" if force else f"calc_{base_method}"
|
||||||
|
# calc_bool_force only takes field_id_size (no value needed - bool is always 1 byte)
|
||||||
|
if base_method == "bool" and force:
|
||||||
|
return f"size += ProtoSize::{method}({field_id_size});"
|
||||||
value = value_expr or name
|
value = value_expr or name
|
||||||
return f"size.{method}({field_id_size}, {value});"
|
return f"size += ProtoSize::{method}({field_id_size}, {value});"
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
@@ -410,7 +413,7 @@ class DoubleType(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"size.add_double({field_id_size}, {name});"
|
return f"size += ProtoSize::calc_fixed64({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 8
|
return 8
|
||||||
@@ -434,7 +437,7 @@ class FloatType(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"size.add_float({field_id_size}, {name});"
|
return f"size += ProtoSize::calc_float({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 4
|
return 4
|
||||||
@@ -457,7 +460,7 @@ class Int64Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_int64")
|
return self._get_simple_size_calculation(name, force, "int64")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -477,7 +480,7 @@ class UInt64Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_uint64")
|
return self._get_simple_size_calculation(name, force, "uint64")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -497,7 +500,7 @@ class Int32Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_int32")
|
return self._get_simple_size_calculation(name, force, "int32")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -518,7 +521,7 @@ class Fixed64Type(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"size.add_fixed64({field_id_size}, {name});"
|
return f"size += ProtoSize::calc_fixed64({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 8
|
return 8
|
||||||
@@ -542,7 +545,7 @@ class Fixed32Type(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"size.add_fixed32({field_id_size}, {name});"
|
return f"size += ProtoSize::calc_fixed32({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 4
|
return 4
|
||||||
@@ -563,7 +566,7 @@ class BoolType(TypeInfo):
|
|||||||
return f"out.append(YESNO({name}));"
|
return f"out.append(YESNO({name}));"
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_bool")
|
return self._get_simple_size_calculation(name, force, "bool")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 1 # field ID + 1 byte
|
return self.calculate_field_id_size() + 1 # field ID + 1 byte
|
||||||
@@ -647,18 +650,18 @@ class StringType(TypeInfo):
|
|||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
# For SOURCE_CLIENT only messages, use the string field directly
|
# For SOURCE_CLIENT only messages, use the string field directly
|
||||||
if not self._needs_encode:
|
if not self._needs_encode:
|
||||||
return self._get_simple_size_calculation(name, force, "add_length")
|
return self._get_simple_size_calculation(name, force, "length")
|
||||||
|
|
||||||
# Check if this is being called from a repeated field context
|
# Check if this is being called from a repeated field context
|
||||||
# In that case, 'name' will be 'it' and we need to use the repeated version
|
# In that case, 'name' will be 'it' and we need to use the repeated version
|
||||||
if name == "it":
|
if name == "it":
|
||||||
# For repeated fields, we need to use add_length_force which includes field ID
|
# For repeated fields, we need to use length_force which includes field ID
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"size.add_length_force({field_id_size}, it.size());"
|
return f"size += ProtoSize::calc_length_force({field_id_size}, it.size());"
|
||||||
|
|
||||||
# For messages that need encoding, use the StringRef size
|
# For messages that need encoding, use the StringRef size
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"size.add_length({field_id_size}, this->{self.field_name}_ref_.size());"
|
return f"size += ProtoSize::calc_length({field_id_size}, this->{self.field_name}_ref_.size());"
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string
|
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string
|
||||||
@@ -721,7 +724,9 @@ class MessageType(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_message_object")
|
field_id_size = self.calculate_field_id_size()
|
||||||
|
method = "calc_message_force" if force else "calc_message"
|
||||||
|
return f"size += ProtoSize::{method}({field_id_size}, {name}.calculate_size());"
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
# For message types, we can't easily estimate the submessage size without
|
# For message types, we can't easily estimate the submessage size without
|
||||||
@@ -822,7 +827,7 @@ class BytesType(TypeInfo):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return f"size.add_length({self.calculate_field_id_size()}, this->{self.field_name}_len_);"
|
return f"size += ProtoSize::calc_length({self.calculate_field_id_size()}, this->{self.field_name}_len_);"
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes
|
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes
|
||||||
@@ -897,7 +902,7 @@ class PointerToBytesBufferType(PointerToBufferTypeBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return f"size.add_length({self.calculate_field_id_size()}, this->{self.field_name}_len);"
|
return f"size += ProtoSize::calc_length({self.calculate_field_id_size()}, this->{self.field_name}_len);"
|
||||||
|
|
||||||
|
|
||||||
class PointerToStringBufferType(PointerToBufferTypeBase):
|
class PointerToStringBufferType(PointerToBufferTypeBase):
|
||||||
@@ -939,7 +944,7 @@ class PointerToStringBufferType(PointerToBufferTypeBase):
|
|||||||
return f'dump_field(out, "{self.name}", this->{self.field_name});'
|
return f'dump_field(out, "{self.name}", this->{self.field_name});'
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return f"size.add_length({self.calculate_field_id_size()}, this->{self.field_name}.size());"
|
return f"size += ProtoSize::calc_length({self.calculate_field_id_size()}, this->{self.field_name}.size());"
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string
|
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string
|
||||||
@@ -1103,9 +1108,9 @@ class FixedArrayBytesType(TypeInfo):
|
|||||||
|
|
||||||
if force:
|
if force:
|
||||||
# For repeated fields, always calculate size (no zero check)
|
# For repeated fields, always calculate size (no zero check)
|
||||||
return f"size.add_length_force({field_id_size}, {length_field});"
|
return f"size += ProtoSize::calc_length_force({field_id_size}, {length_field});"
|
||||||
# For non-repeated fields, add_length already checks for zero
|
# For non-repeated fields, length already checks for zero
|
||||||
return f"size.add_length({field_id_size}, {length_field});"
|
return f"size += ProtoSize::calc_length({field_id_size}, {length_field});"
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
# Estimate based on typical BLE advertisement size
|
# Estimate based on typical BLE advertisement size
|
||||||
@@ -1132,7 +1137,7 @@ class UInt32Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_uint32")
|
return self._get_simple_size_calculation(name, force, "uint32")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -1168,7 +1173,7 @@ class EnumType(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(
|
return self._get_simple_size_calculation(
|
||||||
name, force, "add_uint32", f"static_cast<uint32_t>({name})"
|
name, force, "uint32", f"static_cast<uint32_t>({name})"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
@@ -1190,7 +1195,7 @@ class SFixed32Type(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"size.add_sfixed32({field_id_size}, {name});"
|
return f"size += ProtoSize::calc_sfixed32({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 4
|
return 4
|
||||||
@@ -1214,7 +1219,7 @@ class SFixed64Type(TypeInfo):
|
|||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
return f"size.add_sfixed64({field_id_size}, {name});"
|
return f"size += ProtoSize::calc_sfixed64({field_id_size}, {name});"
|
||||||
|
|
||||||
def get_fixed_size_bytes(self) -> int:
|
def get_fixed_size_bytes(self) -> int:
|
||||||
return 8
|
return 8
|
||||||
@@ -1237,7 +1242,7 @@ class SInt32Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_sint32")
|
return self._get_simple_size_calculation(name, force, "sint32")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -1257,7 +1262,7 @@ class SInt64Type(TypeInfo):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_sint64")
|
return self._get_simple_size_calculation(name, force, "sint64")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
@@ -1694,11 +1699,17 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
# For repeated fields, we always need to pass force=True to the underlying type's calculation
|
# For repeated fields, we always need to pass force=True to the underlying type's calculation
|
||||||
# This is because the encode method always sets force=true for repeated fields
|
# This is because the encode method always sets force=true for repeated fields
|
||||||
|
|
||||||
# Handle message types separately as they use a dedicated helper
|
# Handle message types separately - generate inline loop
|
||||||
if isinstance(self._ti, MessageType):
|
if isinstance(self._ti, MessageType):
|
||||||
field_id_size = self._ti.calculate_field_id_size()
|
field_id_size = self._ti.calculate_field_id_size()
|
||||||
container = f"*{name}" if self._use_pointer else name
|
container_ref = f"*{name}" if self._use_pointer else name
|
||||||
return f"size.add_repeated_message({field_id_size}, {container});"
|
empty_check = f"{name}->empty()" if self._use_pointer else f"{name}.empty()"
|
||||||
|
o = f"if (!{empty_check}) {{\n"
|
||||||
|
o += f" for (const auto &it : {container_ref}) {{\n"
|
||||||
|
o += f" size += ProtoSize::calc_message_force({field_id_size}, it.calculate_size());\n"
|
||||||
|
o += " }\n"
|
||||||
|
o += "}"
|
||||||
|
return o
|
||||||
|
|
||||||
# For non-message types, generate size calculation with iteration
|
# For non-message types, generate size calculation with iteration
|
||||||
container_ref = f"*{name}" if self._use_pointer else name
|
container_ref = f"*{name}" if self._use_pointer else name
|
||||||
@@ -1713,14 +1724,14 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
field_id_size = self._ti.calculate_field_id_size()
|
field_id_size = self._ti.calculate_field_id_size()
|
||||||
bytes_per_element = field_id_size + num_bytes
|
bytes_per_element = field_id_size + num_bytes
|
||||||
size_expr = f"{name}->size()" if self._use_pointer else f"{name}.size()"
|
size_expr = f"{name}->size()" if self._use_pointer else f"{name}.size()"
|
||||||
o += f" size.add_precalculated_size({size_expr} * {bytes_per_element});\n"
|
o += f" size += {size_expr} * {bytes_per_element};\n"
|
||||||
else:
|
else:
|
||||||
# Other types need the actual value
|
# Other types need the actual value
|
||||||
# Special handling for const char* elements
|
# Special handling for const char* elements
|
||||||
if self._use_pointer and "const char" in self._container_no_template:
|
if self._use_pointer and "const char" in self._container_no_template:
|
||||||
field_id_size = self.calculate_field_id_size()
|
field_id_size = self.calculate_field_id_size()
|
||||||
o += f" for (const char *it : {container_ref}) {{\n"
|
o += f" for (const char *it : {container_ref}) {{\n"
|
||||||
o += f" size.add_length_force({field_id_size}, strlen(it));\n"
|
o += f" size += ProtoSize::calc_length_force({field_id_size}, strlen(it));\n"
|
||||||
else:
|
else:
|
||||||
auto_ref = "" if self._ti_is_bool else "&"
|
auto_ref = "" if self._ti_is_bool else "&"
|
||||||
o += f" for (const auto {auto_ref}it : {container_ref}) {{\n"
|
o += f" for (const auto {auto_ref}it : {container_ref}) {{\n"
|
||||||
@@ -2233,23 +2244,19 @@ def build_message_type(
|
|||||||
o += indent("\n".join(encode)) + "\n"
|
o += indent("\n".join(encode)) + "\n"
|
||||||
o += "}\n"
|
o += "}\n"
|
||||||
cpp += o
|
cpp += o
|
||||||
prot = "void encode(ProtoWriteBuffer &buffer) const override;"
|
prot = "void encode(ProtoWriteBuffer &buffer) const;"
|
||||||
public_content.append(prot)
|
public_content.append(prot)
|
||||||
# If no fields to encode or message doesn't need encoding, the default implementation in ProtoMessage will be used
|
# If no fields to encode or message doesn't need encoding, the default implementation in ProtoMessage will be used
|
||||||
|
|
||||||
# Add calculate_size method only if this message needs encoding and has fields
|
# Add calculate_size method only if this message needs encoding and has fields
|
||||||
if needs_encode and size_calc:
|
if needs_encode and size_calc:
|
||||||
o = f"void {desc.name}::calculate_size(ProtoSize &size) const {{"
|
o = f"uint32_t {desc.name}::calculate_size() const {{\n"
|
||||||
# For a single field, just inline it for simplicity
|
o += " uint32_t size = 0;\n"
|
||||||
if len(size_calc) == 1 and len(size_calc[0]) + len(o) + 3 < 120:
|
o += indent("\n".join(size_calc)) + "\n"
|
||||||
o += f" {size_calc[0]} }}\n"
|
o += " return size;\n"
|
||||||
else:
|
o += "}\n"
|
||||||
# For multiple fields
|
|
||||||
o += "\n"
|
|
||||||
o += indent("\n".join(size_calc)) + "\n"
|
|
||||||
o += "}\n"
|
|
||||||
cpp += o
|
cpp += o
|
||||||
prot = "void calculate_size(ProtoSize &size) const override;"
|
prot = "uint32_t calculate_size() const;"
|
||||||
public_content.append(prot)
|
public_content.append(prot)
|
||||||
# If no fields to calculate size for or message doesn't need encoding, the default implementation in ProtoMessage will be used
|
# If no fields to calculate size for or message doesn't need encoding, the default implementation in ProtoMessage will be used
|
||||||
|
|
||||||
@@ -2933,14 +2940,8 @@ static const char *const TAG = "api.service";
|
|||||||
hpp += " public:\n"
|
hpp += " public:\n"
|
||||||
hpp += "#endif\n\n"
|
hpp += "#endif\n\n"
|
||||||
|
|
||||||
# Add non-template send_message method
|
# send_message is now a template on APIConnection directly
|
||||||
hpp += " bool send_message(const ProtoMessage &msg, uint8_t message_type) {\n"
|
# No non-template send_message method needed here
|
||||||
hpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n"
|
|
||||||
hpp += " DumpBuffer dump_buf;\n"
|
|
||||||
hpp += " this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));\n"
|
|
||||||
hpp += "#endif\n"
|
|
||||||
hpp += " return this->send_message_impl(msg, message_type);\n"
|
|
||||||
hpp += " }\n\n"
|
|
||||||
|
|
||||||
# Add logging helper method implementations to cpp
|
# Add logging helper method implementations to cpp
|
||||||
cpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n"
|
cpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user