@ -0,0 +1,29 @@ | |||
.eunit | |||
*.o | |||
*.beam | |||
*.plt | |||
erl_crash.dump | |||
.concrete/DEV_MODE | |||
# rebar 2.x | |||
.rebar | |||
rel/example_project | |||
ebin/* | |||
deps | |||
# rebar 3 | |||
.rebar3 | |||
_build/ | |||
_checkouts/ | |||
rebar.lock | |||
# idea | |||
.idea | |||
*.iml | |||
cmake-build* | |||
CMakeLists.txt | |||
# nif compile temp file | |||
*.pdb | |||
*.d | |||
compile_commands.json |
@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) 2021 AICells | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@ -0,0 +1,17 @@ | |||
eFaw | |||
===== | |||
An OTP application | |||
Build | |||
----- | |||
$ rebar3 compile | |||
Useage | |||
------ | |||
1 write you worker mod like: fwWtp.erl | |||
2 open your factory : eFaw:openF(myFactory, [{wMod, fwWtp}, ...]), more option see eFaw.hrl | |||
3 send your async task to your factory: eFaw:inWork(myFactory, [{report_log, xxxx}, {write_log, yyyyyy}]). | |||
4 apply your sync task to your factory: eFaw:syncWork(myFactory, retTag, 5000, {report_log, xxxx}). | |||
5 then worker auto do the task |
@ -0,0 +1,24 @@ | |||
-define(wMod, wMod). %% 工人任务处理模块 | |||
-define(wFCnt, wFCnt). %% 固定工人人数 | |||
-define(wTCnt, wTCnt). %% 可以聘请的零时工数量 | |||
-define(wTLive, wTLive). %% 零时工最大空闲时间单位(s) 空闲超时之后就解雇 | |||
-define(fTpm, fTpm). %% 工厂任务处理模式 fifo lifo | |||
-define(fTLfl, fTLfl). %% 工厂任务承载线 超过该值就可以聘请零时工了 0 固定工人数 工厂负载超过该值时增加零时工 | |||
-define(fTMax, fTMax). %% 工厂最大负载量 超过该值时 工厂就不再接受任务了 | |||
-type fawOtp() :: {?wMod, atom()} |{?wFCnt, pos_integer()} |{?wTCnt, pos_integer()} |{?wTLive, pos_integer()} |{?fTpm, fifo | lifo} |{?fTLfl, pos_integer()} | {?fTMax, pos_integer()}. | |||
-define(FawDefV, [ | |||
, {?wMod, fwWTP} | |||
, {?wFCnt, 30} | |||
, {?wTCnt, 20} | |||
, {?wTLive, 300} | |||
, {?fTpm, fifo} | |||
, {?fTLfl, 10000} | |||
, {?fTMax, 10000} | |||
]). | |||
-type fawOtps() :: [fawOtp(), ...]. | |||
-define(ERR(Format, Args), error_logger:error_msg(Format, Args)). | |||
@ -0,0 +1,10 @@ | |||
{erl_opts, [debug_info]}. | |||
{deps, [ | |||
{eGbh, ".*", {git, "http://sismaker.tpddns.cn:53000/SisMaker/eGbh.git", {branch, "master"}}}, | |||
{eSync, ".*", {git, "http://sismaker.tpddns.cn:53000/SisMaker/eSync.git", {branch, "master"}}} | |||
]}. | |||
{shell, [ | |||
% {config, "config/sys.config"}, | |||
{apps, [eFaw]} | |||
]}. |
@ -0,0 +1,86 @@ | |||
-module(fwFMgr). | |||
-behavior(gen_srv). | |||
-include("eFaw.hrl"). | |||
-export([ | |||
start_link/0 | |||
]). | |||
-export([ | |||
init/1 | |||
, handleCall/3 | |||
, handleCast/2 | |||
, handleInfo/2 | |||
, terminate/2 | |||
, code_change/3 | |||
]). | |||
-define(SERVER, ?MODULE). | |||
%% ******************************************** API ******************************************************************* | |||
start_link() -> | |||
ut_gen_srv:start_link({local, ?SERVER}, ?MODULE, [], []). | |||
%% ******************************************** callback ************************************************************** | |||
init(_Args) -> | |||
{ok, #{}}. | |||
handleCall(_Msg, _State, _FROM) -> | |||
?ERR("~p call receive unexpect msg ~p ~n ", [?MODULE, _Msg]), | |||
{reply, ok}. | |||
%% 默认匹配 | |||
handleCast(_Msg, _State) -> | |||
?ERR("~p cast receive unexpect msg ~p ~n ", [?MODULE, _Msg]), | |||
kpS. | |||
handleInfo({mChAddW, FName}, _State) -> | |||
WTCnt = FName:getV(?wTCnt), | |||
case WTCnt > 0 of | |||
true -> | |||
WFCnt = FName:getV(?wFCnt), | |||
Counts = supervisor:count_children(FName), | |||
{_, WorkerCnt} = lists:keyfind(workers, 1, Counts), | |||
AddCnt = WTCnt + WFCnt - WorkerCnt, | |||
case AddCnt > 0 of | |||
true -> | |||
hireW(AddCnt, FName, true); | |||
_ -> | |||
ignore | |||
end; | |||
_ -> | |||
ignore | |||
end, | |||
kpS; | |||
handleInfo({mChAwkW, FName}, State) -> | |||
case State of | |||
#{FName := PidList} -> | |||
case PidList of | |||
[OnePid | LeftList] -> | |||
NewState = State#{FName := LeftList}, | |||
gen_srv:send(OnePid, tryWork), | |||
{noreply, NewState}; | |||
_ -> | |||
kpS | |||
end | |||
end; | |||
handleInfo({mWSleep, FName, Pid}, State) -> | |||
NewState = | |||
case State of | |||
#{FName := OldList} -> | |||
State#{FName := [Pid | OldList]}; | |||
_ -> | |||
State#{FName => [Pid]} | |||
end, | |||
{noreply, NewState}; | |||
handleInfo(_Msg, _State) -> | |||
?ERR("~p info receive unexpect msg ~p ~n ", [?MODULE, _Msg]), | |||
kpS. | |||
terminate(_Reason, _State) -> | |||
ok. | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. |
@ -0,0 +1,42 @@ | |||
-module(fwKvsToBeam). | |||
-export([ | |||
load/2 | |||
]). | |||
%% 注意 map类型的数据不能当做key | |||
-type key() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple(). | |||
-type value() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple() | map(). | |||
-spec load(term(), [{key(), value()}]) -> ok. | |||
load(Module, KVs) -> | |||
Forms = forms(Module, KVs), | |||
{ok, Module, Bin} = compile:forms(Forms), | |||
code:soft_purge(Module), | |||
{module, Module} = code:load_binary(Module, atom_to_list(Module), Bin), | |||
ok. | |||
forms(Module, KVs) -> | |||
%% -module(Module). | |||
Mod = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]), | |||
%% -export([getV/0]). | |||
ExportList = [erl_syntax:arity_qualifier(erl_syntax:atom(getV), erl_syntax:integer(1))], | |||
Export = erl_syntax:attribute(erl_syntax:atom(export), [erl_syntax:list(ExportList)]), | |||
%% getV(K) -> V | |||
Function = erl_syntax:function(erl_syntax:atom(getV), lookup_clauses(KVs, [])), | |||
[erl_syntax:revert(X) || X <- [Mod, Export, Function]]. | |||
lookup_clause(Key, Value) -> | |||
Var = erl_syntax:abstract(Key), | |||
Body = erl_syntax:abstract(Value), | |||
erl_syntax:clause([Var], [], [Body]). | |||
lookup_clause_anon() -> | |||
Var = erl_syntax:variable("_"), | |||
Body = erl_syntax:atom(undefined), | |||
erl_syntax:clause([Var], [], [Body]). | |||
lookup_clauses([], Acc) -> | |||
lists:reverse(lists:flatten([lookup_clause_anon() | Acc])); | |||
lookup_clauses([{Key, Value} | T], Acc) -> | |||
lookup_clauses(T, [lookup_clause(Key, Value) | Acc]). |
@ -0,0 +1,60 @@ | |||
-module(fwQueue). | |||
-export([ | |||
new/1 | |||
, del/1 | |||
, in/2 | |||
, ins/2 | |||
, outF/1 | |||
, outL/1 | |||
, clear/1 | |||
, size/1 | |||
]). | |||
-spec new(Name :: atom()) -> ok | name_used. | |||
new(Name) -> | |||
case ets:info(Name, id) of | |||
undefined -> | |||
ets:new(Name, [ordered_set, public, named_table, {write_concurrency, true}]); | |||
_ -> | |||
name_used | |||
end. | |||
-spec del(Name :: atom()) -> ok. | |||
del(Name) -> | |||
ets:delete(Name). | |||
-spec in(Name :: atom(), Value :: term()) -> true. | |||
in(Name, Value) -> | |||
ets:insert(Name, {erlang:unique_integer(), Value}). | |||
-spec ins(Name :: atom(), Values :: [term()]) -> true. | |||
ins(Name, Values) -> | |||
[ets:insert(Name, {erlang:unique_integer(), Value}) || Value <- Values], | |||
true. | |||
-spec out(Name :: atom()) -> empty | Value :: term(). | |||
outF(Name) -> | |||
case ets:first_take(Name) of | |||
[] -> | |||
empty; | |||
[{_, Value}] -> | |||
Value | |||
end. | |||
-spec out(Name :: atom()) -> empty | Value :: term(). | |||
outL(Name) -> | |||
case ets:last_take(Name) of | |||
[] -> | |||
empty; | |||
[{_, Value}] -> | |||
Value | |||
end. | |||
-spec clear(Name :: atom()) -> ok. | |||
clear(Name) -> | |||
ets:delete_all_objects(Name). | |||
-spec size(Name :: atom()) -> Size :: integer() | undefined. | |||
size(Name) -> | |||
ets:info(Name, size). |
@ -0,0 +1,46 @@ | |||
-module(fwUtil). | |||
-export([ | |||
tryWorkOnce/4 | |||
, tryWorkLoop/4 | |||
]). | |||
tryWorkOnce(FName, Mod, WStrategy, State) -> | |||
case WStrategy of | |||
fifo -> | |||
Task = fwQueue:outF(FName); | |||
_ -> | |||
Task = fwQueue:outL(FName) | |||
end, | |||
case Task of | |||
empty -> | |||
Mod:idle(State); | |||
_ -> | |||
try Mod:work(Task, State) of | |||
NewState -> | |||
NewState | |||
catch | |||
_C:_R:_S -> State | |||
end | |||
end. | |||
tryWorkLoop(FName, Mod, WStrategy, State) -> | |||
case WStrategy of | |||
fifo -> | |||
Task = fwQueue:outF(FName); | |||
_ -> | |||
Task = fwQueue:outL(FName) | |||
end, | |||
case Task of | |||
empty -> | |||
Mod:idle(State); | |||
_ -> | |||
NewState = | |||
try Mod:work(Task, State) of | |||
TemState -> | |||
TemState | |||
catch | |||
_C:_R:_S -> State | |||
end, | |||
tryWorkLoop(FName, Mod, WStrategy, NewState) | |||
end. |
@ -0,0 +1,26 @@ | |||
-module(fwWSup). | |||
-behavior(supervisor). | |||
-export([start_link/2]). | |||
-export([init/1]). | |||
-define(SERVER, ?MODULE). | |||
start_link(FName, Mod) -> | |||
supervisor:start_link({local, FName}, ?MODULE, [FName, Mod]). | |||
init([FName, Mod]) -> | |||
SupFlags = #{strategy => simple_one_for_one, intensity => 100, period => 3600}, | |||
ChildSpecs = [ | |||
#{ | |||
id => Mod, | |||
start => {Mod, start_link, [FName]}, | |||
restart => permanent, | |||
shutdown => 30000, | |||
type => worker, | |||
modules => [Mod] | |||
} | |||
], | |||
{ok, {SupFlags, ChildSpecs}}. |
@ -0,0 +1,73 @@ | |||
-module(fwWTP). | |||
-behavior(gen_srv). | |||
-include("eFaw.hrl"). | |||
-export([ | |||
start_link/2 | |||
]). | |||
%% worker back | |||
-export([ | |||
idle/1 | |||
, work/2 | |||
, close/1 | |||
]). | |||
-export([ | |||
init/1 | |||
, handleCall/3 | |||
, handleCast/2 | |||
, handleInfo/2 | |||
, terminate/2 | |||
, code_change/3 | |||
]). | |||
-define(SERVER, ?MODULE). | |||
-record(state, {fName :: ets:tid(), isTmp = false :: boolean()}). | |||
%% ******************************************** API ******************************************************************* | |||
start_link(FName, IsTmp) -> | |||
gen_srv:start_link({local, ?SERVER}, ?MODULE, [FName, IsTmp], []). | |||
%% ******************************************** callback ************************************************************** | |||
init([FName, IsTmp]) -> | |||
erlang:process_flag(trap_exit, true), | |||
{ok, #state{fName = FName, isTmp = IsTmp}}. | |||
handleCall(_Msg, _State, _FROM) -> | |||
?ERR("~p call receive unexpect msg ~p ~n ", [?MODULE, _Msg]), | |||
{reply, ok}. | |||
%% 默认匹配 | |||
handleCast(_Msg, _State) -> | |||
?ERR("~p cast receive unexpect msg ~p ~n ", [?MODULE, _Msg]), | |||
kpS. | |||
handleInfo(tryWork, _State) -> | |||
%fwUtil:tryWorkLoop(xxxxxxxx); | |||
NewState = fwUtil:tryWorkOnce(), | |||
{noreply, NewState}; | |||
handleInfo(_Msg, _State) -> | |||
?ERR("~p info receive unexpect msg ~p ~n ", [?MODULE, _Msg]), | |||
kpS. | |||
terminate(_Reason, _State) -> | |||
ok. | |||
code_change(_OldVsn, State, _Extra) -> | |||
{ok, State}. | |||
work(task1, State) -> | |||
State; | |||
work(task1, State) -> | |||
State; | |||
work(_Task, State) -> | |||
State. | |||
idle(State) -> | |||
State. | |||
close(State) -> | |||
State. |
@ -0,0 +1,12 @@ | |||
{application, eFaw, | |||
[{description, "An OTP application"}, | |||
{vsn, "0.1.0"}, | |||
{registered, []}, | |||
{mod, {eFaw_app, []}}, | |||
{applications, [kernel, stdlib]}, | |||
{env, []}, | |||
{modules, []}, | |||
{licenses, ["MIT"]}, | |||
{links, []} | |||
]}. |
@ -0,0 +1,143 @@ | |||
-module(eFaw). | |||
-include("eFaw.hrl"). | |||
-export([ | |||
start/0 %% 启动应用 | |||
, stop/0 %% 停止应用 | |||
, openF/2 %% 开个工厂 | |||
, closeW/1 %% 关闭工厂 | |||
, hireW/3 %% 录用工人 | |||
, inWork/2 %% 插入异步工作 | |||
, inWorks/2 %% 插入异步工作 | |||
, syncWork/4 %% 插入同步工作 并等待返回结果 | |||
]). | |||
start() -> | |||
application:ensure_all_started(eFaw). | |||
stop() -> | |||
application:stop(eFaw). | |||
initCfg(Kvs) -> | |||
[ | |||
begin | |||
case lists:keyfind(Key, 1, Kvs) of | |||
false -> | |||
{Key, DefV}; | |||
Tuple -> | |||
Tuple | |||
end | |||
end | |||
|| {Key, DefV} <- ?FawDefV | |||
]. | |||
openF(FName, Kvs) -> | |||
FChildSpec = #{ | |||
id => FName, | |||
start => {fwWSup, start_link, [FName]}, | |||
restart => permanent, | |||
shutdown => 300000, | |||
type => supervisor, | |||
modules => [fwWSup] | |||
}, | |||
Ret = supervisor:start_child(eFaw_sup, FChildSpec), | |||
fwKvsToBeam:load(FName, initCfg(Kvs)), | |||
hireW(FName:getV(?wFCnt), FName, false), | |||
Ret. | |||
hireW(WorkerNum, FName, IsTmp) when is_integer(WorkerNum), WorkerNum > 0 -> | |||
supervisor:start_child(FName, [IsTmp]), | |||
hireW(WorkerNum - 1, FName, IsTmp); | |||
hireW(_WorkerNum, _FName, _IsTmp) -> | |||
ok. | |||
closeW(FName) -> | |||
supervisor:terminate_child(eFaw_sup, FName). | |||
-spec inWork(FName :: atom(), Work :: term()) -> true | false. | |||
inWork(FName, Work) -> | |||
FTaskLen = fwQueue:size(FName), | |||
FTMax = FName:getV(?fTMax), | |||
FTLfl = FName:getV(?fTLfl), | |||
WFCnt = FName:getV(?wFCnt), | |||
if | |||
FTaskLen > FTMax -> | |||
%% 查看是否超过工厂负载; | |||
false; | |||
FTaskLen == FTLfl -> | |||
%% 查看是否需要雇佣零时工 ; | |||
gen_srv:send(fwFMgr, mChAddW), | |||
fwQueue:in(FName, Work); | |||
FTaskLen < WFCnt -> | |||
%% 查看是否需要唤醒空闲的工人 | |||
gen_srv:send(fwFMgr, mChAwkW), | |||
fwQueue:in(FName, Work); | |||
true -> | |||
fwQueue:in(FName, Work) | |||
end. | |||
-spec inWorks(FName :: atom(), Works :: [term(), ...]) -> true | false. | |||
inWorks(FName, Works) -> | |||
FTaskLen = fwQueue:size(FName), | |||
FTMax = FName:getV(?fTMax), | |||
FTLfl = FName:getV(?fTLfl), | |||
WFCnt = FName:getV(?wFCnt), | |||
if | |||
FTaskLen > FTMax -> | |||
%% 查看是否超过工厂负载; | |||
false; | |||
FTaskLen == FTLfl -> | |||
%% 查看是否需要雇佣零时工 ; | |||
gen_srv:send(fwFMgr, mChAddW), | |||
fwQueue:ins(FName, Works); | |||
FTaskLen < WFCnt -> | |||
%% 查看是否需要唤醒空闲的工人 | |||
gen_srv:send(fwFMgr, mChAwkW), | |||
fwQueue:ins(FName, Works); | |||
true -> | |||
fwQueue:ins(FName, Works) | |||
end. | |||
-spec syncWork(FName :: atom(), Work :: term()) -> true | false. | |||
syncWork(FName, RetTag, Timeout, Work) -> | |||
FTaskLen = fwQueue:size(FName), | |||
FTMax = FName:getV(?fTMax), | |||
FTLfl = FName:getV(?fTLfl), | |||
WFCnt = FName:getV(?wFCnt), | |||
if | |||
FTaskLen > FTMax -> | |||
%% 查看是否超过工厂负载; | |||
false; | |||
FTaskLen == FTLfl -> | |||
%% 查看是否需要雇佣零时工 ; | |||
gen_srv:send(fwFMgr, mChAddW), | |||
fwQueue:in(FName, Work), | |||
receive | |||
{RetTag, Ret} -> | |||
Ret | |||
after Timeout -> | |||
timeout | |||
end; | |||
FTaskLen < WFCnt -> | |||
%% 查看是否需要唤醒空闲的工人 | |||
gen_srv:send(fwFMgr, mChAwkW), | |||
fwQueue:in(FName, Work), | |||
receive | |||
{RetTag, Ret} -> | |||
Ret | |||
after Timeout -> | |||
timeout | |||
end; | |||
true -> | |||
fwQueue:in(FName, Work), | |||
receive | |||
{RetTag, Ret} -> | |||
Ret | |||
after Timeout -> | |||
timeout | |||
end | |||
end. | |||
@ -0,0 +1,11 @@ | |||
-module(eFaw_app). | |||
-behaviour(application). | |||
-export([start/2, stop/1]). | |||
start(_StartType, _StartArgs) -> | |||
eFaw_sup:start_link(). | |||
stop(_State) -> | |||
ok. |
@ -0,0 +1,26 @@ | |||
-module(eFaw_sup). | |||
-behaviour(supervisor). | |||
-export([start_link/0]). | |||
-export([init/1]). | |||
-define(SERVER, ?MODULE). | |||
start_link() -> | |||
supervisor:start_link({local, ?SERVER}, ?MODULE, []). | |||
init([]) -> | |||
SupFlags = #{strategy => one_for_one, intensity => 100, period => 3600}, | |||
ChildSpecs = [ | |||
#{ | |||
id => fwFMgr, | |||
start => {fwFMgr, start_link, []}, | |||
restart => permanent, | |||
shutdown => 3000, | |||
type => worker, | |||
modules => [fwFMgr] | |||
} | |||
], | |||
{ok, {SupFlags, ChildSpecs}}. |