fix parse
This commit is contained in:
parent
737cc9fbab
commit
df3e7c4346
@ -78,4 +78,11 @@
|
||||
scale = 1.0 :: float(),
|
||||
unit :: string() | undefined,
|
||||
poll = true :: boolean()
|
||||
}).
|
||||
|
||||
-record(ast, {
|
||||
modbus,
|
||||
devices = [],
|
||||
processors = [],
|
||||
alarms = []
|
||||
}).
|
||||
@ -19,9 +19,42 @@
|
||||
%% 主解析函数
|
||||
parse(Input) when is_binary(Input) ->
|
||||
Tokens = lexer(Input),
|
||||
{ok, AST} = parser(Tokens),
|
||||
try validate(AST) of
|
||||
{ok, Blocks} = parser(Tokens),
|
||||
Trees = parse_ast(Blocks),
|
||||
try validate(Trees) of
|
||||
ok ->
|
||||
%% 分组
|
||||
Modbus = lists:filter(fun(E) ->
|
||||
case E of
|
||||
#modbus{} -> true;
|
||||
_ -> false
|
||||
end
|
||||
end, Trees),
|
||||
Devices = lists:filter(fun(E) ->
|
||||
case E of
|
||||
#modbus_device{} ->
|
||||
true;
|
||||
_ -> false
|
||||
end
|
||||
end, Trees),
|
||||
Processors = lists:filter(fun(E) ->
|
||||
case E of
|
||||
#modbus_processor{} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, Trees),
|
||||
Alarms = lists:filter(fun(E) ->
|
||||
case E of
|
||||
#modbus_alarm{} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, Trees),
|
||||
|
||||
AST = #ast{modbus = Modbus, devices = Devices, processors = Processors, alarms = Alarms },
|
||||
{ok, AST}
|
||||
catch throw:Reason ->
|
||||
{error, Reason}
|
||||
@ -124,11 +157,11 @@ parse_ast0(#block{ident = <<"device", Name0/binary>>, props = Props}) ->
|
||||
MapProps = parse_ast1(Props),
|
||||
#modbus_device {
|
||||
name = string:trim(Name0),
|
||||
slave_id = maps:get(<<"slave_id">>, MapProps, undefined),
|
||||
slave_id = map_of_integer(<<"slave_id">>, MapProps, 0),
|
||||
model = maps:get(<<"model">>, MapProps, undefined),
|
||||
description = maps:get(<<"description">>, MapProps, undefined),
|
||||
poll_interval = maps:get(<<"poll_interval">>, MapProps, undefined),
|
||||
retries = maps:get(<<"retries">>, MapProps, undefined),
|
||||
retries = map_of_integer(<<"retries">>, MapProps, 0),
|
||||
retry_timeout = maps:get(<<"retry_timeout">>, MapProps, undefined),
|
||||
variables = maps:get(<<"variables">>, MapProps, undefined),
|
||||
controls = maps:get(<<"controls">>, MapProps, undefined)
|
||||
@ -212,4 +245,18 @@ validate([#modbus_device{slave_id = Id}|_]) when Id < 1 orelse Id > 247 ->
|
||||
validate([#modbus_device{variables = _Vars}|T]) ->
|
||||
validate(T);
|
||||
validate([_|T]) ->
|
||||
validate(T).
|
||||
validate(T).
|
||||
|
||||
-spec starts_with(Bin :: binary(), Prefix :: binary()) -> boolean().
|
||||
starts_with(Bin, Prefix) when is_binary(Bin), is_binary(Prefix) ->
|
||||
binary:part(Bin, 0, byte_size(Prefix)) == Prefix.
|
||||
|
||||
map_of_integer(Key, M, Def) ->
|
||||
case maps:is_key(Key, M) of
|
||||
true ->
|
||||
[Val0] = maps:get(Key, M),
|
||||
binary_to_integer(Val0);
|
||||
false ->
|
||||
Def
|
||||
end.
|
||||
|
||||
|
||||
100
apps/modbus/src/modbus_service.erl
Normal file
100
apps/modbus/src/modbus_service.erl
Normal file
@ -0,0 +1,100 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author anlicheng
|
||||
%%% @copyright (C) 2025, <COMPANY>
|
||||
%%% @doc
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 13. 6月 2025 09:46
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(modbus_service).
|
||||
-author("anlicheng").
|
||||
-include("modbus_ast.hrl").
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-record(state, {
|
||||
|
||||
}).
|
||||
|
||||
%%%===================================================================
|
||||
%%% 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({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
|
||||
%% @private
|
||||
%% @doc Initializes the server
|
||||
-spec(init(Args :: term()) ->
|
||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
||||
{stop, Reason :: term()} | ignore).
|
||||
init([]) ->
|
||||
{ok, #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}.
|
||||
|
||||
%% @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}.
|
||||
|
||||
%% @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(_Info, State = #state{}) ->
|
||||
{noreply, State}.
|
||||
|
||||
%% @private
|
||||
%% @doc This function is called by a gen_server 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{}) ->
|
||||
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}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
66
modbus.conf
66
modbus.conf
@ -86,6 +86,72 @@ device boiler_controller {
|
||||
}
|
||||
}
|
||||
|
||||
device xyz {
|
||||
# 设备标识
|
||||
slave_id 1;
|
||||
model "Siemens S7-1200";
|
||||
description "Main boiler controller";
|
||||
|
||||
# 轮询间隔
|
||||
poll_interval 5s;
|
||||
|
||||
# 重试策略
|
||||
retries 3;
|
||||
retry_timeout 2s;
|
||||
|
||||
# 数据点定义
|
||||
variables {
|
||||
# 温度读取(保持寄存器)
|
||||
temperature {
|
||||
address 40001; # Modbus地址表示法
|
||||
type int16;
|
||||
scale 0.1;
|
||||
unit "°C";
|
||||
poll on;
|
||||
}
|
||||
|
||||
# 压力传感器(输入寄存器)
|
||||
pressure {
|
||||
address 30001;
|
||||
type uint16;
|
||||
scale 0.01;
|
||||
unit "kPa";
|
||||
poll on;
|
||||
}
|
||||
|
||||
# 状态位(线圈)
|
||||
alarm_status {
|
||||
address 00001;
|
||||
type bool;
|
||||
bits {
|
||||
0 "overheat";
|
||||
1 "low_pressure";
|
||||
2 "pump_failure";
|
||||
}
|
||||
poll on;
|
||||
}
|
||||
}
|
||||
|
||||
# 写入控制
|
||||
controls {
|
||||
# 启停控制
|
||||
power_switch {
|
||||
address 00010;
|
||||
type bool;
|
||||
safe_value off;
|
||||
}
|
||||
|
||||
# PID设定值
|
||||
pid_setpoint {
|
||||
address 40010;
|
||||
type float32;
|
||||
min 0.0;
|
||||
max 100.0;
|
||||
precision 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processor temperature_processor {
|
||||
# 输入源
|
||||
input $boiler_controller.temperature;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user