diff --git a/apps/modbus/src/modbus_service.erl b/apps/modbus/src/modbus_service.erl index 4153661..a1dbad5 100644 --- a/apps/modbus/src/modbus_service.erl +++ b/apps/modbus/src/modbus_service.erl @@ -4,26 +4,28 @@ %%% @doc %%% %%% @end -%%% Created : 13. 6月 2025 09:46 +%%% Created : 20. 6月 2025 16:38 %%%------------------------------------------------------------------- -module(modbus_service). -author("anlicheng"). -include("modbus_ast.hrl"). --behaviour(gen_server). - -%% rtu指令 --define(MODBUS_CONNECT, 16#01). --define(MODBUS_READ, 16#02). +-behaviour(gen_statem). %% API -export([start_link/1]). -export([test/0]). -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +%% gen_statem callbacks +-export([init/1, handle_event/4, terminate/3, code_change/4, callback_mode/0]). --define(SERVER, ?MODULE). +%% rtu指令 +-define(MODBUS_CONNECT, 16#01). +-define(MODBUS_READ, 16#02). + +%% 当前的状态 +-define(DISCONNECTED, disconnected). +-define(CONNECTED, connected). -record(state, { port, @@ -31,33 +33,31 @@ error_log_pid :: pid() | undefined }). +%%%=================================================================== +%%% API +%%%=================================================================== + test() -> {ok, Config} = file:read_file("/usr/local/code/cloudkit/modbus/modbus.conf"), %lager:debug("config is: ~ts", [Config]), {ok, AST} = modbus_parser:parse(Config), start_link(AST). -%%%=================================================================== -%%% API -%%%=================================================================== - -%% @doc Spawns the server and registers the local name (unique) --spec(start_link(AST :: #ast{}) -> - {ok, Pid :: pid()} | ignore | {error, Reason :: term()}). -start_link(AST) -> - gen_server:start_link(?MODULE, [AST], []). +%% @doc Creates a gen_statem process which calls Module:init/1 to +%% initialize. To ensure a synchronized start-up procedure, this +%% function does not return until Module:init/1 has returned. +start_link(AST = #ast{}) -> + gen_statem:start_link(?MODULE, [AST], []). %%%=================================================================== -%%% gen_server callbacks +%%% gen_statem callbacks %%%=================================================================== %% @private -%% @doc Initializes the server --spec(init(Args :: term()) -> - {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | - {stop, Reason :: term()} | ignore). +%% @doc Whenever a gen_statem is started using gen_statem:start/[3,4] or +%% gen_statem:start_link/[3,4], this function is called by the new +%% process to initialize. init([AST = #ast{modbus = Modbus, devices = Devices}]) -> - lager:debug("modbus is: ~p", [Modbus]), Device = hd(Devices), lager:debug("devices is: ~p", [Device#modbus_device.metrics]), @@ -70,66 +70,45 @@ init([AST = #ast{modbus = Modbus, devices = Devices}]) -> %{ok, AccessLogPid} = modbus_logger:start_link(AccessLog), %{ok, ErrorLogPid} = modbus_logger:start_link(ErrorLog), - {ok, #state{access_log_pid = undefined, error_log_pid = undefined}}. + {ok, ?DISCONNECTED, #state{}}. %% @private -%% @doc Handling call messages --spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()}, - State :: #state{}) -> - {reply, Reply :: term(), NewState :: #state{}} | - {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} | - {noreply, NewState :: #state{}} | - {noreply, NewState :: #state{}, timeout() | hibernate} | - {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | - {stop, Reason :: term(), NewState :: #state{}}). -handle_call(_Request, _From, State = #state{}) -> - {reply, ok, State}. +%% @doc This function is called by a gen_statem when it needs to find out +%% the callback mode of the callback module. +callback_mode() -> + handle_event_function. %% @private -%% @doc Handling cast messages --spec(handle_cast(Request :: term(), State :: #state{}) -> - {noreply, NewState :: #state{}} | - {noreply, NewState :: #state{}, timeout() | hibernate} | - {stop, Reason :: term(), NewState :: #state{}}). -handle_cast(_Request, State = #state{}) -> - {noreply, State}. +%% @doc If callback_mode is handle_event_function, then whenever a +%% gen_statem receives an event from call/2, cast/2, or as a normal +%% process message, this function is called. -%% @private -%% @doc Handling all non call/cast messages --spec(handle_info(Info :: timeout() | term(), State :: #state{}) -> - {noreply, NewState :: #state{}} | - {noreply, NewState :: #state{}, timeout() | hibernate} | - {stop, Reason :: term(), NewState :: #state{}}). -%% 发送读取指令 -handle_info({read, SlaveId, Address}, State = #state{port = Port}) -> +handle_event(info, {read, SlaveId, Address}, ?CONNECTED, State = #state{port = Port}) -> ReadCmd = <>, Port ! {self(), {command, ReadCmd}}, - ok; + {keep_state, State}; -handle_info({Port, {data, Data}}, State = #state{port = Port}) -> +%% 从port读取数据 +handle_event(info, {Port, {data, Data}}, ?CONNECTED, State = #state{port = Port}) -> lager:debug("port data is: ~p", [Data]), - {noreply, State}; + {keep_state, State}; -handle_info(_Info, State = #state{}) -> - {noreply, State}. +handle_event(_EventType, _EventContent, _StateName, State = #state{}) -> + NextStateName = the_next_state_name, + {next_state, NextStateName, State}. %% @private -%% @doc This function is called by a gen_server when it is about to +%% @doc This function is called by a gen_statem when it is about to %% terminate. It should be the opposite of Module:init/1 and do any -%% necessary cleaning up. When it returns, the gen_server terminates -%% with Reason. The return value is ignored. --spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), - State :: #state{}) -> term()). -terminate(_Reason, _State = #state{}) -> +%% necessary cleaning up. When it returns, the gen_statem terminates with +%% Reason. The return value is ignored. +terminate(_Reason, _StateName, _State = #state{}) -> ok. %% @private %% @doc Convert process state when code is changed --spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{}, - Extra :: term()) -> - {ok, NewState :: #state{}} | {error, Reason :: term()}). -code_change(_OldVsn, State = #state{}, _Extra) -> - {ok, State}. +code_change(_OldVsn, StateName, State = #state{}, _Extra) -> + {ok, StateName, State}. %%%=================================================================== %%% Internal functions @@ -152,4 +131,4 @@ connect(#modbus{transport = #modbus_transport_tcp{host = Host, port = Port, time false -> 2000 end, - gen_tcp:connect(Host, Port, [binary], Timeout). + gen_tcp:connect(Host, Port, [binary], Timeout). \ No newline at end of file diff --git a/apps/modbus/src/modbus_service2.erl b/apps/modbus/src/modbus_service2.erl deleted file mode 100644 index 0b64ea6..0000000 --- a/apps/modbus/src/modbus_service2.erl +++ /dev/null @@ -1,134 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @author anlicheng -%%% @copyright (C) 2025, -%%% @doc -%%% -%%% @end -%%% Created : 20. 6月 2025 16:38 -%%%------------------------------------------------------------------- --module(modbus_service2). --author("anlicheng"). --include("modbus_ast.hrl"). - --behaviour(gen_statem). - -%% API --export([start_link/1]). --export([test/0]). - -%% gen_statem callbacks --export([init/1, format_status/2, state_name/3, handle_event/4, terminate/3, code_change/4, callback_mode/0]). - -%% rtu指令 --define(MODBUS_CONNECT, 16#01). --define(MODBUS_READ, 16#02). - -%% 当前的状态 --define(DISCONNECTED, disconnected). --define(CONNECTED, connected). - --record(state, { - port, - access_log_pid :: pid() | undefined, - error_log_pid :: pid() | undefined -}). - -%%%=================================================================== -%%% API -%%%=================================================================== - -test() -> - {ok, Config} = file:read_file("/usr/local/code/cloudkit/modbus/modbus.conf"), - %lager:debug("config is: ~ts", [Config]), - {ok, AST} = modbus_parser:parse(Config), - start_link(AST). - -%% @doc Creates a gen_statem process which calls Module:init/1 to -%% initialize. To ensure a synchronized start-up procedure, this -%% function does not return until Module:init/1 has returned. -start_link(AST = #ast{}) -> - gen_statem:start_link(?MODULE, [AST], []). - -%%%=================================================================== -%%% gen_statem callbacks -%%%=================================================================== - -%% @private -%% @doc Whenever a gen_statem is started using gen_statem:start/[3,4] or -%% gen_statem:start_link/[3,4], this function is called by the new -%% process to initialize. -init([AST = #ast{modbus = Modbus, devices = Devices}]) -> - lager:debug("modbus is: ~p", [Modbus]), - Device = hd(Devices), - lager:debug("devices is: ~p", [Device#modbus_device.metrics]), - % Res = connect(Transport), - % lager:debug("connect res: ~p", [Res]), - - {ok, DevicePid} = modbus_device:start_link(hd(Devices)), - lager:debug("device pid: ~p", [DevicePid]), - - %{ok, AccessLogPid} = modbus_logger:start_link(AccessLog), - %{ok, ErrorLogPid} = modbus_logger:start_link(ErrorLog), - - {ok, ?DISCONNECTED, #state{}}. - -%% @private -%% @doc This function is called by a gen_statem when it needs to find out -%% the callback mode of the callback module. -callback_mode() -> - handle_event_function. - -%% @private -%% @doc If callback_mode is handle_event_function, then whenever a -%% gen_statem receives an event from call/2, cast/2, or as a normal -%% process message, this function is called. - -handle_event(info, {read, SlaveId, Address}, ?CONNECTED, State = #state{port = Port}) -> - ReadCmd = <>, - Port ! {self(), {command, ReadCmd}}, - {keep_state, State}; - -%% 从port读取数据 -handle_event(info, {Port, {data, Data}}, ?CONNECTED, State = #state{port = Port}) -> - lager:debug("port data is: ~p", [Data]), - {keep_state, State}; - -handle_event(_EventType, _EventContent, _StateName, State = #state{}) -> - NextStateName = the_next_state_name, - {next_state, NextStateName, State}. - -%% @private -%% @doc This function is called by a gen_statem when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any -%% necessary cleaning up. When it returns, the gen_statem terminates with -%% Reason. The return value is ignored. -terminate(_Reason, _StateName, _State = #state{}) -> - ok. - -%% @private -%% @doc Convert process state when code is changed -code_change(_OldVsn, StateName, State = #state{}, _Extra) -> - {ok, StateName, State}. - -%%%=================================================================== -%%% Internal functions -%%%=================================================================== - -connect(#modbus{transport = #modbus_transport_rtu{port = Port, baudrate = Baudrate, stopbits = Stopbits, parity = Parity, timeout = Timeout}}) -> - RealExecCmd = "", - Port = erlang:open_port({spawn_executable, RealExecCmd}, [binary, {packet, 2}, exit_status]), - - Len0 = byte_size(Port), - ConnectCmd = <>, - %% 建立连接 - Port ! {self(), {command, ConnectCmd}}, - ok; - -connect(#modbus{transport = #modbus_transport_tcp{host = Host, port = Port, timeout = Timeout0}}) -> - Timeout = case is_integer(Timeout0) andalso Timeout0 > 0 of - true -> - Timeout0; - false -> - 2000 - end, - gen_tcp:connect(Host, Port, [binary], Timeout). \ No newline at end of file