GajuMine/src/gmc_conf.erl
2025-06-18 23:02:31 +09:00

255 lines
7.5 KiB
Erlang

%%% @doc
%%% GajuMine Configuration GUI
%%% @end
-module(gmc_conf).
-vsn("0.2.5").
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <craigeverett@qpq.swiss>").
-license("GPL-3.0-or-later").
-behavior(wx_object).
-include_lib("wx/include/wx.hrl").
-export([to_front/0, trouble/1]).
-export([start_link/1]).
-export([init/1, terminate/2, code_change/3,
handle_call/3, handle_cast/2, handle_info/2, handle_event/2]).
-include("$zx_include/zx_logger.hrl").
-record(s,
{wx = none :: none | wx:wx_object(),
frame = none :: none | wx:wx_object(),
lang = en :: en | jp,
j = none :: none | fun(),
net = none :: none | wx:wx_object(),
acc = none :: none | wx:wx_object(),
keys = none :: none | wx:wx_object(),
cores = none :: none | wx:wx_object(),
memory = none :: none | wx:wx_object()}).
%%% Interface functions
-spec to_front() -> ok.
to_front() ->
wx_object:cast(?MODULE, to_front).
-spec trouble(term()) -> ok.
trouble(Info) ->
wx_object:cast(?MODULE, {trouble, Info}).
%%% Startup Functions
start_link(Prefs) ->
wx_object:start_link({local, ?MODULE}, ?MODULE, Prefs, []).
init({Prefs, {Net, Acc, Keys, {AProcs, AMem, MProcs, MMem}}}) ->
ok = log(info, "GUI starting..."),
Lang = maps:get(lang, Prefs, en_US),
Trans = gd_jt:read_translations(?MODULE),
J = gd_jt:j(Lang, Trans),
Label = J("Configure GajuMine"),
WX = wx:new(),
Frame = wxFrame:new(WX, ?wxID_ANY, Label),
MainSz = wxBoxSizer:new(?wxVERTICAL),
AccSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("GajuMining Account ID")}]),
AccTx = wxTextCtrl:new(Frame, ?wxID_ANY, [{value, acc(Acc)}]),
_ = wxStaticBoxSizer:add(AccSz, AccTx, wide(5)),
KeysSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Additional Account IDs (optional)")}]),
KeysTx = wxTextCtrl:new(Frame, ?wxID_ANY, [{style, ?wxTE_MULTILINE}, {value, keys(Keys)}]),
_ = wxStaticBoxSizer:add(KeysSz, KeysTx, wide(5)),
StatSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Max System Committment (optional)")}]),
AProcsS = i_to_l(AProcs),
MProcsS = i_to_l(MProcs),
AMemS = gigs(AMem),
MMemS = gigs(MMem),
Labels = [{J("HW Cores"), J("Max Cores"), AProcsS, MProcsS}, {J("Memory (GB)"), J("Max Memory"), AMemS, MMemS}],
{Grid, [CoresTx, MemoryTx]} = display_box(Frame, Labels),
Network = wxRadioBox:new(Frame, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]),
ok = wxRadioBox:setSelection(Network, net_num(Net)),
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
Affirm = wxButton:new(Frame, ?wxID_OK),
Cancel = wxButton:new(Frame, ?wxID_CANCEL),
_ = wxBoxSizer:add(StatSz, Grid, wide(5)),
_ = wxBoxSizer:add(ButtSz, Affirm, wide(5)),
_ = wxBoxSizer:add(ButtSz, Cancel, wide(5)),
_ = wxBoxSizer:add(MainSz, AccSz, base(5)),
_ = wxBoxSizer:add(MainSz, KeysSz, wide(5)),
_ = wxBoxSizer:add(MainSz, StatSz, base(5)),
_ = wxBoxSizer:add(MainSz, Network, base(5)),
_ = wxBoxSizer:add(MainSz, ButtSz, base(5)),
ok = wxFrame:setSizer(Frame, MainSz),
ok = wxBoxSizer:layout(MainSz),
ok = wxFrame:connect(Frame, command_button_clicked),
ok = wxFrame:connect(Frame, close_window),
ok = wxFrame:setSize(Frame, {500, 500}),
ok = wxFrame:center(Frame),
true = wxFrame:show(Frame),
State =
#s{wx = WX, frame = Frame,
lang = Lang, j = J,
net = Network, acc = AccTx, keys = KeysTx,
cores = CoresTx, memory = MemoryTx},
{Frame, State}.
i_to_l(none) -> "0";
i_to_l(N) when is_integer(N) -> integer_to_list(N).
base(Padding) -> [{proportion, 0}, {flag, ?wxEXPAND bor ?wxALL}, {border, Padding}].
wide(Padding) -> [{proportion, 1}, {flag, ?wxEXPAND bor ?wxALL}, {border, Padding}].
center() -> [{proportion, 0}, {flag, ?wxEXPAND bor ?wxALIGN_CENTER_VERTICAL}].
gigs(none) ->
"0";
gigs(Bytes) ->
float_to_list(Bytes / gig(), [{decimals, 2}, compact]).
gig() -> 1_073_741_824.
display_box(Parent, Labels) ->
Grid = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(Grid, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(Grid, 1),
Make =
fun({SO, SI, VS, VM}) ->
LO = [SO, ":"],
LI = [SI, ":"],
T_LO = wxStaticText:new(Parent, ?wxID_ANY, LO),
T_CO = wxStaticText:new(Parent, ?wxID_ANY, VS, [{style, ?wxALIGN_LEFT}]),
T_LI = wxStaticText:new(Parent, ?wxID_ANY, LI),
T_CI = wxTextCtrl:new(Parent, ?wxID_ANY, [{value, VM}]),
_ = wxFlexGridSizer:add(Grid, T_LO, center()),
_ = wxFlexGridSizer:add(Grid, T_CO, center()),
_ = wxFlexGridSizer:add(Grid, T_LI, center()),
_ = wxFlexGridSizer:add(Grid, T_CI, zxw:flags(wide)),
T_CI
end,
Controls = lists:map(Make, Labels),
{Grid, Controls}.
handle_call(Unexpected, From, State) ->
ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]),
{noreply, State}.
handle_cast(to_front, State = #s{frame = Frame}) ->
ok = wxFrame:raise(Frame),
{noreply, State};
handle_cast({trouble, Info}, State) ->
ok = handle_troubling(State, Info),
{noreply, State};
handle_cast(Unexpected, State) ->
ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]),
{noreply, State}.
handle_info(Unexpected, State) ->
ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
{noreply, State}.
handle_event(#wx{event = #wxCommand{type = command_button_clicked}, id = ID}, State) ->
ok =
case ID of
?wxID_OK -> done(State);
?wxID_CANCEL -> buh_bye(State)
end,
{noreply, State};
handle_event(#wx{event = #wxClose{}}, State) ->
ok = buh_bye(State),
{noreply, State};
handle_event(Event, State) ->
ok = tell(info, "Unexpected event ~tp State: ~tp~n", [Event, State]),
{noreply, State}.
handle_troubling(#s{frame = Frame}, Info) ->
zxw:show_message(Frame, Info).
code_change(_, State, _) ->
{ok, State}.
terminate(Reason, State) ->
ok = log(info, "Reason: ~tp, State: ~tp", [Reason, State]),
wx:destroy().
buh_bye(#s{frame = Frame}) ->
wxWindow:destroy(Frame).
%%% Doers
done(State = #s{net = Network, acc = AccTx, keys = KeysTx, cores = CoresTx, memory = MemTx}) ->
Net =
case wxRadioBox:getSelection(Network) of
0 -> <<"mainnet">>;
1 -> <<"testnet">>;
_ -> <<"mainnet">>
end,
AccID = wxTextCtrl:getValue(AccTx),
MOAR_IDs = wxTextCtrl:getValue(KeysTx),
CoreS = wxTextCtrl:getValue(CoresTx),
GigsS = wxTextCtrl:getValue(MemTx),
ok = gmc_con:conf({Net, list_to_binary(AccID), binify_keys(MOAR_IDs), cores(CoreS), bytes(GigsS)}),
buh_bye(State).
cores("") ->
none;
cores(CoreS) ->
try
list_to_integer(CoreS)
catch
_:_ -> none
end.
bytes("") ->
none;
bytes(GigsS) ->
try
list_to_integer(GigsS) * gig()
catch
_:_ ->
try
trunc(list_to_float(GigsS) * gig())
catch
_:_ -> none
end
end.
% NOTE: 32 is space, 12288 is full-width space.
binify_keys(MOAR) ->
[list_to_binary(K) || K <- string:lexemes(MOAR, [$\r, $\n, 32, 12288, $\t, $,, $;])].
acc(none) -> "";
acc(AKID) -> AKID.
keys(List) ->
unicode:characters_to_list(lists:join("\n", List)).
net_num(<<"mainnet">>) -> 0;
net_num(<<"testnet">>) -> 1;
net_num(_) -> 0.