diff options
Diffstat (limited to 'lib/appmon/src/appmon.erl')
-rw-r--r-- | lib/appmon/src/appmon.erl | 1081 |
1 files changed, 0 insertions, 1081 deletions
diff --git a/lib/appmon/src/appmon.erl b/lib/appmon/src/appmon.erl deleted file mode 100644 index ba98a24400..0000000000 --- a/lib/appmon/src/appmon.erl +++ /dev/null @@ -1,1081 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% --module(appmon). --behaviour(gen_server). --compile([{nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,create,3}}, - {nowarn_deprecated_function,{gs,destroy,1}}, - {nowarn_deprecated_function,{gs,read,2}}, - {nowarn_deprecated_function,{gs,start,1}}]). - -%%%--------------------------------------------------------------------- -%%% Appmon main module. -%%% Creates the main window and receives load and application -%%% information from all connected nodes. -%%%--------------------------------------------------------------------- - -%% External exports --export([start/0, stop/0]). - -%% gen_server callbacks --export([init/1, handle_cast/2, handle_info/2, terminate/2]). --export([handle_call/3, code_change/3]). % not used - -%% Canvas button data --record(canvasbutton, {text, ul, ll, rect, x, y, w, h}). - -%% Options - all the fields are GS radio buttons --record(options, {single, many, time, queue, prog, linear}). - -%% Main window data --record(win, {name, % atom() Monitored node - window, % gsobj() - wwindow, % int() Window width - hwindow, % int() Window height - options, % #options{} - canvas, % gsobj() - wcanvas, % int() Canvas width - hcanvas, % int() Canvas height - l1, l2, % gsobj() Canvas lines - leds, % [gsobj()] Load meter - nodelabel, % {gsobj(),gsobj()} - appobjs=[], % [gsobj()] Buttons etc. - nodemenu}). % gsobj() Node menu - -%% Node data --record(mnode, {name, % atom() Node name - status, % alive | dead - pid, % pid() - apps, % [{Pid,App,Descr}] - load}). % {Old, New} - -%% Internal state data --record(state, {gs, % pid() - wins=[], % [#win()] GUIs - window_mode, % single | many - load_mode1, % time | queue - load_mode2, % prog | linear - lbpid, % pid() - mnodes=[]}). % [#mnode{}] - -%%%--------------------------------------------------------------------- -%%% External exports -%%%--------------------------------------------------------------------- - -start() -> - gen_server:start({local, appmon}, ?MODULE, [], []). - -stop() -> - gen_server:cast(appmon, stop). - - -%%%--------------------------------------------------------------------- -%%% gen_server callbacks -%%%--------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% Func: init/1 -%% Returns: {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%%---------------------------------------------------------------------- -init([]) -> - process_flag(trap_exit, true), - - %% Subscribe to {nodeup,Node} and {nodedown,Node} messages - net_kernel:monitor_nodes(true), - - LbPid = appmon_lb:start(self ()), - - %% Check which remote nodes have appmon code available (OTP-4887) - NodesOk = lists:filter(fun(Node) -> check_node(Node) end, nodes()), - Nodes = [node()|NodesOk], - - %% Start monitoring the existing nodes - MNodes = mk_mnodes(Nodes, LbPid), - - %% Draw the main window - GS = gs:start([{kernel,true}]), - GUI = draw_win(GS, node()), - - %% Update the Nodes menu with all known nodes - lists:foreach(fun(Node) -> - display_addnode(GUI, Node) - end, - Nodes), - - %% Mark the default options as selected in the Options menu - display_setopt(GUI, single), - display_setopt(GUI, time), - display_setopt(GUI, prog), - - {ok, #state{gs=GS, wins=[GUI], - window_mode=single, load_mode1=time, load_mode2=prog, - lbpid=LbPid, mnodes=MNodes}}. - -check_node(Node) -> - case rpc:call(Node, code, which, [appmon]) of - File when is_list(File) -> - true; - _ -> % non_existing (| cover_compiled) - false - end. - -%%---------------------------------------------------------------------- -%% Func: handle_call/3 -%% Returns: {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | (terminate/2 is called) -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- -handle_call(norequest, _From, State) -> - {reply, null, State}. - -%%---------------------------------------------------------------------- -%% Func: handle_cast/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- -handle_cast(stop, State) -> - {stop, normal, State}. - -%%---------------------------------------------------------------------- -%% Func: handle_info/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- -%% Load information from a node -handle_info({delivery, _Serv, load, Node, Load}, State) -> - - %% Update node information - MNode = get_mnode(Node, State#state.mnodes), - MNode1 = MNode#mnode{load=Load}, - MNodes = replace_mnode(Node, MNode1, State#state.mnodes), - - %% If Node is currently displayed, update graphics - case get_win(Node, State#state.wins) of - {ok, GUI} -> - display_load(GUI, Load); - false -> - ignore - end, - - {noreply, State#state{mnodes=MNodes}}; - -%% Application information from a node -handle_info({delivery, _Serv, app_ctrl, Node, Apps}, State) -> - - %% Update node information - MNode = get_mnode(Node, State#state.mnodes), - MNode1 = MNode#mnode{apps=Apps}, - MNodes = replace_mnode(Node, MNode1, State#state.mnodes), - - %% If Node is currently displayed, update graphics - Wins = case get_win(Node, State#state.wins) of - {ok, GUI} -> - draw_clear(GUI), - GUI1 = draw_apps(GUI, Apps), - replace_win(Node, GUI1, State#state.wins); - false -> - State#state.wins - end, - - appmon_lb:add_apps (State#state.lbpid, Apps, Node), - {noreply, State#state{wins=Wins, mnodes=MNodes}}; - -handle_info({nodeup, Node}, State) -> - - %% First, make sure appmon code is available at remode node, - %% or the node should be ignored (OTP-3591) - case check_node(Node) of - true -> - - %% If this is a previously unknown node, update window's - %% 'Nodes' menu - case get_mnode(Node, State#state.mnodes) of - false -> - display_addnode(State#state.wins, Node); - _OldMnode -> - ignore - end, - - %% Update node information (=> state is automatically - %% changed to 'alive') - MNode = mk_mnode(Node, State#state.lbpid), - MNodes = replace_mnode(Node, MNode, State#state.mnodes), - - %% If Node is currently displayed, update graphics - case get_win(Node, State#state.wins) of - {ok, GUI} -> - display_nodeup(GUI, Node); - false -> - ignore - end, - - appmon_lb:update_status(State#state.lbpid, Node, alive), - {noreply, State#state{mnodes=MNodes}}; - - false -> - {noreply, State} - end; - -handle_info({nodedown, Node}, State) -> - - %% If this is a previously unknown node, ignore the message. - %% (The situation occurs when failing to connect to another node). - %% Otherwise, update the node information. - case get_mnode(Node, State#state.mnodes) of - false -> - {noreply, State}; - MNode -> - MNode1 = MNode#mnode{status=dead}, - MNodes = replace_mnode(Node, MNode1, State#state.mnodes), - - %% If Node is currently displayed, update graphics - Wins = case get_win(Node, State#state.wins) of - {ok, GUI} -> - display_nodedown(GUI), - GUI1 = draw_clear(GUI), - replace_win(Node, GUI1, State#state.wins); - false -> - State#state.wins - end, - - appmon_lb:remove_node(State#state.lbpid, Node), - {noreply, State#state{wins=Wins, mnodes=MNodes}} - end; - -%% Application 'button' events -handle_info({gs, _Obj, buttonpress, Data, _Arg}, State) -> - {canvasbutton, CBtn, _App} = Data, - press(CBtn), - {noreply, State}; -handle_info({gs, _Obj, buttonrelease, Data, [_,X,Y|_]}, State) -> - {canvasbutton, CBtn, {application, App, Node}} = Data, - release(CBtn), - - %% Check that mouse button was released over the button! - L = CBtn#canvasbutton.x, R = L + CBtn#canvasbutton.w, - T = CBtn#canvasbutton.y, B = T + CBtn#canvasbutton.h, - if - X>L, X<R, Y>T, Y<B -> - MNode = get_mnode(Node, State#state.mnodes), - {value, {Pid, _App, _Descr}} = - lists:keysearch(App, 2, MNode#mnode.apps), - appmon_a:start(Node, App, Pid); - true -> - ignore - end, - {noreply, State}; - -handle_info({gs, _Button, click, Data, _Arg}, State) -> - ThisNode = node(), - case Data of - - %% File menu item - listbox -> - appmon_lb:open_win(State#state.lbpid, - parse_nodes(State#state.mnodes)), - {noreply, State}; - {close, WinObj} -> - {ok, GUI} = get_win2(WinObj, State#state.wins), - gs:destroy(WinObj), - - %% Terminate if this was the only open window - case remove_win(GUI#win.name, State#state.wins) of - [] -> - {stop, normal, State}; - Wins -> - {noreply, State#state{wins=Wins}} - end; - exit -> - {stop, normal, State}; - - %% Actions menu item - {action, Action, WinObj} -> - {ok, GUI} = get_win2(WinObj, State#state.wins), - Node = GUI#win.name, - - if - Node==ThisNode -> - case Action of - ping -> - %% Ignore - makes no sense to ping yourself - ignore; - _ -> % reboot | restart | stop - apply(init, Action, []) - end; - - Node/=ThisNode -> - case Action of - ping -> - net_adm:ping(Node); - _ -> % reboot | restart | stop - rpc:cast(Node, init, Action, []) - end - end, - {noreply, State}; - - %% Options menu item - {window_mode, Mode} -> - - %% Update windows so they all show the same options - lists:foreach(fun(GUI) -> - display_setopt(GUI, Mode) - end, - State#state.wins), - {noreply, State#state{window_mode=Mode}}; - - {option, Tag, Option} -> - - %% Update windows so they all show the same options - lists:foreach(fun(GUI) -> - display_setopt(GUI, Tag) - end, - State#state.wins), - - %% Update all appmon_info processes about which kind of - %% load data is desired - lists:foreach(fun(MNode) -> - appmon_info:load(MNode#mnode.pid, - MNode#mnode.name, - true, - Option) - end, - State#state.mnodes), - - if - Tag==time; Tag==queue -> - {noreply, State#state{load_mode1=Tag}}; - Tag==prog; Tag==linear -> - {noreply, State#state{load_mode2=Tag}} - end; - - %% Nodes menu item - {node, Node, WinObj} -> - - %% Check first if this window is already displayed - case get_win(Node, State#state.wins) of - {ok, GUI} -> - - %% Node is already displayed, raise its window - gs:config(GUI#win.window, raise), - - {noreply, State}; - - %% Node is not displayed - false -> - - %% Redraw existing window or create a new window - %% depending on window mode - case State#state.window_mode of - - single -> - {ok, GUI} = - get_win2(WinObj, State#state.wins), - - %% Clear window and correct the node name - draw_clear(GUI), - GUI1 = draw_nodename(GUI, Node), - - %% Update window with the correct node name - %% and the applications running at the node - MNode = get_mnode(Node, State#state.mnodes), - GUI2 = case MNode#mnode.status of - dead -> - display_nodedown(GUI1), - GUI1; - alive -> - display_nodeup(GUI1, Node), - draw_apps(GUI1, - MNode#mnode.apps) - end, - Wins = replace_win(GUI#win.name, GUI2, - State#state.wins), - - {noreply, State#state{wins=Wins}}; - - many -> - GUI = draw_win(State#state.gs, Node), - - %% Update Nodes menu with all known nodes - - %% use MNodes to get them in the right order - lists:foreach(fun(MNode) -> - Name = - MNode#mnode.name, - display_addnode(GUI, - Name) - end, - State#state.mnodes), - - %% Mark selected options in the Options menu - display_setopt(GUI, many), - display_setopt(GUI, State#state.load_mode1), - display_setopt(GUI, State#state.load_mode2), - - %% Add the applications running at the node - MNode = get_mnode(Node, State#state.mnodes), - - GUI1 = case MNode#mnode.status of - dead -> - display_nodedown(GUI), - GUI; - alive -> - display_nodeup(GUI, Node), - draw_apps(GUI, - MNode#mnode.apps) - end, - Wins = [GUI1|State#state.wins], - - {noreply, State#state{wins=Wins}} - end - end; - - %% Help menu = Help button - help -> - HelpFile = filename:join([code:lib_dir(appmon), - "doc", "html", "part_frame.html"]), - case State#state.wins of - [Win] -> - tool_utils:open_help(Win#win.window, HelpFile); - _ -> - tool_utils:open_help(State#state.gs, HelpFile) - end, - {noreply, State}; - - _Other -> - {noreply, State} - end; -handle_info({gs, WinObj, configure, _, [WWindow, HWindow|_]}, State) -> - {ok, GUI} = get_win2(WinObj, State#state.wins), - GUI1 = draw_resize(GUI, WWindow, HWindow), - display_scrollbar(GUI1), - Wins = replace_win(GUI#win.name, GUI1, State#state.wins), - {noreply, State#state{wins=Wins}}; -handle_info({gs, WinObj, destroy, _, _}, State) -> % OTP-1179 - {ok, GUI} = get_win2(WinObj, State#state.wins), - - %% Terminate if this was the only open window - case remove_win(GUI#win.name, State#state.wins) of - [] -> - {stop, normal, State}; - Wins -> - {noreply, State#state{wins=Wins}} - end; - -handle_info(stop, State) -> - {stop, normal, State}; -handle_info({'EXIT', Pid, Reason}, State) -> - case Reason of - shutdown -> - %% Appmon is being asked to shut down, eg during reboot - {stop, Reason, State}; - _ -> - case State#state.gs of - - %% GS exited, kill appmon - {0, Pid} -> - {stop, normal, State}; - - _ -> - {noreply, State} - end - end; -handle_info(_Info, State) -> - {noreply, State}. - -%%---------------------------------------------------------------------- -%% Func: terminate/2 -%% Purpose: Shutdown the server -%% Returns: any (ignored by gen_server) -%%---------------------------------------------------------------------- -terminate(_Reason, State) -> - bcast(State#state.mnodes, {kill}), - appmon_lb:stop(State#state.lbpid), - ok. - -%%---------------------------------------------------------------------- -%% Func: code_change/3 -%% Purpose: Convert process state when code is changed -%% Returns: {ok, NewState} -%%---------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - - -%%%--------------------------------------------------------------------- -%%% Internal functions -%%%--------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% MNode manipulating functions -%%---------------------------------------------------------------------- - -%% mk_mnodes(Nodes, LbPid) -> MNodes -%% Nodes -> [atom()] -%% LbPid -> pid() -%% MNodes -> [#mnode{}] -mk_mnodes([Node|Nodes], LbPid) -> - [mk_mnode(Node, LbPid) | mk_mnodes(Nodes, LbPid)]; -mk_mnodes([], _LbPid) -> - []. - -mk_mnode(Node, LbPid) -> - - %% Create an appmon process at the node - {ok, Pid} = appmon_info:start_link(Node, self(), []), - - appmon_lb:add_node(LbPid, Node), - appmon_info:load(Pid, Node, true, [{timeout,1000}]), - appmon_info:app_ctrl(Pid, Node, true, []), - - #mnode{name=Node, status=alive, pid=Pid}. - -%% get_mnode(Node, MNodes) -> MNode | false -%% Node -> atom() -%% MNodes -> [#mnode{}] -%% MNode -> #mnode{} -get_mnode(Node, MNodes) -> - case lists:keysearch(Node, #mnode.name, MNodes) of - {value, MNode} -> - MNode; - false -> - false - end. - -%% replace_mnode(Node, MNode, MNodes1) -> Mnodes2 -%% Node -> atom() -%% MNode -> #mnode{} -%% MNodes1 -> MNodes2 -> [#mnode{}] -%% Replaces, or adds if previously not included, the mnode with name -%% Node in MNodes1 with MNode. -replace_mnode(Node, MNode, [#mnode{name=Node} | MNodes]) -> - [MNode | MNodes]; -replace_mnode(Node, MNode, [MNode2 | MNodes]) -> - [MNode2 | replace_mnode(Node, MNode, MNodes)]; -replace_mnode(_Node, MNode, []) -> - [MNode]. - - -%%---------------------------------------------------------------------- -%% GUI list manipulating functions -%%---------------------------------------------------------------------- - -%% get_win(Node, Wins) -> Win -%% Node -> atom() -%% Wins -> [#win{}] -%% Win -> #win{} -get_win(Node, Wins) -> - case lists:keysearch(Node, #win.name, Wins) of - {value, Win} -> - {ok, Win}; - false -> - false - end. - -%% get_win2(WinObj, Wins) -> Win -%% Window -> gsobj() -%% Wins -> [#win{}] -%% Win -> #win{} -get_win2(WinObj, Wins) -> - case lists:keysearch(WinObj, #win.window, Wins) of - {value, Win} -> - {ok, Win}; - false -> - false - end. - -%% replace_win(Node, Win, Wins) -> Wins2 -%% Node -> atom() -%% Win -> #win{} -%% Wins -> Wins2 -> [#win{}] -replace_win(Node, Win, Wins) -> - lists:keyreplace(Node, #win.name, Wins, Win). - -%% remove_win(Node, Wins) -> Wins2 -%% Node -> atom() -%% Wins -> Wins2 -> [#win{}] -remove_win(Node, Wins) -> - lists:keydelete(Node, #win.name, Wins). - - - - -%%---------------------------------------------------------------------- -%% GUI manipulating functions -%%---------------------------------------------------------------------- --define(PAD, 10). % Pad between objects --define(PAD2, 4*?PAD). % Pad betw. node lbl and app - --define(hMENUBAR, 25). % Note: Hardwired in Tcl/Tk - --define(xNODELBL, 60). % Node label --define(yNODELBL, 35). --define(hNODELBL, 20). - --define(xMETER, 5). % Meter --define(yMETER, ?yNODELBL). --define(wMETER, 20). --define(hMETER, ?hNODELBL + ?PAD + ?PAD2 + ?hBTN). --define(LEDCOUNT, 16). - --define(xBTN, ?xNODELBL). % Application buttons --define(yBTN, ?yNODELBL + ?hNODELBL + ?PAD + ?PAD2). --define(wBTN, 70). % min width --define(hBTN, 20). - --define(wCANVAS, 470 + ?wMETER + 3*?PAD). % Canvas --define(hCANVAS, ?yNODELBL + ?hNODELBL + ?PAD + ?PAD2 + ?hBTN + 2*?PAD). - --define(wWIN, ?wCANVAS). % Window --define(hWIN, ?hMENUBAR + ?hCANVAS). - -%%--Main window--------------------------------------------------------- - -draw_win(GS, Node) -> - - %% Main window - NodeStr = atom_to_list(Node), - Win = gs:create(window, GS, [{title, - "APPMON: Overview on " ++ NodeStr}, - {width, ?wWIN}, {height, ?hWIN}, - {configure, true}]), - Canvas = gs:create(canvas, Win, [{x, 0}, {y, ?hMENUBAR}, - {width, ?wCANVAS}, - {height, ?hCANVAS}]), - L1 = gs:create(line, Canvas, [{coords, - [{0,?yNODELBL-?PAD}, - {?wCANVAS,?yNODELBL-?PAD}]}]), - L2 = gs:create(line, Canvas, [{coords, - [{0,?hCANVAS-?PAD}, - {?wCANVAS,?hCANVAS-?PAD}]}]), - - %% Standard buttons - MenuBar = gs:create(menubar, Win, [{height, ?hMENUBAR}]), - - FileMenuBtn = gs:create(menubutton, MenuBar, - [{label, {text,"File"}}]), - FileMenu = gs:create(menu, FileMenuBtn, []), - gs:create(menuitem, FileMenu, [{label, {text,"Show List Box..."}}, - {data, listbox}]), - gs:create(menuitem, FileMenu, [{label, {text, "Close"}}, - {data, {close, Win}}]), - gs:create(menuitem, FileMenu, [{itemtype, separator}]), - gs:create(menuitem, FileMenu, [{label, {text, "Exit"}}, - {data, exit}]), - - ActionMenuBtn = gs:create(menubutton, MenuBar, - [{label,{text,"Actions"}}]), - ActionMenu = gs:create(menu, ActionMenuBtn, []), - gs:create(menuitem, ActionMenu, [{label, {text,"Reboot"}}, - {data, {action, reboot, Win}}]), - gs:create(menuitem, ActionMenu, [{label, {text,"Restart"}}, - {data, {action, restart, Win}}]), - gs:create(menuitem, ActionMenu, [{label, {text,"Stop"}}, - {data, {action, stop, Win}}]), - gs:create(menuitem, ActionMenu, [{label, {text,"Ping"}}, - {data, {action, ping, Win}}]), - - OptMenuBtn = gs:create(menubutton, MenuBar, - [{label, {text,"Options"}}]), - OptMenu = gs:create(menu, OptMenuBtn, []), - G0 = now(), % Group identity unique per window! - SMI = gs:create(menuitem, OptMenu, [{label, {text,"One window"}}, - {itemtype, radio}, {group, G0}, - {data, {window_mode, single}}]), - MMI = gs:create(menuitem, OptMenu, [{label, {text,"Many windows"}}, - {itemtype, radio}, {group, G0}, - {data, {window_mode, many}}]), - gs:create(menuitem, OptMenu, [{itemtype, separator}]), - G1 = now(), - TMI = gs:create(menuitem, OptMenu, [{label, {text,"Load: time"}}, - {itemtype, radio}, {group, G1}, - {data, - {option, time, - [{load_method,time}]}}]), - QMI = gs:create(menuitem, OptMenu, [{label, {text,"Load: queue"}}, - {itemtype, radio}, {group, G1}, - {data, - {option, queue, - [{load_method,queue}]}}]), - G2 = now(), - PMI = gs:create(menuitem, OptMenu, - [{label, {text,"Load: progressive"}}, - {itemtype, radio}, {group, G2}, - {data, {option, prog, [{load_scale,prog}]}}]), - LMI = gs:create(menuitem, OptMenu, [{label, {text,"Load: linear"}}, - {itemtype, radio}, {group, G2}, - {data, - {option, linear, - [{load_scale,linear}]}}]), - - NodeMenuBtn = gs:create(menubutton, MenuBar, - [{label, {text,"Nodes"}}]), - NodeMenu = gs:create(menu, NodeMenuBtn, []), - - HelpMenuBtn = gs:create(menubutton, MenuBar, - [{label, {text,"Help"}}, {side, right}]), - HelpMenu = gs:create(menu, HelpMenuBtn, []), - gs:create(menuitem, HelpMenu, [{label, {text,"Help"}}, - {data, help}]), - - %% Meter - HLed = trunc((?hMETER)/(?LEDCOUNT)), - Leds = draw_leds(?LEDCOUNT, Canvas, ?yMETER, HLed, []), - leds_down(Leds, ?LEDCOUNT, 0), - gs:create(text, Canvas, [{coords, - [{?xMETER, ?yMETER+HLed*?LEDCOUNT}]}, - {anchor, nw}, - {font, {screen,8}}, - {text, "Load"}]), - gs:create(text, Canvas, [{coords, [{?xMETER+?wMETER, ?yMETER}]}, - {anchor, nw}, - {font, {screen,8}}, - {text, "Hi"}]), - gs:create(text, Canvas, [{coords, [{?xMETER+?wMETER, - ?yMETER+HLed*?LEDCOUNT}]}, - {anchor, w}, - {font, {screen,8}}, - {text, "Lo"}]), - - %% Node label - WNodeLbl = 8*length(NodeStr)+10, - NLRect = gs:create(rectangle, Canvas, - [{coords, [{?xNODELBL,?yNODELBL}, - {?xNODELBL+WNodeLbl, - ?yNODELBL+?hNODELBL}]}, - {fill, black}]), - Xc = ?xNODELBL + round(WNodeLbl/2), - Yc = ?yNODELBL + round(?hNODELBL/2), - NLText = gs:create(text, Canvas, [{text, NodeStr}, - {fg, {250,235,215}}, - {coords, [{Xc,Yc}]}, - {anchor, c}]), - NodeLbl = {NLRect, NLText}, - - gs:config(Win, {map, true}), - #win{name=Node, - window=Win, wwindow=?wWIN, hwindow=?hCANVAS, - options=#options{single=SMI, many=MMI, - time=TMI, queue=QMI, prog=PMI, linear=LMI}, - canvas=Canvas, wcanvas=?wCANVAS, hcanvas=?hCANVAS, - l1=L1, l2=L2, leds=Leds, nodelabel=NodeLbl, nodemenu=NodeMenu}. - -draw_leds(N, Canvas, Y, HLed, Leds) when N>0 -> - Led = gs:create(rectangle, Canvas, - [{coords, - [{?xMETER,Y}, {?xMETER+?wMETER,Y+HLed}]}]), - draw_leds(N-1, Canvas, Y+HLed, HLed, [Led | Leds]); -draw_leds(0, _Canvas, _Y, _HLed, Leds) -> - Leds. - -%%--Draw functions------------------------------------------------------ -%% Functions that modify the GUI and its data (win{}) - -%% Display the node name in the window title -%% (The name in the node label is changed by display_nodeup|nodedown) -%% Used when a changing the node to display -draw_nodename(GUI, Node) -> - NodeStr = atom_to_list(Node), - gs:config(GUI#win.window, - {title, "APPMON: Overview on " ++ NodeStr}), - GUI#win{name=Node}. - -%% Resize the canvas (when the window has been resized) -draw_resize(GUI, W, H) -> - Hc = H - ?hMENUBAR, - gs:config(GUI#win.canvas, [{width, W}, {height, Hc}]), - Yline1 = ?yNODELBL-?PAD, - Yline2 = ?hCANVAS-?PAD, - gs:config(GUI#win.l1, [{coords, [{0,Yline1},{W,Yline1}]}]), - gs:config(GUI#win.l2, [{coords, [{0,Yline2},{W,Yline2}]}]), - GUI#win{wwindow=W, hwindow=Hc}. - -%% Clear the GUI from applications and connecting lines -draw_clear(GUI) -> - draw_clear2(GUI#win.appobjs), - gs:config(GUI#win.canvas, [{hscroll, false}]), - GUI#win{appobjs=[]}. -draw_clear2([CBtn | AppObjs]) when is_record(CBtn, canvasbutton) -> - gs:destroy(CBtn#canvasbutton.text), - gs:destroy(CBtn#canvasbutton.ul), - gs:destroy(CBtn#canvasbutton.ll), - gs:destroy(CBtn#canvasbutton.rect), - draw_clear2(AppObjs); -draw_clear2([GSObj | AppObjs]) -> - gs:destroy(GSObj), - draw_clear2(AppObjs); -draw_clear2([]) -> - ignore. - -%% Display the applications, which are a list of tuples: {Pid,App,Descr} -%% Display them in the reversed order to get them chronologically -%% from left to right. -draw_apps(GUI, Apps) -> - {AppObjs, WCanvas} = draw_apps(GUI, lists:reverse(Apps), ?xNODELBL, - undefined, 0, []), - NewGUI = GUI#win{wcanvas=WCanvas, appobjs=AppObjs}, - display_scrollbar(NewGUI), - NewGUI. - -draw_apps(GUI, [App | Apps], X, Lx0, N, GSObjs) -> - - %% Some necessary data - {_Pid, AppName, _Descr} = App, - Text = atom_to_list(AppName), - Width = erlang:max(8*length(Text)+10, ?wBTN), - - %% Connect the application to the node label with a line - %% Lx0 = leftmost X coordinate (above previous application button) - %% Lx = X coordinate, Ly1, Ly2 = top and bottom Y coordinates - Lx = X + trunc(Width/2), - Line = case N of - %% First (leftmost application) - draw a vertical line - %% between the node label and application button - 0 -> - Ly1 = ?yNODELBL + ?hNODELBL +?PAD, - Ly2 = Ly1 + ?PAD2, - gs:create(line, GUI#win.canvas, - [{coords, [{Lx, Ly1}, {Lx, Ly2}]}]); - %% Nth application, N>1 - draw a horizontal line from - %% line connecting to the previous application button, - %% to above this application button, then vertically down - %% to the application button - _ -> - Ly1 = ?yNODELBL + ?hNODELBL + ?PAD + ?PAD2/2, - Ly2 = Ly1 + ?PAD2/2, - gs:create(line, GUI#win.canvas, - [{coords, [{Lx0, Ly1}, {Lx, Ly1}, - {Lx, Ly2}]}]) - end, - - %% The application is represented using a 'canvasbutton' - Data = {application, AppName, GUI#win.name}, - AppBtn = canvasbutton(GUI#win.canvas, Text, X, ?yBTN, Width, ?hBTN, - Data), - - draw_apps(GUI, Apps, X+Width+?PAD, Lx, N+1, [AppBtn, Line|GSObjs]); -draw_apps(_GUI, [], X, _N, _Lx0, GSObjs) -> - {GSObjs, X}. - -%%--Display functions--------------------------------------------------- -%% Functions that modify the GUI but not its data - -%% Add a new node to the Nodes menu -%% Used when a new node has connected -display_addnode([GUI|GUIs], Node) -> - display_addnode(GUI, Node), - display_addnode(GUIs, Node); -display_addnode([], _Node) -> - ignore; -display_addnode(GUI, Node) -> - Txt = "Show " ++ atom_to_list(Node), - gs:create(menuitem, GUI#win.nodemenu, - [{label, {text,Txt}}, - {data, {node, Node, GUI#win.window}}]). - -%% Show that a node has come back up -display_nodeup(GUI, Node) -> - {Rect, Text} = GUI#win.nodelabel, - - %% Check coordinates for the rectangle and compute the new width - [{L, T}, {_R, B}] = gs:read(Rect, coords), - NodeStr = atom_to_list(Node), - W = 8*length(NodeStr)+10, - - gs:config(Rect, [{coords, [{L, T}, {L+W, B}]}, {fill, black}]), - gs:config(Text, [{text, NodeStr}, {fg, {250,235,215}}, - {coords, - [{L+round(W/2), T+round((?hNODELBL)/2)}]}]). - -%% Show that a node has gone down -display_nodedown(GUI) -> - {Rect, Text} = GUI#win.nodelabel, - - [{L, T}, {_R, B}] = gs:read(Rect, coords), - gs:config(Rect, [{coords, [{L, T}, {L+114, B}]}, {fill, gray}]), - gs:config(Text, [{text, "No connection"}, {fg, black}, - {coords, [{L+57, T+round((?hNODELBL)/2)}]}]). - -%% Add/remove scrollbars as necessary -display_scrollbar(GUI) -> - - WWindow = GUI#win.wwindow, - HWindow = GUI#win.hwindow, - WCanvas = GUI#win.wcanvas, - HCanvas = GUI#win.hcanvas, - if - WCanvas>WWindow -> - gs:config(GUI#win.canvas, - [{hscroll, bottom}, - {scrollregion,{0,0,WCanvas,HCanvas}}]); - true -> - gs:config(GUI#win.canvas, [{hscroll, false}]) - end, - if - HCanvas>HWindow -> - gs:config(GUI#win.canvas, - [{vscroll, left}, - {scrollregion,{0,0,WCanvas,HCanvas}}]); - - true -> - gs:config(GUI#win.canvas, [{vscroll, false}]) - end. - -%% Select option radio buttons -display_setopt(GUI, Option) -> - gs:config(radiobutton(GUI, Option), {select,true}). - -radiobutton(GUI, single) -> (GUI#win.options)#options.single; -radiobutton(GUI, many) -> (GUI#win.options)#options.many; -radiobutton(GUI, time) -> (GUI#win.options)#options.time; -radiobutton(GUI, queue) -> (GUI#win.options)#options.queue; -radiobutton(GUI, prog) -> (GUI#win.options)#options.prog; -radiobutton(GUI, linear) -> (GUI#win.options)#options.linear. - -%% Display load -%% Used when load information is received from the displayed node --define(highloadfg, {255,99,71}). --define(midloadfg, yellow). --define(lowloadfg, green). --define(highloadbg, {140,157,178}). --define(midloadbg, ?highloadbg). --define(lowloadbg, ?highloadbg). - -display_load(GUI, {Old, New}) -> - if - Old == New -> - true; - Old > New -> - leds_down(GUI#win.leds, Old, New); - true -> - leds_up(GUI#win.leds, Old, New) - end. - -leds_down(_Leds, Old, New) when Old == New -> - done; -leds_down(Leds, Old, New) when Old > New -> - reset_led(Leds, Old), - leds_down(Leds, Old-1, New). -leds_up(_Leds, Old, New) when Old == New -> - done; -leds_up(Leds, Old, New) when Old < New -> - set_led(Leds, Old), - leds_up(Leds, Old+1, New). - -led_on_col(N) when N > 13 -> ?highloadfg; -led_on_col(N) when N > 9 -> ?midloadfg; -led_on_col(_) -> ?lowloadfg. - -led_off_col(N) when N > 13 -> ?highloadbg; -led_off_col(N) when N > 9 -> ?midloadbg; -led_off_col(_) -> ?lowloadbg. - -reset_led(_Leds, 0) -> ok; -reset_led(Leds, N) -> - gs:config(lists:nth(N, Leds), [{fill, led_off_col(N)}]). - -set_led(_Leds, 0) -> ok; -set_led(Leds, N) -> - gs:config(lists:nth(N, Leds), [{fill, led_on_col(N)}]). - -%%---------------------------------------------------------------------- -%% Utilities -%%---------------------------------------------------------------------- - -bcast(MNodes, Msg) -> - lists:foreach(fun(MNode) -> - case MNode#mnode.status of - alive -> - MNode#mnode.pid ! Msg; - dead -> - ignore - end - end, - MNodes). - -%% parse_nodes(MNodes) -> NodeApps -%% MNodes -> [#mnode{}] -%% NodeApps -> [{Node, Status, Apps}] -%% Node -> atom() -%% Status -> alive | dead -%% Apps -> [{Pid, App}] -%% Pid -> pid() -%% App -> atom() -parse_nodes(MNodes) -> - parse_nodes(MNodes, []). -parse_nodes([MNode|MNodes], NodeApps) -> - Apps = parse_apps(MNode#mnode.apps, []), - parse_nodes(MNodes, - [{MNode#mnode.name,MNode#mnode.status,Apps}|NodeApps]); -parse_nodes([], NodeApps) -> - NodeApps. - -parse_apps([{Pid, App, _Descr}|Rest], Apps) -> - parse_apps(Rest, [{Pid, App}|Apps]); -parse_apps([], Apps) -> - Apps. - -%%---------------------------------------------------------------------- -%% Canvas buttons -%%---------------------------------------------------------------------- - -canvasbutton(Canvas, Text, X, Y, W, H, Data) -> - - %% Draw a rectangle (for event catching) - Rect = gs:create(rectangle, Canvas, [{coords, [{X,Y}, {X+W,Y+H}]}, - {fill, gs:read(Canvas, bg)}, - {buttonpress, true}, - {buttonrelease, true}]), - - %% Make the rectangle area look like a 3D button by using lines - Ul = gs:create(line, Canvas, [{coords, [{X,Y+H},{X,Y},{X+W,Y}]}, - {fg, white}, {width, 2}]), - Ll = gs:create(line, Canvas, [{coords, [{X,Y+H},{X+W,Y+H},{X+W,Y}]}, - {fg, {87,87,87}}, {width, 2}]), - - %% Write the text in the middle - Xc = X + round(W/2), - Yc = Y + round(H/2), - T = gs:create(text, Canvas, [{text, Text}, {coords, [{Xc,Yc}]}, - {anchor, c}, - {buttonpress, true}, - {buttonrelease, true}]), - - %% Create the canvasbutton object - CBtn = #canvasbutton{text=T, ul=Ul, ll=Ll, rect=Rect, - x=X, y=Y, w=W, h=H}, - - %% Configure the data - gs:config(T, {data, {canvasbutton, CBtn, Data}}), - gs:config(Rect, {data, {canvasbutton, CBtn, Data}}), - - CBtn. - -press(Canvasbutton) -> - gs:config(Canvasbutton#canvasbutton.ul, {fg, {87,87,87}}), - gs:config(Canvasbutton#canvasbutton.ll, {fg, white}). - -release(Canvasbutton) -> - gs:config(Canvasbutton#canvasbutton.ul, {fg, white}), - gs:config(Canvasbutton#canvasbutton.ll, {fg, {87,87,87}}). |