diff options
Diffstat (limited to 'lib/tv/src/tv_pc.erl')
-rw-r--r-- | lib/tv/src/tv_pc.erl | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/lib/tv/src/tv_pc.erl b/lib/tv/src/tv_pc.erl new file mode 100644 index 0000000000..50214fe06a --- /dev/null +++ b/lib/tv/src/tv_pc.erl @@ -0,0 +1,794 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. 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% +%%%********************************************************************* +%%% +%%% Description: pc part of the table tool, i.e., the process +%%% controlling all other processes, and managing +%%% the actions to take. +%%% +%%%********************************************************************* + + +-module(tv_pc). + + + +-export([pc/7, + send_data/2 + ]). + + + +-include("tv_int_def.hrl"). +-include("tv_int_msg.hrl"). +-include("tv_pc_int_def.hrl"). + + + + +%%%********************************************************************* +%%% EXTERNAL FUNCTIONS +%%%********************************************************************* + + + +%%====================================================================== +%% Function: pc. +%% +%% Return Value: None. +%% +%% Description: Process controlling the processes 'pd', 'pw', 'dbs' and 'etsread'. +%% After necessary initialisations, an eternal loop is +%% entered, where window created messages are received and +%% handled, as well as user input. +%% +%% Parameters: +%%====================================================================== + + +pc(Master, Node, LocalNode, TableId, KindOfTable, TableName, ErrMsgMode) -> + process_flag(trap_exit, true), + put(error_msg_mode, ErrMsgMode), + ProcVars = prepare_and_open_table(Node, LocalNode, TableId, KindOfTable, TableName, + false, #process_variables{parent_pid=Master}), + loop(ProcVars). + + + + + + + +%%%******************************************************************** +%%% INTERNAL FUNCTIONS +%%%******************************************************************** + + + + + +prepare_and_open_table(Node, LocalNode, TabId, TabType, TabName, Raise, ProcVars) -> + IpPid = spawn(tv_ip, ip, [self()]), + show_progress(IpPid, 5, "Initializing graphics..."), + + TmpProcVars = start_procs(IpPid, ProcVars), + + show_progress(IpPid, 5, "Loading table..."), + NewProcVars = ?MENU_FUNC_FILE:open_table(Node, LocalNode, TabId, TabType, TabName, + Raise, TmpProcVars), + + IpPid ! #ip_quit{sender = self()}, + % Now make window visible! + WinP = NewProcVars#process_variables.window_params, + gs:config(WinP#window_params.window_id, [{map, true}]), + NewProcVars. + + + + + +start_procs(IpPid, ProcVars) -> + ErrorMsgMode = get(error_msg_mode), + PwPid = spawn_link(tv_pw, pw, [self()]), + PdPid = spawn_link(tv_pd, pd, [self(), ErrorMsgMode]), + DbsPid = spawn_link(tv_db, dbs, [self(), ErrorMsgMode]), + EtsreadPid = spawn_link(tv_etsread, etsread, [self(), ErrorMsgMode]), + + show_progress(IpPid, 5, "Initializing graphics..."), + NewWinP = init_pw(PwPid, ProcVars), + + show_progress(IpPid, 5, "Initializing graphics..."), + init_pd(PdPid, NewWinP), + ProcVars#process_variables{pw_pid = PwPid, + pd_pid = PdPid, + dbs_pid = DbsPid, + etsread_pid = EtsreadPid, + current_node = node(), %% Will be replaced, when table opened. + local_node = true, + window_params = NewWinP + }. + + + + + + + +%%====================================================================== +%% Function: +%% +%% Return Value: +%% +%% Description: +%% +%% Parameters: +%%====================================================================== + + +show_progress(IpPid, NofElements, Text) -> + IpPid ! #ip_update{sender = self(), + nof_elements_to_mark = NofElements, + text = Text + }. + + + + + + + +%%====================================================================== +%% Function: loop. +%% +%% Return Value: None. +%% +%% Description: Eternal (well, almost) loop, receiving messages and +%% handling them. +%% +%% Parameters: None. +%%====================================================================== + + +loop(ProcVars) -> + receive + Msg -> + case Msg of + + % Normal messages! + #dbs_subset{} -> + NewProcVars1 = send_data(Msg, ProcVars), + NewProcVars2 = check_time_to_poll_table(Msg, NewProcVars1), + loop(NewProcVars2); + + #pc_poll_table{} -> + TmpProcVars = check_node(ProcVars), + NewProcVars = ?MENU_FUNC_FILE:poll_table(TmpProcVars), + loop(NewProcVars); + + #pc_search_req{} -> + DbsPid = ProcVars#process_variables.dbs_pid, + DbsPid ! #dbs_search_req{sender=self()}, + loop(ProcVars); + + #pc_set_sorting_mode{} -> + set_sorting_mode(Msg, ProcVars), + loop(ProcVars); + + + #pc_data_req{element = Pos, nof_elements = Length} -> + DbsPid = ProcVars#process_variables.dbs_pid, + DbsPid ! #dbs_subset_req{sender = self(), + subset_pos = Pos, + subset_length = Length + }, + loop(ProcVars); + + + #pc_marked_row{row_no=RowNo, object=Obj, color=Color} -> + DbsPid = ProcVars#process_variables.dbs_pid, + DbsPid ! #dbs_marked_row{sender = self(), + row_no = RowNo + }, + NewProcVars = ProcVars#process_variables{marked_row = RowNo, + marked_object = Obj, + marked_color = Color}, + loop(NewProcVars); + + + #pc_menu_msg{} -> + Fcn = Msg#pc_menu_msg.data, + NewProcVars = ?MENU_FUNC_FILE:Fcn(ProcVars), + loop(NewProcVars); + + + #pd_updated_object{object=Obj,old_object=OldObj,old_color=Color,obj_no=ObjNo} -> + DbsPid = ProcVars#process_variables.dbs_pid, + DbsPid ! #dbs_updated_object{sender = self(), + object = Obj, + old_object = OldObj, + old_color = Color, + obj_no = ObjNo}, + loop(ProcVars); + + + #pd_new_object{object=Obj} -> + DbsPid = ProcVars#process_variables.dbs_pid, + DbsPid ! #dbs_new_object{sender = self(), + object = Obj}, + loop(ProcVars); + + + #pc_show_table_info{} -> + NewProcVars = ?MENU_FUNC_FILE:table_info(ProcVars), + loop(NewProcVars); + + #pc_win_conf{} -> + NewProcVars = ?GRAPH_FUNC_FILE:win_conf(Msg, ProcVars), + loop(NewProcVars); + + #pc_help{} -> + NewProcVars = ?MENU_FUNC_FILE:help_button(ProcVars), + loop(NewProcVars); + + #pc_dead_table{automatic_polling = AutoPoll} -> + WinP = ProcVars#process_variables.window_params, + WinId = WinP#window_params.window_id, + gs:config(WinId, [beep]), + case get(error_msg_mode) of + normal -> + tv_utils:notify(WinId, "TV Notification", + ["The table no longer exists!"]); + haiku -> + ErrMsg1 = ["A table that big?", + "It might be very useful.", + "But now it is gone."], + tv_utils:notify(WinId, "TV Notification", ErrMsg1) + end, + NewProcVars = + case AutoPoll of + true -> + gs:config(WinId, [beep]), + case get(error_msg_mode) of + normal -> + tv_utils:notify(WinId, "TV Notification", + ["The automatic polling is turned off!"]); + haiku -> + ErrMsg2 = ["Previously on", + "The polling is now idled.", + "That's the way it is."], + tv_utils:notify(WinId, "TV Notification", ErrMsg2) + end, + ProcVars#process_variables{poll_interval = infinity}; + false -> + ProcVars + end, + loop(NewProcVars); + + #pc_nodedown{automatic_polling = AutoPoll} -> + WinP = ProcVars#process_variables.window_params, + WinId = WinP#window_params.window_id, + gs:config(WinId, [beep]), + case get(error_msg_mode) of + normal -> + tv_utils:notify(WinId, "TV Notification", + ["The node is down, and the", + "table cannot be reached."]); + haiku -> + ErrMsg1 = ["With searching comes loss", + "And the presence of absence:", + "Node is down."], + tv_utils:notify(WinId, "TV Notification", ErrMsg1) + end, + NewProcVars = + case AutoPoll of + true -> + gs:config(WinId, [beep]), + case get(error_msg_mode) of + normal -> + tv_utils:notify(WinId, "TV Notification", + ["The automatic polling is turned off!"]); + haiku -> + ErrMsg = ["Previously on,", + "The polling is now idled.", + "That's the way it is."], + tv_utils:notify(WinId, "TV Notification", ErrMsg) + end, + ProcVars#process_variables{poll_interval = infinity}; + false -> + ProcVars + end, + loop(NewProcVars); + + + {pc_edit_object, _Sender} -> + NewProcVars = ?MENU_FUNC_FILE:insert_object(ProcVars), + loop(NewProcVars); + + + check_node -> + NewProcVars = check_node(ProcVars), + loop(NewProcVars); + + + raise -> + WinP = ProcVars#process_variables.window_params, + gs:config(WinP#window_params.window_id, [raise]), + loop(ProcVars); + + + {error_msg_mode, Mode} -> + ProcVars#process_variables.dbs_pid ! {error_msg_mode, Mode}, + ProcVars#process_variables.etsread_pid ! {error_msg_mode, Mode}, + ProcVars#process_variables.pd_pid ! {error_msg_mode, Mode}, + put(error_msg_mode, Mode), + loop(ProcVars); + + % Exit messages! + {'EXIT', Sender, Reason} -> + exit_signals({Sender, Reason}, ProcVars); + + + _Other -> + loop(ProcVars) + + end + end. + + + + + + +check_node(ProcVars) -> + #process_variables{pw_pid = PwPid, + current_node = OldCurrNode, + local_node = LocalNode, + table_id = TableId, + table_type = TableType, + table_name = TableName} = ProcVars, + + HomeNode = node(), + case net_adm:ping(OldCurrNode) of + pong -> + ProcVars; + pang when not LocalNode -> + ProcVars; + pang when LocalNode -> + %% XXX [siri] Will this ever happen? I thought local_node + %% indicated if current_node was the node where tv was + %% started. If so, we are pinging ourselves here, and + %% a pang can never happen?? + WinTitle = ?MENU_FUNC_FILE:get_window_title(TableType,HomeNode,TableId,TableName), + PwPid ! #pw_set_window_title{sender = self(), + win_title = WinTitle}, + ProcVars#process_variables{current_node = HomeNode} + end. + + + + + + + +send_data(Msg, ProcVars) -> + #process_variables{pd_pid = PdPid, + parent_pid = ParentPid, + table_id = Table, + table_type = Type, + current_node = Node} = ProcVars, + + ParentPid ! {tv_update_infowin, Table, Node, Type}, + + #dbs_subset{data = DbData, + subset_pos = ScalePos, + db_length = DbLength, + list_of_keys = ListOfKeys, + max_elem_size = MaxElemSize, + requested_row = ReqRowData} = Msg, + + Range = case ScalePos of + 0 -> + {0, 0}; + _Other -> + {1, DbLength} + end, + + PdPid ! #pc_data{sender = self(), + scale_pos = ScalePos, + scale_range = Range, + elementlist = DbData, + list_of_keys = ListOfKeys, + max_elem_size = MaxElemSize, + marked_row = ReqRowData + }, + + {MarkedObject, MarkedColor} = + case ReqRowData of + [] -> + {undefined, undefined}; + [Data] -> + Data + end, + ProcVars#process_variables{marked_object = MarkedObject, + marked_color = MarkedColor}. + + + + + + + + +%%====================================================================== +%% Function: +%% +%% Return Value: +%% +%% Description: +%% +%% Parameters: +%%====================================================================== + + +set_sorting_mode(Msg, ProcVars) -> + #pc_set_sorting_mode{sorting = Sorting, + reverse = Reverse, + sort_key_no = SortKeyNo} = Msg, + + DbsPid = ProcVars#process_variables.dbs_pid, + PdPid = ProcVars#process_variables.pd_pid, + PwPid = ProcVars#process_variables.pw_pid, + TableType = ProcVars#process_variables.table_type, + + NewSortKeyNo = + case SortKeyNo of + undefined -> + if + TableType =:= mnesia -> + 2; + true -> + 1 + end; + _Other -> + SortKeyNo + end, + + Menu = + case Sorting of + true -> + case Reverse of + true -> + sort_falling_order; + false -> + sort_rising_order + end; + false -> + no_sorting + end, + + PwPid ! #pw_select_menu{sender = self(), + menu = Menu}, + + DbsPid ! #dbs_sorting_mode{sender = self(), + sorting = Sorting, + reverse = Reverse, + sort_key_no = NewSortKeyNo + }, + + PdPid ! #pc_set_sorting_mode_cfm{sender = self(), + sort_key_no = NewSortKeyNo + }. + + + + + + + +%%====================================================================== +%% Function: init_pw. +%% +%% Return Value: Tuple containing the Pid of the pw process, and the id of +%% the window created by the pw process. +%% +%% Description: Starts the pw process, and orders it to create a window. +%% (The size of the window may be given as option.) +%% +%% Parameters: None. +%%====================================================================== + + + +init_pw(PwPid, ProcVars) -> + #process_variables{window_params = WinP} = ProcVars, + + % Now deblock pw, and order it to create a window! + PwPid ! #pw_deblock{sender = self(), + win_title = ?APPLICATION_NAME, + win_width = ?DEFAULT_WINDOW_WIDTH, + win_height = ?DEFAULT_WINDOW_HEIGHT, + min_win_width = ?DEFAULT_MIN_WINDOW_WIDTH, + min_win_height = ?DEFAULT_MIN_WINDOW_HEIGHT + }, + + + receive + #pw_deblock_cfm{win_id = WindowId} -> + ?MENU_FUNC_FILE:create_menus(PwPid), + + % Store the window id as well as the size of it. + WinP#window_params{window_id = WindowId, + window_width = ?DEFAULT_WINDOW_WIDTH, + window_height = ?DEFAULT_WINDOW_HEIGHT + } + + + after 180000 -> % A timeout of 1000 ms is too short, at least the first + % time the system is started! + exit(error) + end. + + + + + + + + + + +%%====================================================================== +%% Function: +%% +%% Return Value: +%% +%% Description: +%% +%% Parameters: +%%====================================================================== + + +init_pd(PdPid, WinP) -> + #window_params{window_id = WindowId, + window_width = WindowWidth, + window_height = WindowHeight} = WinP, + + % Now deblock pd, and order it to create a canvas and a scale! + PdPid ! #pd_deblock{sender = self(), + win = WindowId, + win_width = WindowWidth, + win_height = WindowHeight, + scale = true + }, + + receive + #pd_deblock_cfm{} -> + done + after 180000 -> + exit(error) + end. + + + + + + + + + + +%%====================================================================== +%% Function: exit_signals. +%% +%% Return Value: None. +%% +%% Description: Decides, given an error message, action to take, i.e., whether +%% operation shall procede, any process shall be restarted, or +%% the table tool terminated. +%% +%% Parameters: Exit_info: tuple containing sender of the error message, and the +%% reason. +%%====================================================================== + + +exit_signals(ExitInfo, ProcVars) -> + #process_variables{parent_pid = ParentPid, + pd_pid = PdPid, + pw_pid = PwPid, + dbs_pid = DbsPid, + etsread_pid = EtsreadPid, + table_id = TabId, + table_type = TabType, + table_name = TabName, + current_node = Node, + local_node = LocalNode + } = ProcVars, + + case ExitInfo of + {ParentPid, Reason} -> + exit(Reason); + + {PwPid, normal} -> + exit(normal); + + {PwPid, error} -> + io:format("Internal error... restarting. ~n"), + kill_procs(normal, [PdPid, EtsreadPid, DbsPid]), + NewProcVars = pc(ParentPid, Node, LocalNode, TabId, TabType, TabName, + get(error_msg_mode)), + loop(NewProcVars); + + {PdPid, _Reason} -> + io:format("Internal error... restarting. ~n"), + kill_procs(normal, [PwPid, EtsreadPid, DbsPid]), + NewProcVars = pc(ParentPid, Node, LocalNode, TabId, TabType, TabName, + get(error_msg_mode)), + loop(NewProcVars); + + {DbsPid, _Reason} -> + io:format("Internal error... restarting. ~n"), + kill_procs(normal, [PdPid, PwPid, EtsreadPid]), + NewProcVars = pc(ParentPid, Node, LocalNode, TabId, TabType, TabName, + get(error_msg_mode)), + loop(NewProcVars); + + {EtsreadPid, _Reason} -> + io:format("Internal error... restarting. ~n"), + kill_procs(normal, [PdPid, PwPid, DbsPid]), + NewProcVars = pc(ParentPid, Node, LocalNode, TabId, TabType, TabName, + get(error_msg_mode)), + loop(NewProcVars); + + {_Sender, _OtherReason} -> + loop(ProcVars) + end. + + + + + + + + + +%%====================================================================== +%% Function: +%% +%% Return Value: +%% +%% Description: +%% +%% Parameters: +%%====================================================================== + + +kill_procs(_Status, []) -> + done; +kill_procs(Status, [Pid | Tail]) -> + exit(Pid, Status), + kill_procs(Status, Tail). + + + + +%%====================================================================== +%% Function: +%% +%% Return Value: +%% +%% Description: +%% +%% Parameters: +%%====================================================================== + + +check_time_to_poll_table(Msg, ProcVars) -> + #dbs_subset{required_time_etsread = EtsreadTime, + required_time_dbs = DbsTime} = Msg, + + UserSetPollInterval = ProcVars#process_variables.poll_interval, + WinP = ProcVars#process_variables.window_params, + WinId = WinP#window_params.window_id, + + case too_short_pollinterval_chosen(UserSetPollInterval, EtsreadTime, DbsTime) of + true -> + EtsreadPid = ProcVars#process_variables.etsread_pid, + EtsreadPid ! #etsread_set_poll_interval{sender = self(), + interval = infinity}, + + TimeRequired = trunc(max_time_required(EtsreadTime, DbsTime) / 10 + 0.5) * 10 + 20, + + gs:config(WinId, [beep]), + case get(error_msg_mode) of + normal -> + tv_utils:notify(WinId, "TV Notification", + ["The current poll interval is too short!"]), + Str = "to " ++ lists:flatten(io_lib:write(TimeRequired)) ++ " seconds!", + tv_utils:notify(WinId, "TV Notification", ["Setting the poll interval", Str]); + haiku -> + ErrMsg = ["Being way too short", + "The interval of polling", + "Is simply increased."], + tv_utils:notify(WinId, "TV Notification", ErrMsg) + end, + clear_message_buffer(), + EtsreadPid ! #etsread_set_poll_interval{sender = self(), + interval = TimeRequired}, + + ProcVars#process_variables{poll_interval = TimeRequired}; + false -> + ProcVars + end. + + + + + + +clear_message_buffer() -> + receive + #dbs_subset{} -> + clear_message_buffer() + after 100 -> + done + end. + + + + + +max_time_required(T1, T2) when is_number(T1), is_number(T2) -> + if + T1 > T2 -> + T1; + true -> + T2 + end; +max_time_required(T1, _T2) when is_number(T1) -> + T1; +max_time_required(_T1, T2) -> + T2. + + + + + + + + +%%====================================================================== +%% Function: +%% +%% Return Value: +%% +%% Description: +%% +%% Parameters: +%%====================================================================== + + +too_short_pollinterval_chosen(infinity, _EtsreadTime, _DbsTime) -> + false; +too_short_pollinterval_chosen(undefined, _EtsreadTime, _DbsTime) -> + false; +too_short_pollinterval_chosen(PollInt, EtsreadTime, _DbsTime) when EtsreadTime >= PollInt, is_number(EtsreadTime) -> + true; +too_short_pollinterval_chosen(PollInt, _EtsreadTime, DbsTime) when DbsTime >= PollInt, is_number(DbsTime) -> + true; +too_short_pollinterval_chosen(_PollInt, _EtsreadTime, _DbsTime) -> + false. |