summaryrefslogtreecommitdiff
path: root/components/dlink/src/dlink_data_rvi.erl
blob: 01131bea9cc57ec0077eaa7b2a5e2f0992c46774 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
-module(dlink_data_rvi).

-compile(export_all).

-record(dlink_data_rvi, {need, buf}).

-define(MAX_LINE, 79).

init(_Opts) ->
    undefined.

port_options() ->
    [].

encode(Elems, St) ->
    Bin = encode_(Elems, <<>>),
    Sz = byte_size(Bin),
    {ok, <<"&RVI|",
	   (integer_to_binary(Sz, 16))/binary, "\n",
	   Bin/binary>>, St}.

encode_([{Key, Val}|T], Acc) ->
    {Type, ValBin} = encode_val(Val),
    Bin = encode_elem(to_bin(Key), Type, ValBin),
    encode_(T, <<Acc/binary, Bin/binary>>);
encode_([], Acc) ->
    Acc.

encode_val(V) when is_binary(V)  -> {$B, V};
encode_val(V) when is_integer(V) -> {$i, integer_to_binary(V,16)};
encode_val(V) when is_atom(V)    -> {$a, atom_to_binary(V, latin1)};
encode_val(V) when is_float(V) ->
    Bin = <<V/float>>,
    {$f, Bin};
encode_val({T,_} = J) when T==array; T==struct ->
    JSON = exo_json:encode(J),
    {$J, iolist_to_binary(JSON)};
encode_val([T|_] = L) when is_tuple(T) ->
    {$L, encode_(L, <<>>)}.

decode_value($B, Bin) -> Bin;
decode_value($i, Bin) -> binary_to_integer(Bin, 16);
decode_value($f, <<F/float>>) -> F;
decode_value($a, Bin) -> binary_to_existing_atom(Bin, latin1);
decode_value($J, Bin) ->
    {ok, Obj} = exo_json:decode_string(binary_to_list(Bin)),
    Obj;
decode_value($L, Bin) ->
    decode_packet(Bin).

encode_elem(Key, Type, Bin) ->
    BSz = byte_size(Bin),
    case byte_size(Key) + BSz of
	Sz when Sz =< 78, Type >= $a, Type =< $z ->
	    <<Key/binary, "|", Type, ":", Bin/binary, "\n">>;
	_ ->
	    <<Key/binary, "|", Type, "|",
	      (integer_to_binary(BSz+1,16))/binary, "\n",
	      Bin/binary, "\n">>
    end.

decode(<<"&RVI|", Rest/binary>>, undefined) ->
    case erlang:decode_packet(line, Rest, [{line_length, 79}]) of
	{more, _} ->
	    {more, Rest};
	{ok, Ln, Rest1} ->
	    LSz = byte_size(Ln),
	    LSz1 = LSz-1,
	    <<Size:LSz1/binary, "\n">> = Ln,
	    Bytes = binary_to_integer(Size, 16),
	    case Rest1 of
		<<Pkt:Bytes/binary, Tail/binary>> ->
		    {ok, decode_packet(Pkt), Tail};
		_ ->
		    {more, #dlink_data_rvi{need = Bytes, buf = Rest1}}
	    end
    end;
decode(Data, #dlink_data_rvi{need = Bytes, buf = Buf} = St) ->
    case <<Buf/binary, Data/binary>> of
	<<Pkt:Bytes/binary, Tail/binary>> ->
	    {ok, decode_packet(Pkt), Tail};
	Buf1 ->
	    {more, St#dlink_data_rvi{buf = Buf1}}
    end;
decode(_, _St) ->
    {error, unknown, undefined}.

decode_packet(<<>>) ->
    [];
decode_packet(P) ->
    {ok, L, Rest} = erlang:decode_packet(line, P, [{line_length, ?MAX_LINE}]),
    case split_line(L) of
	{Key, Type, simple, Data} ->
	    [{Key, decode_value(Type, Data)}|decode_packet(Rest)];
	{Key, Type, Size} ->
	    Size1 = Size-1,
	    <<VBin:Size1/binary, "\n", Rest1/binary>> = Rest,
	    [{Key, decode_value(Type, VBin)}|decode_packet(Rest1)]
    end.

to_bin(V) when is_atom(V)   -> atom_to_binary(V, latin1);
to_bin(V) when is_binary(V) -> V;
to_bin(V) when is_list(V)   -> iolist_to_binary(V).

split_line(L) ->
    split_line(L, <<>>).

split_line(<<"\\", $|, Rest/binary>>, Acc) ->
    split_line(Rest, <<Acc/binary, $|>>);
split_line(<<"|", T, ":", Rest/binary>>, Acc) ->
    {Acc, T, simple, remove_nl(Rest)};
split_line(<<"|", T, "|", Rest/binary>>, Acc) ->
    SzBin = remove_nl(Rest),
    {Acc, T, binary_to_integer(SzBin, 16)};
split_line(<<H, T/binary>>, Acc) ->
    split_line(T, <<Acc/binary, H>>).


remove_nl(B) ->
    Sz = byte_size(B),
    Sz1 = Sz-1,
    <<V:Sz1/binary, "\n">> = B,
    V.