fix
This commit is contained in:
parent
18723bdb0d
commit
67a8343517
@ -20,7 +20,11 @@
|
|||||||
|
|
||||||
-record(state, {
|
-record(state, {
|
||||||
parent_pid :: pid(),
|
parent_pid :: pid(),
|
||||||
device :: #modbus_device{}
|
device :: #modbus_device{},
|
||||||
|
%% 重新建立 #{Name => Metric}
|
||||||
|
metrics = #{},
|
||||||
|
%% #{Ref => MetricName}
|
||||||
|
inflight = #{}
|
||||||
}).
|
}).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
@ -42,17 +46,20 @@ start_link(ParentPid, Device = #modbus_device{}) when is_pid(ParentPid) ->
|
|||||||
-spec(init(Args :: term()) ->
|
-spec(init(Args :: term()) ->
|
||||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term()} | ignore).
|
{stop, Reason :: term()} | ignore).
|
||||||
init([ParentPid, Device = #modbus_device{metrics = Metrics, poll_interval = PollInterval}]) ->
|
init([ParentPid, Device = #modbus_device{metrics = Metrics0, poll_interval = PollInterval}]) ->
|
||||||
%% 处理采集项目
|
%% 处理采集项目
|
||||||
|
Metrics = maps:from_list(lists:map(fun(Metric0 = #modbus_metric{name = Name}) -> {Name, Metric0} end, Metrics0)),
|
||||||
|
|
||||||
#modbus_metric{
|
%% 初步启动的过程中按照step提交任务
|
||||||
name = Name,
|
Len = length(Metrics0),
|
||||||
address = Address,
|
case Len > 0 of
|
||||||
type = Type,
|
true ->
|
||||||
unit = Uint
|
Step = erlang:max(1, PollInterval div Len),
|
||||||
} = hd(Metrics),
|
start_ticker(maps:keys(Metrics), Step);
|
||||||
|
false ->
|
||||||
{ok, #state{parent_pid = ParentPid, device = Device}}.
|
ok
|
||||||
|
end,
|
||||||
|
{ok, #state{parent_pid = ParentPid, metrics = Metrics, device = Device}}.
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
%% @doc Handling call messages
|
%% @doc Handling call messages
|
||||||
@ -82,16 +89,30 @@ handle_cast(_Request, State = #state{}) ->
|
|||||||
{noreply, NewState :: #state{}} |
|
{noreply, NewState :: #state{}} |
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||||
{stop, Reason :: term(), NewState :: #state{}}).
|
{stop, Reason :: term(), NewState :: #state{}}).
|
||||||
handle_info({timeout, _, poll_ticker}, State = #state{parent_pid = ParentPid,
|
handle_info({timeout, _, {poll_ticker, Name}}, State = #state{parent_pid = ParentPid, inflight = Inflight,
|
||||||
device = #modbus_device{slave_id = SlaveId, poll_interval = PollInterval, metrics = Metrics}}) ->
|
device = #modbus_device{slave_id = SlaveId, poll_interval = PollInterval, metrics = Metrics}}) ->
|
||||||
poll_ticker(PollInterval),
|
|
||||||
|
|
||||||
|
%% 开启下一次的循环
|
||||||
|
poll_ticker(PollInterval, Name),
|
||||||
|
|
||||||
|
#modbus_metric{address = Address} = maps:get(Name, Metrics),
|
||||||
%% 读取采集项目
|
%% 读取采集项目
|
||||||
lists:foreach(fun(#modbus_metric{address = Address}) ->
|
ReceiverPid = self(),
|
||||||
ParentPid ! {read, SlaveId, Address}
|
Ref = make_ref(),
|
||||||
end, Metrics),
|
ParentPid ! {read, ReceiverPid, Ref, SlaveId, Address},
|
||||||
|
|
||||||
|
{noreply, State#state{inflight = maps:put(Ref, Name, Inflight)}};
|
||||||
|
|
||||||
|
handle_info({request_reply, Ref, Val}, State = #state{inflight = Inflight, metrics = Metrics}) ->
|
||||||
|
case maps:take(Ref, Inflight) of
|
||||||
|
error ->
|
||||||
|
{noreply, State};
|
||||||
|
{Name, NInflight} ->
|
||||||
|
#modbus_metric{} = maps:get(Name, Metrics),
|
||||||
|
lager:debug("[modbus_device] metric: ~p, get value: ~p", [Name, Val]),
|
||||||
|
{noreply, State#state{inflight = NInflight}}
|
||||||
|
end;
|
||||||
|
|
||||||
{noreply, State};
|
|
||||||
handle_info(_Info, State = #state{}) ->
|
handle_info(_Info, State = #state{}) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
@ -117,5 +138,14 @@ code_change(_OldVsn, State = #state{}, _Extra) ->
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
poll_ticker(PollInterval) when is_integer(PollInterval) ->
|
%% 启动的时候按照step分散一下轮询的压力
|
||||||
erlang:start_timer(PollInterval, self(), poll_ticker).
|
-spec start_ticker(list(), Step :: integer()) -> no_return().
|
||||||
|
start_ticker([], _) ->
|
||||||
|
ok;
|
||||||
|
start_ticker([Name|T], Step) ->
|
||||||
|
poll_ticker(Step, Name),
|
||||||
|
start_ticker(T, Step + Step).
|
||||||
|
|
||||||
|
-spec poll_ticker(PollInterval :: integer(), Name :: binary()) -> no_return().
|
||||||
|
poll_ticker(PollInterval, Name) when is_integer(PollInterval), is_binary(Name) ->
|
||||||
|
erlang:start_timer(PollInterval, self(), {poll_ticker, Name}).
|
||||||
@ -90,7 +90,7 @@ init([AST = #ast{modbus = Modbus = #modbus{error_log = ErrorLog, access_log = Ac
|
|||||||
end, Devices),
|
end, Devices),
|
||||||
DevicesMap = maps:from_list(DevicesPids),
|
DevicesMap = maps:from_list(DevicesPids),
|
||||||
|
|
||||||
lager:debug("device pid: ~p", [DevicesMap]),
|
lager:debug("devices pid: ~p", [DevicesMap]),
|
||||||
|
|
||||||
{ok, ?DISCONNECTED, #state{ast = AST, access_log_pid = create_log_file(AccessLog), error_log_pid = create_log_file(ErrorLog),
|
{ok, ?DISCONNECTED, #state{ast = AST, access_log_pid = create_log_file(AccessLog), error_log_pid = create_log_file(ErrorLog),
|
||||||
queue = queue:new(), packet_id = 1, devices_map = DevicesMap}}.
|
queue = queue:new(), packet_id = 1, devices_map = DevicesMap}}.
|
||||||
@ -135,7 +135,7 @@ handle_event(info, {Port, {data, <<?MODBUS_READ:8, PacketId:32, Val:32>>}}, ?CON
|
|||||||
erlang:is_process_alive(ReceiverPid) andalso ReceiverPid ! {request_reply, Ref, Val},
|
erlang:is_process_alive(ReceiverPid) andalso ReceiverPid ! {request_reply, Ref, Val},
|
||||||
NMap
|
NMap
|
||||||
end,
|
end,
|
||||||
lager:debug("port data is: ~p", [{PacketId, Val}]),
|
lager:debug("[modbus_service] port data is: ~p", [{PacketId, Val}]),
|
||||||
%% 读取下一条
|
%% 读取下一条
|
||||||
erlang:start_timer(DelayMs, self(), read_next),
|
erlang:start_timer(DelayMs, self(), read_next),
|
||||||
|
|
||||||
@ -143,25 +143,25 @@ handle_event(info, {Port, {data, <<?MODBUS_READ:8, PacketId:32, Val:32>>}}, ?CON
|
|||||||
|
|
||||||
%% port退出
|
%% port退出
|
||||||
handle_event(info, {Port, {exit_status, Code}}, ?CONNECTED, State = #state{mode = rtu, port = Port}) ->
|
handle_event(info, {Port, {exit_status, Code}}, ?CONNECTED, State = #state{mode = rtu, port = Port}) ->
|
||||||
lager:debug("[efka_service] port: ~p, exit with code: ~p", [Port, Code]),
|
lager:debug("[modbus_service] port: ~p, exit with code: ~p", [Port, Code]),
|
||||||
retry_connect(),
|
retry_connect(),
|
||||||
{next_state, ?DISCONNECTED, State#state{port = undefined}};
|
{next_state, ?DISCONNECTED, State#state{port = undefined}};
|
||||||
|
|
||||||
%% 处理port的退出消息
|
%% 处理port的退出消息
|
||||||
handle_event(info, {'EXIT', Port, Reason}, ?CONNECTED, State = #state{mode = rtu, port = Port}) ->
|
handle_event(info, {'EXIT', Port, Reason}, ?CONNECTED, State = #state{mode = rtu, port = Port}) ->
|
||||||
lager:debug("[efka_service] port: ~p, exit with code: ~p", [Port, Reason]),
|
lager:debug("[modbus_service] port: ~p, exit with code: ~p", [Port, Reason]),
|
||||||
retry_connect(),
|
retry_connect(),
|
||||||
{next_state, ?DISCONNECTED, State#state{port = undefined}};
|
{next_state, ?DISCONNECTED, State#state{port = undefined}};
|
||||||
|
|
||||||
%% tcp退出
|
%% tcp退出
|
||||||
handle_event(info, {tcp_closed, Socket}, ?CONNECTED, State = #state{mode = tcp, socket = Socket}) ->
|
handle_event(info, {tcp_closed, Socket}, ?CONNECTED, State = #state{mode = tcp, socket = Socket}) ->
|
||||||
lager:debug("[efka_service] socket: ~p, exit with tcp_closed", [Socket]),
|
lager:debug("[modbus_service] socket: ~p, exit with tcp_closed", [Socket]),
|
||||||
retry_connect(),
|
retry_connect(),
|
||||||
{next_state, ?DISCONNECTED, State#state{socket = undefined}};
|
{next_state, ?DISCONNECTED, State#state{socket = undefined}};
|
||||||
|
|
||||||
%% 处理tcp的退出消息
|
%% 处理tcp的退出消息
|
||||||
handle_event(info, {tcp_error, Socket, Reason}, ?CONNECTED, State = #state{mode = tcp, socket = Socket}) ->
|
handle_event(info, {tcp_error, Socket, Reason}, ?CONNECTED, State = #state{mode = tcp, socket = Socket}) ->
|
||||||
lager:debug("[efka_service] socket: ~p, exit with code: ~p", [Socket, Reason]),
|
lager:debug("[modbus_service] socket: ~p, exit with code: ~p", [Socket, Reason]),
|
||||||
retry_connect(),
|
retry_connect(),
|
||||||
{next_state, ?DISCONNECTED, State#state{socket = undefined}};
|
{next_state, ?DISCONNECTED, State#state{socket = undefined}};
|
||||||
|
|
||||||
@ -207,6 +207,8 @@ code_change(_OldVsn, StateName, State = #state{}, _Extra) ->
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
|
-spec connect(Transport :: #modbus_transport_rtu{} | #modbus_transport_tcp{}) ->
|
||||||
|
{ok, {rtu, Port :: port(), DelayMs :: integer()}} | {ok, Socket :: gen_tcp:socket()} | {error, Reason :: any()}.
|
||||||
%% databits为8位
|
%% databits为8位
|
||||||
connect(#modbus_transport_rtu{port = Port, baudrate = BaudRate, stopbits = StopBits, parity = Parity, timeout = Timeout}) ->
|
connect(#modbus_transport_rtu{port = Port, baudrate = BaudRate, stopbits = StopBits, parity = Parity, timeout = Timeout}) ->
|
||||||
RealExecCmd = "",
|
RealExecCmd = "",
|
||||||
@ -229,18 +231,21 @@ connect(#modbus_transport_tcp{host = Host, port = Port, timeout = Timeout0}) ->
|
|||||||
gen_tcp:connect(Host, Port, [binary], Timeout).
|
gen_tcp:connect(Host, Port, [binary], Timeout).
|
||||||
|
|
||||||
%% 计算3.5个字符的静默时间
|
%% 计算3.5个字符的静默时间
|
||||||
|
-spec frame_delay(BaudRate :: integer(), DataBits :: integer(), Parity :: integer(), StopBits :: integer()) -> integer().
|
||||||
frame_delay(BaudRate, DataBits, Parity, StopBits) when is_integer(BaudRate), is_integer(DataBits), is_integer(Parity), is_integer(StopBits) ->
|
frame_delay(BaudRate, DataBits, Parity, StopBits) when is_integer(BaudRate), is_integer(DataBits), is_integer(Parity), is_integer(StopBits) ->
|
||||||
%% 计算1个字符的总位数
|
%% 计算1个字符的总位数
|
||||||
CharBits = 1 + DataBits + case Parity of 0 -> 0; _ -> 1 end + StopBits,
|
CharBits = 1 + DataBits + case Parity of 0 -> 0; _ -> 1 end + StopBits,
|
||||||
%% 3.5字符时间(毫秒), 预留20%的时间
|
%% 3.5字符时间(毫秒), 预留20%的时间
|
||||||
erlang:ceil((3500 * CharBits) / BaudRate * 1.2).
|
erlang:ceil((3500 * CharBits) / BaudRate * 1.2).
|
||||||
|
|
||||||
|
-spec retry_connect() -> no_return().
|
||||||
retry_connect() ->
|
retry_connect() ->
|
||||||
erlang:start_timer(5000, self(), modbus_connect).
|
erlang:start_timer(5000, self(), modbus_connect).
|
||||||
|
|
||||||
|
-spec create_log_file(undefined | string()) -> undefined | pid().
|
||||||
create_log_file(undefined) ->
|
create_log_file(undefined) ->
|
||||||
undefined;
|
undefined;
|
||||||
create_log_file(FileName) ->
|
create_log_file(FileName) when is_list(FileName) ->
|
||||||
case modbus_logger:start_link(FileName) of
|
case modbus_logger:start_link(FileName) of
|
||||||
{ok, FilePid} ->
|
{ok, FilePid} ->
|
||||||
FilePid;
|
FilePid;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user