Compare commits

..

36 Commits

Author SHA1 Message Date
cee09ebf58 Merge pull request 'Deps update' (#19) from deps-0.4.2 into master
Reviewed-on: #19
2025-12-03 09:34:31 +09:00
9495b606b4 Deps update 2025-12-03 09:35:03 +09:00
3fb82b59a8 Merge branch 'master' into uw-is_key-type-error 2025-11-21 11:57:57 +09:00
eb6a94a169 Merge pull request 'Fix key check in config' (#18) from fixcheck into master
Reviewed-on: #18
2025-11-21 11:50:32 +09:00
54802cf607 Fix key check in config 2025-11-21 11:50:41 +09:00
Ulf Wiger
b1e649961c Correct type error in gmc_conf:is_key/1 2025-11-20 20:35:32 +01:00
68cc86723d Merge pull request 'iface' (#16) from iface into master
Reviewed-on: #16
2025-11-20 16:02:06 +09:00
30dda02cc4 MacOS works now 2025-11-20 10:39:26 +09:00
8ee712e81a Windows works! 2025-11-20 10:04:19 +09:00
4b6a4372d9 Fix wallet launch 2025-11-20 09:10:52 +09:00
5b95daa27e Interface update. v0.4.0 2025-11-20 00:52:19 +09:00
54a70d4b0b WIP 2025-11-19 21:17:57 +09:00
6d0d371d5d Update deps 2025-11-07 19:56:00 +09:00
a47a259135 Fixing interface issues and GajuDesk startup call 2025-11-07 19:53:16 +09:00
c6ab147d97 Move to gmhive_client-0.9.3 2025-10-25 17:28:45 +09:00
c421ee1785 Update gmhive_client 2025-10-25 09:31:01 +09:00
a5dea54919 usort additional keys list 2025-10-15 21:01:25 +09:00
159d522522 Deps update + patch verup 2025-10-15 20:56:35 +09:00
97c546610d Verup deps, and start listening for gproc messages at startup 2025-10-15 15:52:46 +09:00
cb1d879b99 Handle whitespace around account ID 2025-10-14 21:55:04 +09:00
66a06480d5 Merge pull request 'Match on more messages, verup dependencies/' (#11) from deps-0.3.1 into master
Reviewed-on: #11
Reviewed-by: Jarvis Carroll <jarviscarrol@qpq.swiss>
2025-09-24 15:46:18 +09:00
33d6edaa42 Updating gmhive_client one more time... 2025-09-24 15:45:41 +09:00
86ac9be07d Match on more messages, verup dependencies/ 2025-09-23 19:24:57 +09:00
9b88ac7a90 Add autostart argument 2025-08-14 11:11:00 +09:00
ff538018c5 verup 2025-08-14 10:47:51 +09:00
56be96c4f3 Update system arch discovery method 2025-08-14 10:41:52 +09:00
5e3e8c9eae Make version update stick 2025-08-12 09:54:47 +09:00
ecf597eed2 Update cuckoo_cpu dep 2025-08-12 09:48:31 +09:00
0ec8109684 Merge pull request 'Fix mac path' (#9) from mac-path into master
Reviewed-on: #9
2025-08-12 08:22:18 +09:00
ca0d48b498 Fix mac path 2025-08-12 08:17:08 +09:00
7eaacaddce Patch fix to make Windows use generic until we fix AVX2 2025-08-11 17:43:08 +09:00
be63fe141b Merge pull request 'naive-arch2' (#8) from naive-arch2 into master
Reviewed-on: #8
Reviewed-by: Jarvis Carroll <jarviscarrol@qpq.swiss>
2025-08-11 15:18:03 +09:00
06907a6c8b Update deps 2025-08-11 14:44:10 +09:00
7feea1421b make init more robust 2025-08-11 14:34:51 +09:00
5d4fd6a2ab Merge pull request 'Add Mac arch detection (relatively naive)' (#7) from naive-arch2 into master
Reviewed-on: #7
Reviewed-by: Jarvis Carroll <jarviscarrol@qpq.swiss>
2025-08-11 12:19:21 +09:00
35842b8ac7 Add Mac arch detection (relatively naive) 2025-08-08 13:00:00 +09:00
7 changed files with 290 additions and 135 deletions

View File

@ -3,6 +3,6 @@
{registered,[]},
{included_applications,[]},
{applications,[stdlib,kernel]},
{vsn,"0.2.5"},
{vsn,"0.4.2"},
{modules,[gajumine,gmc_con,gmc_conf,gmc_gui,gmc_sup]},
{mod,{gajumine,[]}}]}.

View File

@ -3,7 +3,7 @@
%%% @end
-module(gajumine).
-vsn("0.2.5").
-vsn("0.4.2").
-behavior(application).
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <craigeverett@qpq.swiss>").
@ -29,6 +29,7 @@ start(normal, _Args) ->
{ok, _} = application:ensure_all_started(ssl),
ok = application:ensure_started(hakuzaru),
ok = application:ensure_started(zxwidgets),
ok = application:ensure_started(gproc),
gmc_sup:start_link().

View File

@ -3,7 +3,7 @@
%%% @end
-module(gmc_con).
-vsn("0.2.5").
-vsn("0.4.2").
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <craigeverett@qpq.swiss>").
-license("GPL-3.0-or-later").
@ -23,15 +23,16 @@
-record(s,
{version = 1 :: integer(),
window = none :: none | wx:wx_object(),
network = <<"mainnet">> :: binary(), % <<"testnet">> | <<"mainnet">>
acc = none :: none | binary(),
keys = [] :: [],
max_cores = 2 :: pos_integer(),
max_mem = 3550722201 :: pos_integer(),
prefs = #{} :: #{},
gmc_conf = none :: none | reference()}).
{version = 1 :: integer(),
window = none :: none | wx:wx_object(),
network = <<"mainnet">> :: binary(), % <<"testnet">> | <<"mainnet">>
acc = none :: none | binary(),
keys = [] :: [],
max_cores = 2 :: pos_integer(),
max_mem = 3550722201 :: pos_integer(),
prefs = #{} :: #{},
gmc_conf = none :: none | reference(),
toggle = {stop, 0} :: {start | stop, integer()}}).
-type state() :: #s{}.
@ -101,18 +102,38 @@ start_link() ->
init(none) ->
ok = log(info, "Starting"),
{AProcs, AMem} = proc_mem(),
TwoGraphs = default_spec(mem) * 2,
RProcs =
case AProcs >= 2 of
true -> 2;
false -> 1
end,
RMem =
case AMem >= TwoGraphs of
true -> TwoGraphs;
false -> default_spec(mem)
end,
{Acc, Keys, Network, MaxCores, MaxMem} =
case read_conf() of
{ok, C} ->
{maps:get(account, C, none),
maps:get(keys, C, []),
maps:get(network, C, <<"mainnet">>),
maps:get(max_cores, C, 2),
maps:get(max_mem, C, 3550722201)};
maps:get(max_cores, C, RProcs),
maps:get(max_mem, C, RMem)};
none ->
{none, [], <<"mainnet">>, 2, 3550722201}
{none, [], <<"mainnet">>, RProcs, RMem}
end,
State = #s{network = Network, acc = Acc, keys = Keys, max_cores = MaxCores, max_mem = MaxMem},
Events =
[pool_notification,
connected,
puzzle,
result,
error,
disconnected],
ok = lists:foreach(fun gmhc_events:ensure_subscribed/1, Events),
{ok, start_gui(State)}.
@ -124,6 +145,11 @@ start_gui(State = #s{acc = none}) ->
start_gui(State = #s{acc = AccID}) ->
Window = gmc_gui:start_link(#{}),
ok = gmc_gui:set_account(AccID),
ok =
case lists:member("autostart", zx_daemon:argv()) of
false -> ok;
true -> gmc_gui:start_stop()
end,
State#s{window = Window}.
@ -138,8 +164,8 @@ handle_call(Unexpected, From, State) ->
handle_cast(start_stop, State) ->
ok = do_start_stop(State),
{noreply, State};
NewState = do_start_stop(State),
{noreply, NewState};
handle_cast(gajudesk, State) ->
ok = do_gajudesk(),
{noreply, State};
@ -159,7 +185,7 @@ handle_cast(Unexpected, State) ->
handle_info({gproc_ps_event, Event, Data}, State) ->
ok = gmc_gui:message({Event, Data}),
ok = gproc_ps_event({Event, Data}),
{noreply, State};
handle_info({'DOWN', Mon, process, PID, Info}, State = #s{gmc_conf = Mon}) ->
ok = log(info, "gmc_conf ~p closed with ~p", [PID, Info]),
@ -170,6 +196,14 @@ handle_info(Unexpected, State) ->
{noreply, State}.
gproc_ps_event(Info = {disconnected, #{info := #{reconnecting := false, error := #{code := Code, message := Message}}}}) ->
ok = log(error, "Error terminal event received. Code: ~p. Message: ~p", [Code, Message]),
ok = gmc_gui:message(Info),
gmc_gui:start_stop();
gproc_ps_event(Info) ->
gmc_gui:message(Info).
code_change(_, State, _) ->
{ok, State}.
@ -187,15 +221,25 @@ terminate(Reason, State) ->
do_stop() ->
case is_pid(whereis(gd_con)) of
false -> zx:stop();
true -> application:stop(gajumine)
end.
zx:stop().
%%% Doers
do_start_stop(#s{acc = PubKey, keys = Keys, network = Network, max_cores = MaxCores, max_mem = MaxMem}) ->
do_start_stop(State = #s{toggle = {Last, TS}}) ->
Now = erlang:system_time(second),
Clicklimit = Now - 1,
case TS < Clicklimit of
true ->
case Last of
stop -> do_start(State#s{toggle = {start, Now}});
start -> do_stop(State#s{toggle = {stop, Now}})
end;
false -> State
end.
do_start(State = #s{acc = PubKey, keys = Keys, network = Network, max_cores = MaxCores, max_mem = MaxMem}) ->
% smrt guy stuff happens here
{Bits, Eureka} =
case Network of
@ -205,7 +249,10 @@ do_start_stop(#s{acc = PubKey, keys = Keys, network = Network, max_cores = MaxCo
{Fatness, Type} =
case os:type() of
{unix, linux} ->
{"mean", "generic"};
case Network of
<<"mainnet">> -> {"mean", "avx2"};
<<"testnet">> -> {"mean", "generic"}
end;
{unix, darwin} ->
% Check memory. >7gb gets mean, <7gb gets lean
{"mean", "generic"};
@ -213,7 +260,11 @@ do_start_stop(#s{acc = PubKey, keys = Keys, network = Network, max_cores = MaxCo
% Check memory. >7gb gets mean, <7gb gets lean
% Check avx2.
% Both should be provided by the F# start program
{"mean", "generic"}
case Network of
% <<"mainnet">> -> {"mean", "avx2.exe"};
<<"mainnet">> -> {"mean", "generic.exe"};
<<"testnet">> -> {"mean", "generic.exe"}
end
end,
Miner = filename:join(platform_dir(), unicode:characters_to_binary([Fatness, Bits, "-", Type])),
Count = optimize_count(MaxCores, MaxMem),
@ -227,32 +278,35 @@ do_start_stop(#s{acc = PubKey, keys = Keys, network = Network, max_cores = MaxCo
ok = gmc_gui:message({notice, "Starting..."}),
ok = gmc_gui:message({notice, ["Miner: ", Miner]}),
{ok, Apps} = gmhc_app:start(Profile),
Started = io_lib:format("Apps started: ~p", [Apps]),
Started = io_lib:format("Apps started:~n ~p", [Apps]),
ok = log(info, Started),
ok = gmc_gui:message({notice, Started}),
Events =
[pool_notification,
connected,
puzzle,
result,
error,
disconnected],
lists:foreach(fun gmhc_events:ensure_subscribed/1, Events).
State.
do_stop(State) ->
ok = gmc_gui:message({notice, "Stopping."}),
ok =
case application:stop(gmhive_client) of
ok -> ok;
Error -> log(warning, "application:stop(gmhive_client) returned: ~p", [Error])
end,
State.
optimize_count(MaxC, MaxM) ->
MapSize = 3550722201,
GraphSize = 3550722201,
MaxCores = max(1, MaxC),
MaxMem = max(MapSize, MaxM),
MaxMem = max(GraphSize, MaxM),
{Procs, Memory} = proc_mem(),
MeanMaps = min(MaxMem, Memory) div MapSize,
Recommended = min(MaxCores, max(min(Procs, MeanMaps) - 1, 1)),
MeanGraphs = min(MaxMem, Memory) div GraphSize,
Recommended = min(MaxCores, min(Procs, MeanGraphs)),
Notice = fun(F, A) -> gmc_gui:message({notice, io_lib:format(F, A)}) end,
ok = Notice("Physical Processors: ~p", [Procs]),
ok = Notice("Physical Memory: ~p", [Memory]),
ok = Notice("Max Processor Commit: ~p", [MaxCores]),
ok = Notice("Max Memory Commit: ~p", [MaxMem]),
ok = Notice("29-bit Mean Map Space: ~p", [MeanMaps]),
ok = Notice("29-bit Mean Graph Space: ~p", [MeanGraphs]),
ok = Notice("Workers: ~p", [Recommended]),
Recommended.
@ -264,8 +318,15 @@ platform_dir() ->
{unix, linux} ->
"linux_x86_64";
{unix, darwin} ->
% TODO: Check M2 vs x86
"mac_m2";
case string:trim(os:cmd("uname -m")) of
"arm64" ->
"mac_arm64";
"x86_64" ->
"mac_x86_64";
Other ->
ok = log(info, "uname -m: ~p", [Other]),
"mac_x86_64"
end;
{win32, nt} ->
"win_x86_64"
end,
@ -313,23 +374,26 @@ win_mem() ->
do_gajudesk() ->
ok = tell(info, "Running gajudesk"),
PID = spawn(fun run_gajudesk/0),
tell(info, "GajuDesk launched at PID: ~p", [PID]).
ok = log(info, "Running gajudesk"),
GajuDesk = "zx run gajudesk",
Command =
case os:type() of
{unix, darwin} -> detach_darwin(GajuDesk);
{unix, _} -> detach_unix(GajuDesk);
{win32, nt} -> detach_windows(GajuDesk)
end,
Out = os:cmd(Command),
log(info, "os:cmd(~s) -> ~s", [Command, Out]).
run_gajudesk() ->
R = "otpr",
N = "gajudesk",
{ok, V} = zx:latest({R, N}),
{ok, PackageString} = zx_lib:package_string({R, N, V}),
try
case zx:run(PackageString, []) of
ok -> ok;
Error -> tell(error, "gajudesk died with: ~p", [Error])
end
catch
E:R -> tell(error, "gajudesk died with: ~p", [{E, R}])
end.
detach_darwin(Command) ->
"nohup " ++ Command ++ " >/dev/null 2>&1 & disown".
detach_unix(Command) ->
"setsid sh -c 'exec nohup " ++ Command ++ " >/dev/null 2>&1' &".
detach_windows(Command) ->
PSCmd = "Start-Process -WindowStyle Hidden -FilePath cmd.exe -ArgumentList '/c " ++ Command ++ "'",
"powershell -NoProfile -Command \"" ++ PSCmd ++ "\"".
run_gmc_conf(State = #s{gmc_conf = none, network = Net, acc = Acc, keys = Keys,

View File

@ -3,7 +3,7 @@
%%% @end
-module(gmc_conf).
-vsn("0.2.5").
-vsn("0.4.2").
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <craigeverett@qpq.swiss>").
-license("GPL-3.0-or-later").
@ -18,15 +18,15 @@
-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()}).
{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 = 2 :: pos_integer(),
memory = 3550722201 :: pos_integer()}).
@ -60,27 +60,30 @@ init({Prefs, {Net, Acc, Keys, {AProcs, AMem, MProcs, MMem}}}) ->
WX = wx:new(),
Frame = wxFrame:new(WX, ?wxID_ANY, Label),
Panel = wxWindow:new(Frame, ?wxID_ANY),
TopSz = wxBoxSizer:new(?wxVERTICAL),
_ = wxBoxSizer:add(TopSz, Panel, zxw:flags(wide)),
MainSz = wxBoxSizer:new(?wxVERTICAL),
AccSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("GajuMining Account ID")}]),
AccTx = wxTextCtrl:new(Frame, ?wxID_ANY, [{value, acc(Acc)}]),
AccSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, J("GajuMining Account ID")}]),
AccTx = wxTextCtrl:new(Panel, ?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)}]),
KeysSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, J("Additional Account IDs (optional)")}]),
KeysTx = wxTextCtrl:new(Panel, ?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)")}]),
StatSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{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")]),
{Grid, [CoresTx, MemoryTx]} = display_box(Panel, Labels),
Network = wxRadioBox:new(Panel, ?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),
Affirm = wxButton:new(Panel, ?wxID_OK),
Cancel = wxButton:new(Panel, ?wxID_CANCEL),
_ = wxBoxSizer:add(StatSz, Grid, wide(5)),
_ = wxBoxSizer:add(ButtSz, Affirm, wide(5)),
_ = wxBoxSizer:add(ButtSz, Cancel, wide(5)),
@ -89,7 +92,8 @@ init({Prefs, {Net, Acc, Keys, {AProcs, AMem, MProcs, MMem}}}) ->
_ = wxBoxSizer:add(MainSz, StatSz, base(5)),
_ = wxBoxSizer:add(MainSz, Network, base(5)),
_ = wxBoxSizer:add(MainSz, ButtSz, base(5)),
ok = wxFrame:setSizer(Frame, MainSz),
ok = wxWindow:setSizer(Panel, MainSz),
ok = wxFrame:setSizer(Frame, TopSz),
ok = wxBoxSizer:layout(MainSz),
ok = wxFrame:connect(Frame, command_button_clicked),
ok = wxFrame:connect(Frame, close_window),
@ -97,9 +101,9 @@ init({Prefs, {Net, Acc, Keys, {AProcs, AMem, MProcs, MMem}}}) ->
ok = wxFrame:center(Frame),
true = wxFrame:show(Frame),
State =
#s{wx = WX, frame = Frame,
lang = Lang, j = J,
net = Network, acc = AccTx, keys = KeysTx,
#s{wx = WX, frame = Frame,
lang = Lang, j = J,
net = Network, acc = AccTx, keys = KeysTx,
cores = CoresTx, memory = MemoryTx},
{Frame, State}.
@ -205,24 +209,51 @@ done(State = #s{net = Network, acc = AccTx, keys = KeysTx, cores = CoresTx, memo
1 -> <<"testnet">>;
_ -> <<"mainnet">>
end,
AccID = wxTextCtrl:getValue(AccTx),
AccID = string:trim(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)}),
ok = gmc_con:conf({Net, list_to_binary(AccID), binify_keys(AccID, MOAR_IDs), cores(CoreS), bytes(GigsS)}),
buh_bye(State).
% NOTE: 32 is space, 12288 is full-width space.
binify_keys(AccID, MOAR) ->
Unwashed = lists:usort([K || K <- string:lexemes(MOAR, [$\r, $\n, 32, 12288, $\t, $,, $;])]),
Scrubbed = lists:delete(AccID, Unwashed),
Keys =
case lists:partition(fun is_key/1, Scrubbed) of
{Cleaned, []} ->
Cleaned;
{Cleaned, Trash} ->
Message = io_lib:format("The following keys are invalid:~n~p", [Trash]),
ok = gmc_gui:message({notice, Message}),
Cleaned
end,
lists:map(fun list_to_binary/1, Keys).
is_key(Mystery) ->
try
MysteryBin = list_to_binary(Mystery),
{account_pubkey, _} = gmser_api_encoder:decode(MysteryBin),
true
catch
_:_ -> false
end.
cores("") ->
none;
2;
cores(CoreS) ->
try
list_to_integer(CoreS)
catch
_:_ -> none
_:_ -> 2
end.
bytes("") ->
none;
3550722201;
bytes(GigsS) ->
try
list_to_integer(GigsS) * gig()
@ -231,16 +262,11 @@ bytes(GigsS) ->
try
trunc(list_to_float(GigsS) * gig())
catch
_:_ -> none
_:_ -> 3550722201
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.

View File

@ -3,14 +3,14 @@
%%% @end
-module(gmc_gui).
-vsn("0.2.5").
-vsn("0.4.2").
-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([ask_conf/0, set_account/1, difficulty/1, speed/1, candidate/1, message/1]).
-export([ask_conf/0, set_account/1, difficulty/1, speed/1, candidate/1, message/1, start_stop/0]).
-export([start_link/1]).
-export([init/1, terminate/2, code_change/3,
handle_call/3, handle_cast/2, handle_info/2, handle_event/2]).
@ -23,21 +23,23 @@
wx = none :: none | wx:wx_object()}).
-record(s,
{wx = none :: none | wx:wx_object(),
frame = none :: none | wx:wx_object(),
lang = en :: en | jp,
j = none :: none | fun(),
id = none :: none | wx:wx_object(),
diff = none :: none | wx:wx_object(),
perf = none :: none | wx:wx_object(),
hist = {ts(), 0} :: {LastTS :: integer(), Average :: integer()},
candy = none :: none | wx:wx_object(),
height = none :: none | wx:wx_object(),
block = none :: none | wx:wx_object(),
% solved = 0 :: non_neg_integer(), % TODO: Add a widget to show this. Maybe
mess = none :: none | wx:wx_object(),
buff = new_buff() :: buff(),
buttons = [] :: [#w{}]}).
{wx = none :: none | wx:wx_object(),
frame = none :: none | wx:wx_object(),
lang = en :: en | jp,
j = none :: none | fun(),
id = none :: none | wx:wx_object(),
diff = none :: none | wx:wx_object(),
perf = none :: none | wx:wx_object(),
hist = {ts(), 0} :: {LastTS :: integer(), Average :: integer()},
candy = none :: none | wx:wx_object(),
height = none :: none | wx:wx_object(),
block = none :: none | wx:wx_object(),
% solved = 0 :: non_neg_integer(), % TODO: Add a widget to show this. Maybe
mess = none :: none | wx:wx_object(),
buff = new_buff() :: buff(),
buttons = [] :: [#w{}],
toggle = start :: stop | start,
toggle_t = none :: none | reference()}).
ts() ->
erlang:system_time(microsecond).
@ -79,6 +81,10 @@ message(Terms) ->
wx_object:cast(?MODULE, {message, Terms}).
start_stop() ->
wx_object:cast(?MODULE, start_stop).
%%% Startup Functions
@ -95,18 +101,21 @@ init(Prefs) ->
VSN = proplists:get_value(vsn, ?MODULE:module_info(attributes)),
WX = wx:new(),
Frame = wxFrame:new(WX, ?wxID_ANY, AppName ++ " v" ++ VSN),
Panel = wxWindow:new(Frame, ?wxID_ANY),
TopSz = wxBoxSizer:new(?wxVERTICAL),
_ = wxBoxSizer:add(TopSz, Panel, zxw:flags(wide)),
MainSz = wxBoxSizer:new(?wxVERTICAL),
LeftRight = wxBoxSizer:new(?wxHORIZONTAL),
Left = wxBoxSizer:new(?wxVERTICAL),
Right = wxBoxSizer:new(?wxVERTICAL),
Labels = [J("ID"), J("Target"), J("Maps/s"), J("Candidate"), J("Height"), J("BlockHash")],
{Grid, [ID_C, DiffC, PerfC, CandyC, HeightC, BlockC]} = display_box(Frame, Labels),
Labels = [J("ID"), J("Target"), J("Graphs/s"), J("Candidate"), J("Height"), J("BlockHash")],
{Grid, [ID_C, DiffC, PerfC, CandyC, HeightC, BlockC]} = display_box(Panel, Labels),
Style = ?wxTE_MULTILINE bor ?wxTE_READONLY,
MessSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Messages")}]),
MessC = wxTextCtrl:new(Frame, ?wxID_ANY, [{style, Style}]),
_ = wxStaticBoxSizer:add(MessSz, MessC, zxw:flags(wide)),
MessSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, J("Messages")}]),
MessC = wxTextCtrl:new(Panel, ?wxID_ANY, [{style, Style}]),
_ = wxStaticBoxSizer:add(MessSz, MessC, zxw:flags({wide, 5})),
ButtonTemplates =
[{start_stop, J("Start")},
@ -117,26 +126,27 @@ init(Prefs) ->
MakeButton =
fun({Name, Label}) ->
B = wxButton:new(Frame, ?wxID_ANY, [{label, Label}]),
B = wxButton:new(Panel, ?wxID_ANY, [{label, Label}]),
#w{name = Name, id = wxButton:getId(B), wx = B}
end,
Buttons = lists:map(MakeButton, ButtonTemplates),
_ = wxBoxSizer:add(Left, Grid, zxw:flags(base)),
_ = wxBoxSizer:add(Left, MessSz, zxw:flags(wide)),
_ = wxBoxSizer:add(Left, Grid, zxw:flags({base, 5})),
_ = wxBoxSizer:add(Left, MessSz, zxw:flags({wide, 5})),
Add = fun(#w{wx = Button}) -> wxBoxSizer:add(Right, Button, zxw:flags(wide)) end,
ok = lists:foreach(Add, Buttons),
_ = wxBoxSizer:add(LeftRight, Left, zxw:flags(wide)),
_ = wxBoxSizer:add(LeftRight, Right, zxw:flags(base)),
_ = wxBoxSizer:add(MainSz, LeftRight, zxw:flags(wide)),
_ = wxBoxSizer:add(MainSz, LeftRight, zxw:flags({wide, 5})),
ok = wxFrame:setSizer(Frame, MainSz),
ok = wxSizer:layout(MainSz),
ok = wxWindow:setSizer(Panel, MainSz),
ok = wxFrame:setSizer(Frame, TopSz),
ok = wxBoxSizer:layout(MainSz),
ok = wxFrame:connect(Frame, command_button_clicked),
ok = wxFrame:connect(Frame, close_window),
ok = wxFrame:setSize(Frame, {650, 300}),
ok = wxFrame:setSize(Frame, {780, 350}),
ok = wxFrame:center(Frame),
true = wxFrame:show(Frame),
State =
@ -187,11 +197,17 @@ handle_cast({candidate, Block}, State) ->
handle_cast({message, Terms}, State) ->
NewState = do_message(Terms, State),
{noreply, NewState};
handle_cast(start_stop, State) ->
NewState = start_stop(State),
{noreply, NewState};
handle_cast(Unexpected, State) ->
ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]),
{noreply, State}.
handle_info(toggle, State) ->
NewState = toggle(State),
{noreply, NewState};
handle_info(Unexpected, State) ->
ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
{noreply, State}.
@ -277,17 +293,43 @@ do_message({pool_notification, #{info := #{msg := MSG}}}, State = #s{height = He
ok = wxStaticText:setLabel(CandT, Candidate),
State;
#{solution_accepted := #{seq := Seq}} ->
Entry = io_lib:format("~nSolution Accepted! You solved one! Sequence: ~w", [Seq]),
Entry = io_lib:format("~nThe hive has produced a solution! Sequence: ~w", [Seq]),
do_message2(Entry, State);
Other ->
Entry = io_lib:format("~nUnexpected 'pool_notification': ~tp", [Other]),
do_message2(Entry, State)
end;
do_message({connected, _}, State) ->
Entry = "\nConnected!",
Entry = "\nConnected",
do_message2(Entry, State);
do_message({disconnected, #{info := #{error := #{code := Code, message := Message}}}}, State = #s{}) ->
Format = "~nAn issue has been reported by the Hive Leader.~nError code: ~p '~s'",
Entry = io_lib:format(Format, [Code, Message]),
do_message2(Entry, State);
do_message({disconnected, _}, State) ->
Entry = "\nDisconnected!",
Entry = "\nDisconnected",
do_message2(Entry, State);
do_message({error, #{info := #{info := #{error := get_failed, data := {error, #{code := 404}}, url := URL}}}}, State) ->
ID =
case uri_string:parse(URL) of
#{path := Path} ->
lists:last(string:split(Path, "/", trailing));
{error, Reason, Info} ->
ok = log(warning, "uri_string:parse/1 failed with: ~p: ~p", [Reason, Info]),
"UNKNOWN"
end,
Format =
"\nLookup for mining ID ~s failed."
"\nCheck that you have joined the hive and have your account ID configured correctly.",
Entry = io_lib:format(Format, [ID]),
do_message2(Entry, State);
do_message({error, #{info := #{info := #{error := get_failed, data := {error, #{code := 443}}}}}}, State) ->
Entry =
"\nThis system is not providing the needed TLS CA Certificate."
"\nlease contact the support community and let them know you have a 443 error with GajuMine.",
do_message2(Entry, State);
do_message({error, #{info := #{info := #{error := connect_failure, data := {error, failed}}, module := gmhc_eureka}}}, State) ->
Entry = "\nConnection terminated. Retrying.",
do_message2(Entry, State);
do_message(Terms, State) ->
tell(info, "~p", [Terms]),
@ -339,11 +381,33 @@ add_message2(Entry, {OMax, 0, IMax, 0, []}) ->
{append, {OMax, 1, IMax, 1, [[Entry]]}}.
start_stop(State = #s{buttons = Buttons}) ->
start_stop(State = #s{buttons = Buttons, toggle = Last, toggle_t = T}) ->
ok = cancel_timer(T),
#w{wx = SSB} = lists:keyfind(start_stop, #w.name, Buttons),
_ = wxButton:disable(SSB),
ok = gmc_con:start_stop(),
State.
Timer = erlang:send_after(2000, self(), toggle),
Next =
case Last of
stop -> start;
start -> stop
end,
State#s{toggle = Next, toggle_t = Timer}.
cancel_timer(none) -> ok;
cancel_timer(Time) -> erlang:cancel_timer(Time).
toggle(State = #s{buttons = Buttons, toggle = Next, j = J}) ->
#w{wx = SSB} = lists:keyfind(start_stop, #w.name, Buttons),
Label =
case Next of
start -> J("Start");
stop -> J("Stop")
end,
ok = wxButton:setLabel(SSB, Label),
_ = wxButton:enable(SSB),
State#s{toggle_t = none}.
gajudesk(State) ->

View File

@ -12,7 +12,7 @@
%%% @end
-module(gmc_sup).
-vsn("0.2.5").
-vsn("0.4.2").
-behaviour(supervisor).
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <craigeverett@qpq.swiss>").

View File

@ -4,19 +4,19 @@
{prefix,"gmc"}.
{author,"Craig Everett"}.
{desc,"Mining client for the Gajumaru Root"}.
{package_id,{"qpq","gajumine",{0,2,5}}}.
{deps,[{"uwiger","gmhive_client",{0,4,4}},
{"uwiger","gmcuckoo",{1,2,3}},
{"qpq","cuckoo_cpu",{0,3,0}},
{"uwiger","gmhive_worker",{0,3,0}},
{package_id,{"qpq","gajumine",{0,4,2}}}.
{deps,[{"uwiger","gmhive_protocol",{0,3,1}},
{"uwiger","setup",{3,0,0}},
{"uwiger","gmhive_client",{0,10,1}},
{"otpr","zxwidgets",{1,1,0}},
{"otpr","hakuzaru",{0,7,0}},
{"uwiger","gmcuckoo",{1,2,4}},
{"uwiger","gmhive_worker",{0,5,1}},
{"qpq","cuckoo_cpu",{0,3,2}},
{"uwiger","gmconfig",{0,1,2}},
{"uwiger","gproc",{1,0,1}},
{"uwiger","gmhive_protocol",{0,1,1}},
{"uwiger","enoise",{1,3,0}},
{"uwiger","setup",{2,2,4}},
{"otpr","hakuzaru",{0,6,1}},
{"otpr","gajudesk",{0,5,3}},
{"otpr","zxwidgets",{1,0,1}},
{"otpr","ec_utils",{1,0,0}},
{"otpr","eblake2",{1,0,1}},
{"otpr","base58",{0,1,1}},