171 lines
4.3 KiB
Erlang
171 lines
4.3 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2022-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
%%
|
|
%% 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.
|
|
%%--------------------------------------------------------------------
|
|
|
|
-module(emqx_ocpp_frame).
|
|
|
|
-behaviour(emqx_gateway_frame).
|
|
|
|
-include("emqx_ocpp.hrl").
|
|
|
|
%% emqx_gateway_frame callbacks
|
|
-export([
|
|
initial_parse_state/1,
|
|
serialize_opts/0,
|
|
serialize_pkt/2,
|
|
parse/2,
|
|
format/1,
|
|
type/1,
|
|
is_message/1
|
|
]).
|
|
|
|
-type parse_state() :: map().
|
|
|
|
-type parse_result() ::
|
|
{ok, frame(), Rest :: binary(), NewState :: parse_state()}.
|
|
|
|
-export_type([
|
|
parse_state/0,
|
|
parse_result/0,
|
|
frame/0,
|
|
serialize_options/0
|
|
]).
|
|
|
|
-type serialize_options() :: emqx_gateway_frame:serialize_options().
|
|
|
|
-dialyzer({nowarn_function, [format/1]}).
|
|
|
|
-spec initial_parse_state(map()) -> parse_state().
|
|
initial_parse_state(_Opts) ->
|
|
#{}.
|
|
|
|
%% No-TCP-Spliting
|
|
|
|
-spec parse(binary() | list(), parse_state()) -> parse_result().
|
|
parse(Bin, Parser) when is_binary(Bin) ->
|
|
case emqx_utils_json:safe_decode(Bin, [return_maps]) of
|
|
{ok, Json} ->
|
|
parse(Json, Parser);
|
|
{error, {Position, Reason}} ->
|
|
error(
|
|
{badjson, io_lib:format("Invalid json at ~w: ~s", [Position, Reason])}
|
|
);
|
|
{error, Reason} ->
|
|
error(
|
|
{badjson, io_lib:format("Invalid json: ~p", [Reason])}
|
|
)
|
|
end;
|
|
%% CALL
|
|
parse([?OCPP_MSG_TYPE_ID_CALL, Id, Action, Payload], Parser) ->
|
|
Frame = #{
|
|
type => ?OCPP_MSG_TYPE_ID_CALL,
|
|
id => Id,
|
|
action => Action,
|
|
payload => Payload
|
|
},
|
|
case emqx_ocpp_schemas:validate(upstream, Frame) of
|
|
ok ->
|
|
{ok, Frame, <<>>, Parser};
|
|
{error, ReasonStr} ->
|
|
error({validation_faliure, Id, ReasonStr})
|
|
end;
|
|
%% CALLRESULT
|
|
parse([?OCPP_MSG_TYPE_ID_CALLRESULT, Id, Payload], Parser) ->
|
|
Frame = #{
|
|
type => ?OCPP_MSG_TYPE_ID_CALLRESULT,
|
|
id => Id,
|
|
payload => Payload
|
|
},
|
|
%% TODO: Validate CALLRESULT frame
|
|
%%case emqx_ocpp_schemas:validate(upstream, Frame) of
|
|
%% ok ->
|
|
%% {ok, Frame, <<>>, Parser};
|
|
%% {error, ReasonStr} ->
|
|
%% error({validation_faliure, Id, ReasonStr})
|
|
%%end;
|
|
{ok, Frame, <<>>, Parser};
|
|
%% CALLERROR
|
|
parse(
|
|
[
|
|
?OCPP_MSG_TYPE_ID_CALLERROR,
|
|
Id,
|
|
ErrCode,
|
|
ErrDesc,
|
|
ErrDetails
|
|
],
|
|
Parser
|
|
) ->
|
|
{ok,
|
|
#{
|
|
type => ?OCPP_MSG_TYPE_ID_CALLERROR,
|
|
id => Id,
|
|
error_code => ErrCode,
|
|
error_desc => ErrDesc,
|
|
error_details => ErrDetails
|
|
},
|
|
<<>>, Parser}.
|
|
|
|
-spec serialize_opts() -> serialize_options().
|
|
serialize_opts() ->
|
|
#{}.
|
|
|
|
-spec serialize_pkt(frame(), emqx_gateway_frame:serialize_options()) -> iodata().
|
|
serialize_pkt(
|
|
#{
|
|
id := Id,
|
|
type := ?OCPP_MSG_TYPE_ID_CALL,
|
|
action := Action,
|
|
payload := Payload
|
|
},
|
|
_Opts
|
|
) ->
|
|
emqx_utils_json:encode([?OCPP_MSG_TYPE_ID_CALL, Id, Action, Payload]);
|
|
serialize_pkt(
|
|
#{
|
|
id := Id,
|
|
type := ?OCPP_MSG_TYPE_ID_CALLRESULT,
|
|
payload := Payload
|
|
},
|
|
_Opts
|
|
) ->
|
|
emqx_utils_json:encode([?OCPP_MSG_TYPE_ID_CALLRESULT, Id, Payload]);
|
|
serialize_pkt(
|
|
#{
|
|
id := Id,
|
|
type := Type,
|
|
error_code := ErrCode,
|
|
error_desc := ErrDesc
|
|
} = Frame,
|
|
_Opts
|
|
) when
|
|
Type == ?OCPP_MSG_TYPE_ID_CALLERROR
|
|
->
|
|
ErrDetails = maps:get(error_details, Frame, #{}),
|
|
emqx_utils_json:encode([Type, Id, ErrCode, ErrDesc, ErrDetails]).
|
|
|
|
-spec format(frame()) -> string().
|
|
format(Frame) ->
|
|
serialize_pkt(Frame, #{}).
|
|
|
|
-spec type(frame()) -> atom().
|
|
type(_Frame) ->
|
|
%% TODO:
|
|
todo.
|
|
|
|
-spec is_message(frame()) -> boolean().
|
|
is_message(_Frame) ->
|
|
%% TODO:
|
|
true.
|