%%%------------------------------------------------------------------- %%% @doc %%% Protocol message encoder for Macula mesh. %%% Encodes message maps to binary wire format. %%% %%% Frame Format (8-byte header + payload): %%% - Version (1 byte): Protocol version (currently 0x01) %%% - Type (1 byte): Message type ID %%% - Flags (1 byte): Reserved for future use (0x00) %%% - Reserved (1 byte): Must be 0x00 %%% - Payload Length (4 bytes): Big-endian uint32 %%% - Payload (N bytes): MessagePack-encoded message data %%% @end %%%------------------------------------------------------------------- -module(macula_protocol_encoder). -export([encode/2]). %%%=================================================================== %%% Constants %%%=================================================================== -define(PROTOCOL_VERSION, 1). -define(FLAGS_NONE, 0). -define(RESERVED, 0). %%%=================================================================== %%% API Functions %%%=================================================================== %% @doc Encode a message to binary format. %% Returns a binary with 8-byte header + MessagePack payload. %% @end -spec encode(macula_protocol_types:message_type(), map()) -> binary(). encode(Type, Msg) when is_atom(Type), is_map(Msg) -> %% Validate message has required fields validate_message(Type, Msg), %% Get message type ID TypeId = macula_protocol_types:message_type_id(Type), %% Encode payload with MessagePack Payload = msgpack:pack(Msg, [{map_format, map}]), %% Build frame: 8-byte header + payload PayloadLen = byte_size(Payload), <>. %%%=================================================================== %%% Internal Functions %%%=================================================================== %% @doc Validate message contains required fields for its type. %% Crashes with badarg if validation fails. %% @end -spec validate_message(atom(), map()) -> ok. validate_message(connect, Msg) -> #{version := _, node_id := _, realm_id := _, capabilities := _} = Msg, ok; validate_message(disconnect, Msg) -> #{reason := _, message := _} = Msg, ok; validate_message(ping, Msg) -> #{timestamp := _} = Msg, ok; validate_message(pong, Msg) -> #{timestamp := _, server_time := _} = Msg, ok; validate_message(publish, Msg) -> #{topic := _, payload := _, qos := _, retain := _, message_id := _} = Msg, ok; validate_message(subscribe, Msg) -> #{topics := _, qos := _} = Msg, ok; validate_message(unsubscribe, Msg) -> #{topics := _} = Msg, ok; validate_message(_Type, _Msg) -> %% For message types not yet validated, allow anything ok.