aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tv/src/tv_pc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tv/src/tv_pc.erl')
-rw-r--r--lib/tv/src/tv_pc.erl794
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.