summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--components/dlink_tcp/src/dlink_tcp_rpc.erl2
-rw-r--r--components/schedule/src/rvi_routing.erl52
-rw-r--r--components/schedule/src/schedule_rpc.erl99
-rw-r--r--rvi_sample.config58
-rw-r--r--test/test_plan.md54
5 files changed, 188 insertions, 77 deletions
diff --git a/components/dlink_tcp/src/dlink_tcp_rpc.erl b/components/dlink_tcp/src/dlink_tcp_rpc.erl
index 9ff68f0..84d6517 100644
--- a/components/dlink_tcp/src/dlink_tcp_rpc.erl
+++ b/components/dlink_tcp/src/dlink_tcp_rpc.erl
@@ -541,7 +541,7 @@ handle_call({rvi, setup_data_link, [ Service, Opts ]}, _From, St) ->
undefined ->
?info("dlink_tcp:setup_data_link(~p) Failed: no target given in options.",
[Service]),
- { reply, [ no_route, 0 ], St };
+ { reply, [ok, -1 ], St };
Addr ->
[ Address, Port] = string:tokens(Addr, ":"),
diff --git a/components/schedule/src/rvi_routing.erl b/components/schedule/src/rvi_routing.erl
index 0e6dfbe..3bbc16f 100644
--- a/components/schedule/src/rvi_routing.erl
+++ b/components/schedule/src/rvi_routing.erl
@@ -16,6 +16,9 @@
-export([get_service_routes/1]).
-export([start_link/0]).
+-export([find_routes_/2,
+ normalize_routes_/2]).
+
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@@ -91,7 +94,7 @@ init([]) ->
%% @end
%%--------------------------------------------------------------------
handle_call( { rvi_get_service_routes, Service }, _From, St) ->
- {reply, normalize_routes_(find_routes_( St#st.routes, Service), []), t};
+ {reply, find_routes_( St#st.routes, Service), St};
handle_call(_Request, _From, St) ->
Reply = ok,
@@ -155,17 +158,25 @@ code_change(_OldVsn, St, _Extra) ->
%%%===================================================================
-prefix_match_(_, [], Len) ->
+%% We ran out of prefix chars.
+%% Service is a prefix match
+prefix_match_(_Service, [], Len) ->
Len;
-prefix_match_([], _, Len ) ->
- Len;
+%% We ran out of service chars.
+%% Service is shorter than prefix no match.
+prefix_match_([], _Prefix, _Len ) ->
+ -1;
-prefix_match_([ C1 | T1], [ C2 | T2 ], Len) when C1 =:= C2 ->
- prefix_match_(T1, T2, Len +1);
+%%
+prefix_match_([ ServiceH | ServiceT], [ PrefixH | PrefixT ], Len)
+ when ServiceH =:= PrefixH ->
-prefix_match_(_,_, Len) ->
- Len.
+ prefix_match_(ServiceT, PrefixT, Len + 1);
+
+%% Mismatch between the the service and candidate. No match
+prefix_match_(_Service, _Prefix, _Len) ->
+ -1.
find_routes_([], _Service, CurRoutes, CurMatchLen ) ->
{ CurRoutes, CurMatchLen };
@@ -178,32 +189,38 @@ find_routes_([ { ServicePrefix, Routes } | T], Service, CurRoutes, CurMatchLen )
true ->
%% Continue with the new routes and matching len installed
find_routes_(T, Service, Routes, MatchLen);
-
+
false ->
%% Continue with the old routes and matching len installed
find_routes_(T, Service, CurRoutes, CurMatchLen)
end;
-
-find_routes_(Rt, Svc, CurRoutes, CurMatchLen) ->
- ?info("----------------Failed on ~p", [Rt]),
- { x, y}.
+
+find_routes_(Rt, _Svc, CurRoutes, CurMatchLen) ->
+ ?warning("rvi_routing(): Incorrect route entry: ~p", [Rt]),
+ { CurRoutes, CurMatchLen }.
find_routes_(Routes, Service) ->
case find_routes_(Routes, Service, undefined, 0) of
{ undefined, 0 } ->
- not_found;
+ ?debug("rvi_routing(): ~p -> unknown", [ Service]),
+ []; %% No routes found
- { Routes, _MatchLen } ->
- Routes
+ { MatchRoutes, _MatchLen } ->
+ ?debug("rvi_routing(): ~p -> ~p", [ Service, MatchRoutes ]),
+ normalize_routes_(MatchRoutes, [])
end.
+%% { Protocol, DataLink } -> { {Protocol, [] }, {DataLink, [] } },
+%% { Protocol, { DataLink, DOpts } -> { {Protocol, [] }, {DataLink, DOpts } },
+%% { { Protocol, POpts }, DataLink } -> { {Protocol, POpts}, {DataLink, [] }},
+%% { { Protocol, POpts }, { DataLink, DOpts } -> { {Protocol, POpts}, {DataLink, DOpts }},
normalize_routes_({ServicePrefix, []}, Acc) ->
{ ServicePrefix, lists:reverse(Acc) };
normalize_routes_({ServicePrefix, [ {{ Pr, PrOp }, { DL, DLOp }} | Rem ]}, Acc) ->
- normalize_routes_({ServicePrefix, Rem}, [ { {Pr, PrOp}, { DL, DLOp } } | Acc]);
+ normalize_routes_({ServicePrefix, Rem}, [ {{Pr, PrOp}, { DL, DLOp } } | Acc]);
normalize_routes_({ServicePrefix, [ { Pr, { DL, DLOp }} | Rem ]}, Acc) ->
normalize_routes_({ServicePrefix, Rem}, [ { {Pr, []}, { DL, DLOp } } | Acc]);
@@ -213,3 +230,4 @@ normalize_routes_({ServicePrefix, [ {{ Pr, PrOp}, DL } | Rem ]}, Acc) ->
normalize_routes_({ServicePrefix, [ {Pr, DL} | Rem ]}, Acc) ->
normalize_routes_({ServicePrefix, Rem}, [ { {Pr, []}, { DL, [] } } | Acc]).
+
diff --git a/components/schedule/src/schedule_rpc.erl b/components/schedule/src/schedule_rpc.erl
index 7a1b23a..be05d92 100644
--- a/components/schedule/src/schedule_rpc.erl
+++ b/components/schedule/src/schedule_rpc.erl
@@ -219,27 +219,22 @@ handle_call( { rvi, schedule_message,
?debug("schedule:sched_msg(): certificate ~p", [Certificate]),
%%?debug("schedule:sched_msg(): St: ~p", [St]),
- %%
- %% Retrieve the routes that we should try for this message
- %%
- case rvi_routing:get_service_routes(SvcName) of
- not_found ->
- {reply, [ no_route ], St };
-
- Routes ->
- { TransID, NSt1} = create_transaction_id(St),
- NSt2 = queue_message(SvcName,
- TransID,
- Routes,
- Timeout,
- calc_relative_tout(Timeout),
- Parameters,
- Signature,
- Certificate,
- NSt1),
-
- { reply, [ok, TransID], NSt2 }
- end;
+ %% Create a transaction ID
+ { TransID, NSt1} = create_transaction_id(St),
+
+ %% Queue the message
+ NSt2 = queue_message(SvcName,
+ TransID,
+ rvi_routing:get_service_routes(SvcName), %% Can be no_route
+ Timeout,
+ calc_relative_tout(Timeout),
+ Parameters,
+ Signature,
+ Certificate,
+ NSt1),
+
+ { reply, [ok, TransID], NSt2 };
+
handle_call(Other, _From, St) ->
@@ -382,21 +377,6 @@ code_change(_OldVsn, St, _Extra) ->
-%%
-%% No more routes to try
-%%
-queue_message(SvcName,
- _TransID,
- [ ],
- _MessageTimeout,
- _RelativeTimeout,
- _Parameters,
- _Signature,
- _Certificate, St) ->
-
- %% FIXME: Handle route failure
- ?notice("schedule:queue_message(): Ran out of routes to try for ~p", [SvcName]),
- { route_failed, St };
%%
@@ -414,6 +394,25 @@ queue_message(SvcName,
do_timeout_callback(St#st.cs, SvcName, TransID),
{ timeout, St };
+%%
+%% No more routes to try, or no routes found at all
+%% by rvi_routing:get_service_routes()
+%%
+%% Stash the message in the unknown datalinvariant of the service
+%% and opportunistically send it if the service
+queue_message(SvcName,
+ _TransID,
+ [ ],
+ _MessageTimeout,
+ _RelativeTimeout,
+ _Parameters,
+ _Signature,
+ _Certificate, St) ->
+
+ %% FIXME: Handle route failure
+ ?notice("schedule:queue_message(): Ran out of routes to try for ~p", [SvcName]),
+ { route_failed, St };
+
%% Try to queue message
queue_message(SvcName,
@@ -442,6 +441,7 @@ queue_message(SvcName,
{ _ , NSt } -> %% Failed to send message. Setup data link
+
%%
%% Bring up the relevant data link for the given route.
%% Once up, the data link will invoke service_availble()
@@ -453,10 +453,12 @@ queue_message(SvcName,
%% Setup a timeout that triggers on whatever
%% comes first of the message's general timeout
%% or the timeout of the data link bringup
- TRef = erlang:send_after(min(RelativeTimeout, DLTimeout),
+ %%
+ TRef = erlang:send_after(select_timeout(RelativeTimeout, DLTimeout),
self(),
{ rvi_message_timeout, SvcName, DLMod, TransID }),
+
%% Setup a message to be added to the service / dl table.
Msg = #message {
transaction_id = TransID,
@@ -469,7 +471,6 @@ queue_message(SvcName,
certificate = Certificate
},
-
%% Add to ets table
TransID = ets:insert(SvcRec#service.messages_tid, Msg),
{ok, NSt};
@@ -689,4 +690,24 @@ first_service_message(#service { messages_tid = Tid }) ->
end
end.
-
+
+%% A timeout of -1 means 'does not apply'
+%%
+%% However, if both are -1, we just return the shortest posssible
+%% timeout.
+%% If both timeouts are specified, we select the shortest one.
+%%
+select_timeout(TimeOut1, TimeOut2) ->
+
+ case { TimeOut1 =:= -1, TimeOut2 =:= -1 } of
+ { true, true } -> 1;
+
+ { true, false } -> TimeOut2;
+
+ { false, true } -> TimeOut1;
+
+ { false, false } -> min(TimeOut1, TimeOut2)
+ end.
+
+
+
diff --git a/rvi_sample.config b/rvi_sample.config
index 9815697..aa2a12b 100644
--- a/rvi_sample.config
+++ b/rvi_sample.config
@@ -171,31 +171,49 @@
%%
{ node_service_prefix, "jlr.com/vin/$rvi_uuid(default_vin)/"},
- %% Specify static service prefixes with well known addresses
+
+ %% Routing rules determining how to get a message targeting a specific
+ %% service to its destination.
%%
- %% Static nodes allows a local RVI node to route services with
- %% matching prefixes to a well known address of a remote node. A
- %% static node is often a backend/cloud server that is assumed to
- %% be found at a given address as soon as network connectivity
- %% has been established.
- %%
- %% When a locally connected service issues a message or rpc to a
- %% service, and the network address of the service cannot be
- %% found in the tables maintained by the peer-to-peer service
- %% discovery process, the static nodes table below is scanned as
- %% to prefix match the service and locate a suitable network
- %% address.
+ %% Please note that if a remotely initiated (== client) data link is
+ %% available and has announced that the targeted service is available,
+ %% that data link will be used regardless of what it is.
+ %%
+ %% In other words, if you setup routing rules for how to reach out
+ %% to a vehicle using SMS, and that vehicle happens to have a 3G
+ %% connection up when the message is sent, the 3G connection will be used.
%%
- { static_nodes,
- [
- %% rvi1.nginfotpdx.net is the JLR hosted RVI server.
- %% { "jlr.com/backend/", }
- ]
- },
-
+ %% We will add a blacklist feature in the future to optionally block
+ %% such opportunistic routing in the future.
+ %%
{ routing_rules,
[
%% Service name prefix that rules are specified for
+ %% The service prefix with the longest match against the service targeted
+ %% by the message will be used.
+ %%
+ %% Example: Targeted service = jlr.com/backend/sota/get_updates
+ %%
+ %% Prefix1: { "jlr.com/backend", [...]}
+ %% Prefix2: { "jlr.com/backend/sota", [...]}
+ %%
+ %% Prefix2 will be used.
+ %%
+ %% This allows you, for example, to setup different servers
+ %% for different types of services (SOTA, remote door unlock,
+ %% HVAC etc).
+ %%
+
+ %% Make sure to have a default if you don't want your message
+ %% to error out immediately. With a default the message will
+ %% be queued until it times out, waiting for a remote node
+ %% to connect and announce that it can handle the targeted service.
+ { "",
+ [
+ { proto_bert_rpc, dlink_tcp_rpc}
+ ]
+ },
+
{ "jlr.com/backend/",
%% Which protocol and data link pair to use when transmitting the message
%% to the targeted service. If a pair reports a failure, the next pair is tried.
diff --git a/test/test_plan.md b/test/test_plan.md
new file mode 100644
index 0000000..e5634d8
--- /dev/null
+++ b/test/test_plan.md
@@ -0,0 +1,54 @@
+# RVI TEST PLAN
+
+While we are implementing an automated test suite, we will start with a simple manual test schedule
+
+
+# LOCAL SERVICES
+
+## SERVICE REGISTRATION
+
+## SERVICE QUERYING (RVI_GET_SERVICES)
+
+## SERVICE AVAILABILITY NOTIFICATION
+
+## SERVICE DEREGISTRATION
+
+## SERVICE INVOCATION
+
+## INVOCATION OF NON-EXISTENT SERVICE
+
+## STRESS TEST
+
+
+
+# REMOTE SERVICES
+
+## SERVICE REGISTRATION
+
+## SERVICE QUERYING (RVI_GET_SERVICES)
+
+## SERVICE AVAILABILITY NOTIFICATION
+
+## SERVICE DEREGISTRATION
+
+## SERVICE INVOCATION
+
+## INVOCATION OF NON-EXISTENT SERVICE
+
+## LINK LOSS
+
+## LINK RECONNECT
+
+## LINK ESTABLISHMENT
+
+## STRESS TEST
+
+
+# MULTI-PATHED SERVICES
+
+## TCP
+
+## BT
+
+## SMS
+