47
47
wcanvas, % int() Canvas width
48
48
hcanvas, % int() Canvas height
49
l1, l2, % gsobj() Lines on canvas
49
l1, l2, % gsobj() Canvas lines
50
50
leds, % [gsobj()] Load meter
51
51
nodelabel, % {gsobj(),gsobj()}
52
appobjs=[], % [gsobj()] App. buttons etc.
52
appobjs=[], % [gsobj()] Buttons etc.
53
53
nodemenu}). % gsobj() Node menu
56
-record(mnode, {name, % atom() Monitored node name
56
-record(mnode, {name, % atom() Node name
57
57
status, % alive | dead
59
59
apps, % [{Pid,App,Descr}]
374
386
case State#state.window_mode of
377
{ok, GUI} = get_win2(WinObj, State#state.wins),
390
get_win2(WinObj, State#state.wins),
379
%% Clear the window and correct the node name
392
%% Clear window and correct the node name
381
394
GUI1 = draw_nodename(GUI, Node),
383
%% Update the window with the correct node name
396
%% Update window with the correct node name
384
397
%% and the applications running at the node
385
398
MNode = get_mnode(Node, State#state.mnodes),
386
399
GUI2 = case MNode#mnode.status of
400
414
GUI = draw_win(State#state.gs, Node),
402
%% Update the Nodes menu with all known nodes -
403
%% use the MNodes to get them in the right order
416
%% Update Nodes menu with all known nodes -
417
%% use MNodes to get them in the right order
404
418
lists:foreach(fun(MNode) ->
405
Name = MNode#mnode.name,
406
display_addnode(GUI, Name)
408
424
State#state.mnodes),
410
%% Mark the selected options in the Options menu
426
%% Mark selected options in the Options menu
411
427
display_setopt(GUI, many),
412
428
display_setopt(GUI, State#state.load_mode1),
413
429
display_setopt(GUI, State#state.load_mode2),
432
449
%% Help menu = Help button
434
HelpFile = filename:join(code:priv_dir(appmon),
435
"../doc/index.html"),
436
tool_utils:open_help(State#state.gs, HelpFile),
451
HelpFile = filename:join([code:lib_dir(appmon),
452
"doc", "html", "part_frame.html"]),
453
case State#state.wins of
455
tool_utils:open_help(Win#win.window, HelpFile);
457
tool_utils:open_help(State#state.gs, HelpFile)
437
459
{noreply, State};
442
handle_info({gs, WinObj, configure, _Data, [WWindow, HWindow|_]}, State) ->
464
handle_info({gs, WinObj, configure, _, [WWindow, HWindow|_]}, State) ->
443
465
{ok, GUI} = get_win2(WinObj, State#state.wins),
444
466
GUI1 = draw_resize(GUI, WWindow, HWindow),
445
467
display_scrollbar(GUI1),
492
514
%% Purpose: Convert process state when code is changed
493
515
%% Returns: {ok, NewState}
494
516
%%----------------------------------------------------------------------
495
code_change(OldVsn, State, Extra) ->
517
code_change(_OldVsn, State, _Extra) ->
499
%%%----------------------------------------------------------------------
521
%%%---------------------------------------------------------------------
500
522
%%% Internal functions
501
%%%----------------------------------------------------------------------
523
%%%---------------------------------------------------------------------
503
525
%%----------------------------------------------------------------------
504
526
%% MNode manipulating functions
540
562
%% Node -> atom()
541
563
%% MNode -> #mnode{}
542
564
%% MNodes1 -> MNodes2 -> [#mnode{}]
543
%% Replaces, or adds if previously not included, the mnode with name Node
544
%% in MNodes1 with MNode.
565
%% Replaces, or adds if previously not included, the mnode with name
566
%% Node in MNodes1 with MNode.
545
567
replace_mnode(Node, MNode, [#mnode{name=Node} | MNodes]) ->
546
568
[MNode | MNodes];
547
569
replace_mnode(Node, MNode, [MNode2 | MNodes]) ->
548
570
[MNode2 | replace_mnode(Node, MNode, MNodes)];
549
replace_mnode(Node, MNode, []) ->
571
replace_mnode(_Node, MNode, []) ->
597
619
%%----------------------------------------------------------------------
598
620
%% GUI manipulating functions
599
621
%%----------------------------------------------------------------------
600
-define(PAD, 10). % Pad between objects
601
-define(PAD2, 4*?PAD). % Pad betw. node lbl and app
603
-define(hMENUBAR, 25). % Note: Hardwired in Tcl/Tk
605
-define(xNODELBL, 60). % Node label
622
-define(PAD, 10). % Pad between objects
623
-define(PAD2, 4*?PAD). % Pad betw. node lbl and app
625
-define(hMENUBAR, 25). % Note: Hardwired in Tcl/Tk
627
-define(xNODELBL, 60). % Node label
606
628
-define(yNODELBL, 35).
607
629
-define(hNODELBL, 20).
609
-define(xMETER, 5). % Meter
631
-define(xMETER, 5). % Meter
610
632
-define(yMETER, ?yNODELBL).
611
633
-define(wMETER, 20).
612
634
-define(hMETER, ?hNODELBL + ?PAD + ?PAD2 + ?hBTN).
613
635
-define(LEDCOUNT, 16).
615
-define(xBTN, ?xNODELBL). % Application buttons
637
-define(xBTN, ?xNODELBL). % Application buttons
616
638
-define(yBTN, ?yNODELBL + ?hNODELBL + ?PAD + ?PAD2).
617
-define(wBTN, 70). % min width
639
-define(wBTN, 70). % min width
618
640
-define(hBTN, 20).
620
-define(wCANVAS, 470 + ?wMETER + 3*?PAD). % Canvas
642
-define(wCANVAS, 470 + ?wMETER + 3*?PAD). % Canvas
621
643
-define(hCANVAS, ?yNODELBL + ?hNODELBL + ?PAD + ?PAD2 + ?hBTN + 2*?PAD).
623
-define(wWIN, ?wCANVAS). % Window
645
-define(wWIN, ?wCANVAS). % Window
624
646
-define(hWIN, ?hMENUBAR + ?hCANVAS).
626
648
%%--Main window---------------------------------------------------------
631
653
NodeStr = atom_to_list(Node),
632
Win = gs:create(window, GS, [{title, "APPMON: Overview on " ++ NodeStr},
654
Win = gs:create(window, GS, [{title,
655
"APPMON: Overview on " ++ NodeStr},
633
656
{width, ?wWIN}, {height, ?hWIN},
634
657
{configure, true}]),
635
658
Canvas = gs:create(canvas, Win, [{x, 0}, {y, ?hMENUBAR},
636
{width, ?wCANVAS}, {height, ?hCANVAS}]),
637
L1 = gs:create(line, Canvas, [{coords, [{0,?yNODELBL-?PAD},
638
{?wCANVAS,?yNODELBL-?PAD}]}]),
639
L2 = gs:create(line, Canvas, [{coords, [{0,?hCANVAS-?PAD},
640
{?wCANVAS,?hCANVAS-?PAD}]}]),
660
{height, ?hCANVAS}]),
661
L1 = gs:create(line, Canvas, [{coords,
663
{?wCANVAS,?yNODELBL-?PAD}]}]),
664
L2 = gs:create(line, Canvas, [{coords,
666
{?wCANVAS,?hCANVAS-?PAD}]}]),
642
668
%% Standard buttons
643
669
MenuBar = gs:create(menubar, Win, [{height, ?hMENUBAR}]),
645
FileMenuBtn = gs:create(menubutton, MenuBar, [{label, {text,"File"}}]),
671
FileMenuBtn = gs:create(menubutton, MenuBar,
672
[{label, {text,"File"}}]),
646
673
FileMenu = gs:create(menu, FileMenuBtn, []),
647
674
gs:create(menuitem, FileMenu, [{label, {text,"Show List Box..."}},
648
675
{data, listbox}]),
652
679
gs:create(menuitem, FileMenu, [{label, {text, "Exit"}},
655
ActionMenuBtn = gs:create(menubutton,MenuBar,[{label,{text,"Actions"}}]),
682
ActionMenuBtn = gs:create(menubutton, MenuBar,
683
[{label,{text,"Actions"}}]),
656
684
ActionMenu = gs:create(menu, ActionMenuBtn, []),
657
685
gs:create(menuitem, ActionMenu, [{label, {text,"Reboot"}},
658
686
{data, {action, reboot, Win}}]),
663
691
gs:create(menuitem, ActionMenu, [{label, {text,"Ping"}},
664
692
{data, {action, ping, Win}}]),
666
OptMenuBtn = gs:create(menubutton, MenuBar, [{label, {text,"Options"}}]),
694
OptMenuBtn = gs:create(menubutton, MenuBar,
695
[{label, {text,"Options"}}]),
667
696
OptMenu = gs:create(menu, OptMenuBtn, []),
668
697
G0 = now(), % Group identity unique per window!
669
698
SMI = gs:create(menuitem, OptMenu, [{label, {text,"One window"}},
677
706
TMI = gs:create(menuitem, OptMenu, [{label, {text,"Load: time"}},
678
707
{itemtype, radio}, {group, G1},
679
{data, {option, time,
680
[{load_method,time}]}}]),
710
[{load_method,time}]}}]),
681
711
QMI = gs:create(menuitem, OptMenu, [{label, {text,"Load: queue"}},
682
712
{itemtype, radio}, {group, G1},
683
{data, {option, queue,
684
[{load_method,queue}]}}]),
715
[{load_method,queue}]}}]),
686
PMI = gs:create(menuitem, OptMenu, [{label, {text,"Load: progressive"}},
687
{itemtype, radio}, {group, G2},
688
{data, {option, prog,
689
[{load_scale,prog}]}}]),
717
PMI = gs:create(menuitem, OptMenu,
718
[{label, {text,"Load: progressive"}},
719
{itemtype, radio}, {group, G2},
720
{data, {option, prog, [{load_scale,prog}]}}]),
690
721
LMI = gs:create(menuitem, OptMenu, [{label, {text,"Load: linear"}},
691
722
{itemtype, radio}, {group, G2},
692
{data, {option, linear,
693
[{load_scale,linear}]}}]),
725
[{load_scale,linear}]}}]),
695
NodeMenuBtn = gs:create(menubutton, MenuBar, [{label, {text,"Nodes"}}]),
727
NodeMenuBtn = gs:create(menubutton, MenuBar,
728
[{label, {text,"Nodes"}}]),
696
729
NodeMenu = gs:create(menu, NodeMenuBtn, []),
698
HelpMenuBtn = gs:create(menubutton, MenuBar, [{label, {text,"Help"}},
731
HelpMenuBtn = gs:create(menubutton, MenuBar,
732
[{label, {text,"Help"}}, {side, right}]),
700
733
HelpMenu = gs:create(menu, HelpMenuBtn, []),
701
gs:create(menuitem, HelpMenu, [{label, {text,"Help"}}, {data, help}]),
734
gs:create(menuitem, HelpMenu, [{label, {text,"Help"}},
704
738
HLed = trunc((?hMETER)/(?LEDCOUNT)),
705
739
Leds = draw_leds(?LEDCOUNT, Canvas, ?yMETER, HLed, []),
706
740
leds_down(Leds, ?LEDCOUNT, 0),
707
gs:create(text, Canvas, [{coords, [{?xMETER, ?yMETER+HLed*?LEDCOUNT}]},
741
gs:create(text, Canvas, [{coords,
742
[{?xMETER, ?yMETER+HLed*?LEDCOUNT}]},
709
744
{font, {screen,8}},
710
745
{text, "Load"}]),
722
757
WNodeLbl = 8*length(NodeStr)+10,
723
NLRect = gs:create(rectangle, Canvas, [{coords, [{?xNODELBL,?yNODELBL},
725
?yNODELBL+?hNODELBL}]},
758
NLRect = gs:create(rectangle, Canvas,
759
[{coords, [{?xNODELBL,?yNODELBL},
761
?yNODELBL+?hNODELBL}]},
727
763
Xc = ?xNODELBL + round(WNodeLbl/2),
728
764
Yc = ?yNODELBL + round(?hNODELBL/2),
729
NLText = gs:create(text, Canvas, [{text, NodeStr}, {fg, {250,235,215}},
730
{coords, [{Xc,Yc}]}, {anchor, c}]),
765
NLText = gs:create(text, Canvas, [{text, NodeStr},
731
769
NodeLbl = {NLRect, NLText},
733
771
gs:config(Win, {map, true}),
739
777
l1=L1, l2=L2, leds=Leds, nodelabel=NodeLbl, nodemenu=NodeMenu}.
741
779
draw_leds(N, Canvas, Y, HLed, Leds) when N>0 ->
742
Top = ?yNODELBL + (?LEDCOUNT-N)*HLed,
743
780
Led = gs:create(rectangle, Canvas,
744
[{coords, [{?xMETER,Y}, {?xMETER+?wMETER,Y+HLed}]}]),
782
[{?xMETER,Y}, {?xMETER+?wMETER,Y+HLed}]}]),
745
783
draw_leds(N-1, Canvas, Y+HLed, HLed, [Led | Leds]);
746
784
draw_leds(0, _Canvas, _Y, _HLed, Leds) ->
830
869
AppBtn = canvasbutton(GUI#win.canvas, Text, X, ?yBTN, Width, ?hBTN,
833
draw_apps(GUI, Apps, X+Width+?PAD, Lx, N+1, [AppBtn, Line | GSObjs]);
872
draw_apps(GUI, Apps, X+Width+?PAD, Lx, N+1, [AppBtn, Line|GSObjs]);
834
873
draw_apps(_GUI, [], X, _N, _Lx0, GSObjs) ->
842
881
display_addnode([GUI|GUIs], Node) ->
843
882
display_addnode(GUI, Node),
844
883
display_addnode(GUIs, Node);
845
display_addnode([], Node) ->
884
display_addnode([], _Node) ->
847
886
display_addnode(GUI, Node) ->
848
887
Txt = "Show " ++ atom_to_list(Node),
849
888
gs:create(menuitem, GUI#win.nodemenu,
850
[{label, {text,Txt}}, {data, {node, Node, GUI#win.window}}]).
889
[{label, {text,Txt}},
890
{data, {node, Node, GUI#win.window}}]).
852
892
%% Show that a node has come back up
853
893
display_nodeup(GUI, Node) ->
854
894
{Rect, Text} = GUI#win.nodelabel,
856
896
%% Check coordinates for the rectangle and compute the new width
857
[{L, T}, {R, B}] = gs:read(Rect, coords),
897
[{L, T}, {_R, B}] = gs:read(Rect, coords),
858
898
NodeStr = atom_to_list(Node),
859
899
W = 8*length(NodeStr)+10,
861
901
gs:config(Rect, [{coords, [{L, T}, {L+W, B}]}, {fill, black}]),
862
902
gs:config(Text, [{text, NodeStr}, {fg, {250,235,215}},
863
{coords, [{L+round(W/2), T+round((?hNODELBL)/2)}]}]).
904
[{L+round(W/2), T+round((?hNODELBL)/2)}]}]).
865
906
%% Show that a node has gone down
866
907
display_nodedown(GUI) ->
867
908
{Rect, Text} = GUI#win.nodelabel,
869
[{L, T}, {R, B}] = gs:read(Rect, coords),
910
[{L, T}, {_R, B}] = gs:read(Rect, coords),
870
911
gs:config(Rect, [{coords, [{L, T}, {L+114, B}]}, {fill, gray}]),
871
912
gs:config(Text, [{text, "No connection"}, {fg, black},
872
913
{coords, [{L+57, T+round((?hNODELBL)/2)}]}]).
880
921
HCanvas = GUI#win.hcanvas,
882
923
WCanvas>WWindow ->
883
gs:config(GUI#win.canvas,[{hscroll, bottom},
884
{scrollregion,{0,0,WCanvas,HCanvas}}]);
924
gs:config(GUI#win.canvas,
926
{scrollregion,{0,0,WCanvas,HCanvas}}]);
886
928
gs:config(GUI#win.canvas, [{hscroll, false}])
889
931
HCanvas>HWindow ->
890
gs:config(GUI#win.canvas,[{vscroll, left},
891
{scrollregion,{0,0,WCanvas,HCanvas}}]);
932
gs:config(GUI#win.canvas,
934
{scrollregion,{0,0,WCanvas,HCanvas}}]);
894
937
gs:config(GUI#win.canvas, [{vscroll, false}])
924
967
leds_up(GUI#win.leds, Old, New)
927
leds_down(Leds, Old, New) when Old == New ->
970
leds_down(_Leds, Old, New) when Old == New ->
929
972
leds_down(Leds, Old, New) when Old > New ->
930
973
reset_led(Leds, Old),
931
974
leds_down(Leds, Old-1, New).
932
leds_up(Leds, Old, New) when Old == New ->
975
leds_up(_Leds, Old, New) when Old == New ->
934
977
leds_up(Leds, Old, New) when Old < New ->
935
978
set_led(Leds, Old),
938
981
led_on_col(N) when N > 13 -> ?highloadfg;
939
982
led_on_col(N) when N > 9 -> ?midloadfg;
940
led_on_col(N) -> ?lowloadfg.
983
led_on_col(_) -> ?lowloadfg.
942
985
led_off_col(N) when N > 13 -> ?highloadbg;
943
986
led_off_col(N) when N > 9 -> ?midloadbg;
944
led_off_col(N) -> ?lowloadbg.
987
led_off_col(_) -> ?lowloadbg.
946
reset_led(Leds, 0) -> ok;
989
reset_led(_Leds, 0) -> ok;
947
990
reset_led(Leds, N) ->
948
991
gs:config(lists:nth(N, Leds), [{fill, led_off_col(N)}]).
950
set_led(Leds, 0) -> ok;
993
set_led(_Leds, 0) -> ok;
951
994
set_led(Leds, N) ->
952
995
gs:config(lists:nth(N, Leds), [{fill, led_on_col(N)}]).