diff --git a/documentation/06 _ Select SOH.help.md b/documentation/06 _ Select SOH.help.md
index 41e6eef89e12b35e57a55a2ebc8040dda98612a6..74ff7ba9b4f1a43488619266b229ce25eacabcc3 100644
--- a/documentation/06 _ Select SOH.help.md	
+++ b/documentation/06 _ Select SOH.help.md	
@@ -72,4 +72,4 @@ channels in the data set.
 + "Save": Save any changes to database
 + "Save - Add to Main": Save any changes to database and add the selected
 Preferred SOH's name to Main Window so that its SOH list will be included when
-reading the data set.
\ No newline at end of file
+reading the data set and the SOH channel will be plotted in this order.
\ No newline at end of file
diff --git a/sohstationviewer/model/data_type_model.py b/sohstationviewer/model/data_type_model.py
index 4f7b6253f786b0f1c7b51d3347779e44e5a6cecb..2f8c6a5dd21e56315dc7a9c6a6527002d3f5fd75 100644
--- a/sohstationviewer/model/data_type_model.py
+++ b/sohstationviewer/model/data_type_model.py
@@ -309,7 +309,6 @@ class DataTypeModel():
         self.sort_all_data()
         self.track_info("Combine data.", LogType.INFO)
         self.combine_traces_in_all_data()
-        self.check_not_found_soh_channels()
         for key in self.data_time:
             if self.data_time[key] == [constants.HIGHEST_INT, 0]:
                 # this happens when there is text or ascii only in the data
@@ -456,19 +455,6 @@ class DataTypeModel():
         execute_db(f'UPDATE PersistentData SET FieldValue="{self.tmp_dir}" '
                    f'WHERE FieldName="tempDataDirectory"')
 
-    def check_not_found_soh_channels(self):
-        all_chans_meet_req = (
-                list(self.soh_data[self.selected_key].keys()) +
-                list(self.mass_pos_data[self.selected_key].keys()) +
-                list(self.log_data[self.selected_key].keys()))
-
-        not_found_chans = [c for c in self.req_soh_chans
-                           if c not in all_chans_meet_req]
-        if not_found_chans != []:
-            msg = (f"No data found for the following channels: "
-                   f"{', '.join( not_found_chans)}")
-            self.processing_log.append((msg, LogType.WARNING))
-
     def combine_times_data_of_traces_w_spr_less_or_equal_1(
             self, data: Dict[str, Dict], selected_key: Union[(str, str), str],
             data_name: str):
diff --git a/sohstationviewer/view/main_window.py b/sohstationviewer/view/main_window.py
index 2e47c78a88eb4a38836764b0267d98cf453b0a92..073165a5145f10c3da87b76b0b6144de734f622a 100755
--- a/sohstationviewer/view/main_window.py
+++ b/sohstationviewer/view/main_window.py
@@ -546,7 +546,8 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
                       self.is_plotting_waveform or self.is_plotting_tps)
         if is_working:
             msg = 'Already working'
-            display_tracking_info(self.tracking_info_text_browser, msg, 'info')
+            display_tracking_info(self.tracking_info_text_browser,
+                                  msg, LogType.INFO)
             return
         self.has_problem = False
 
@@ -796,7 +797,8 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
 
         try:
             self.plotting_widget.plot_channels(
-                d_obj, sel_key, self.start_tm, self.end_tm, time_tick_total)
+                d_obj, sel_key, self.start_tm, self.end_tm, time_tick_total,
+                self.req_soh_chans)
         except Exception:
             fmt = traceback.format_exc()
             msg = f"Can't plot SOH data due to error: {str(fmt)}"
@@ -951,7 +953,8 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
                                'WHERE current=1')
         if len(rows) > 0:
             self.pref_soh_list_name = rows[0]['name']
-            self.pref_soh_list = [t.strip() for t in rows[0]['IDs'].split(',')]
+            self.pref_soh_list = [t.strip() for t in rows[0]['IDs'].split(',')
+                                  if t.strip() != '']
             self.pref_soh_list_data_type = rows[0]['dataType']
 
     def resizeEvent(self, event):
@@ -982,7 +985,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
         """
         display_tracking_info(self.tracking_info_text_browser,
                               'Cleaning up...',
-                              'info')
+                              LogType.INFO)
         if self.data_loader.running:
             self.data_loader.thread.requestInterruption()
             self.data_loader.thread.quit()
diff --git a/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py b/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py
index 35d207e62a5bb46624d6c4b362a66c89b5f863b2..0e36e7ab4e08bee2f3caba711566357eec7473f0 100644
--- a/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py
+++ b/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py
@@ -11,6 +11,8 @@ from sohstationviewer.view.plotting.plotting_widget.plotting_processor import (
 from sohstationviewer.view.plotting.plotting_widget.plotting_widget import (
     PlottingWidget)
 from sohstationviewer.view.util.enums import LogType
+from sohstationviewer.view.util.functions import (
+    replace_actual_question_chans, remove_not_found_chans)
 
 from sohstationviewer.controller.util import display_tracking_info
 from sohstationviewer.controller.plotting_data import get_title
@@ -28,7 +30,8 @@ class MultiThreadedPlottingWidget(PlottingWidget):
     def __init__(self, *args, **kwargs):
         PlottingWidget.__init__(self, *args, **kwargs)
         self.data_processors: List[PlottingChannelProcessor] = []
-
+        # pref_order: order of channels to be plotted
+        self.pref_order: List[str] = []
         # Only one data processor can run at a time, so it is not a big problem
         #
         self.thread_pool = QtCore.QThreadPool()
@@ -105,19 +108,33 @@ class MultiThreadedPlottingWidget(PlottingWidget):
             return True
 
     def create_plotting_channel_processors(
-            self, plotting_data: Dict, need_db_info: bool = False) -> None:
+            self, plotting_data: Dict,
+            need_db_info: bool = False) -> None:
         """
-        Create a data processor for each channel data.
+        Create a data processor for each channel data in the order of
+            pref_order. If pref_order isn't given, process in order of
+            plotting_data.
 
         :param plotting_data: dict of data by chan_id
         :param need_db_info: flag to get db info
         """
-        for chan_id in plotting_data:
+        chan_order = self.pref_order if self.pref_order \
+            else sorted(list(plotting_data.keys()))
+        chan_order = replace_actual_question_chans(
+            chan_order, list(plotting_data.keys()))
+        chan_order = remove_not_found_chans(
+            chan_order, list(plotting_data.keys()), self.processing_log)
+
+        not_plot_chans = []
+        for chan_id in chan_order:
             if need_db_info:
-                chan_db_info = get_chan_plot_info(
-                    chan_id, self.parent.data_type, self.c_mode)
-                if chan_db_info['height'] == 0:
+                chan_db_info = get_chan_plot_info(chan_id,
+                                                  self.parent.data_type,
+                                                  self.c_mode)
+                if (chan_db_info['height'] == 0 or
+                        chan_db_info['plotType'] == ''):
                     # not draw
+                    not_plot_chans.append(chan_id)
                     continue
                 if 'DEFAULT' in chan_db_info['channel']:
                     msg = (f"Channel {chan_id}'s "
@@ -127,14 +144,16 @@ class MultiThreadedPlottingWidget(PlottingWidget):
                     #  instruction here
                     self.processing_log.append((msg, LogType.WARNING))
 
-                if chan_db_info['plotType'] == '':
-                    continue
-
                 plotting_data[chan_id]['chan_db_info'] = chan_db_info
+        if not_plot_chans != []:
+            msg = (f"The database settings 'plotType' or 'height' show not to "
+                   f"be plotted for the following channels: "
+                   f"{', '.join( not_plot_chans)}")
+            self.processing_log.append((msg, LogType.WARNING))
 
         self.move_soh_channels_with_link_to_the_end()
 
-        for chan_id in plotting_data:
+        for chan_id in chan_order:
             if 'chan_db_info' not in plotting_data[chan_id]:
                 continue
             channel_processor = PlottingChannelProcessor(
@@ -165,7 +184,8 @@ class MultiThreadedPlottingWidget(PlottingWidget):
         for channel in channels_to_move:
             self.plotting_data1[channel] = self.plotting_data1.pop(channel)
 
-    def plot_channels(self, d_obj, key, start_tm, end_tm, time_ticks_total):
+    def plot_channels(self, d_obj, key, start_tm, end_tm, time_ticks_total,
+                      pref_order=[]):
         """
         Prepare to plot waveform/SOH/mass-position data by creating a data
         processor for each channel, then, run the processors.
@@ -175,12 +195,14 @@ class MultiThreadedPlottingWidget(PlottingWidget):
         :param start_tm: requested start time to read
         :param end_tm: requested end time to read
         :param time_ticks_total: max number of tick to show on time bar
+        :param pref_order: order of channels to be plotted
         """
+        self.pref_order = pref_order
         if not self.is_working:
             self.reset_widget()
             self.is_working = True
             start_msg = f'Plotting {self.name} data...'
-            display_tracking_info(self.tracking_box, start_msg, 'info')
+            display_tracking_info(self.tracking_box, start_msg, LogType.INFO)
             ret = self.init_plot(d_obj, key, start_tm, end_tm,
                                  time_ticks_total)
             if not ret:
@@ -188,8 +210,10 @@ class MultiThreadedPlottingWidget(PlottingWidget):
                 self.clean_up()
                 self.finished.emit()
                 return
+
             self.create_plotting_channel_processors(self.plotting_data1, True)
             self.create_plotting_channel_processors(self.plotting_data2, True)
+
             self.process_channel()
 
     @QtCore.Slot()
@@ -307,6 +331,6 @@ class MultiThreadedPlottingWidget(PlottingWidget):
         all running background threads.
         """
         display_tracking_info(self.tracking_box,
-                              f'{self.name} plot stopped', 'info')
+                              f'{self.name} plot stopped', LogType.INFO)
         self.is_working = False
         self.stopped.emit()
diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting.py b/sohstationviewer/view/plotting/plotting_widget/plotting.py
index 1a9268988a4960b05616b498961e8e3029a54e1a..faa1f90b097b42a274860aecfc252e4e0ee5df18 100644
--- a/sohstationviewer/view/plotting/plotting_widget/plotting.py
+++ b/sohstationviewer/view/plotting/plotting_widget/plotting.py
@@ -37,6 +37,7 @@ class Plotting:
             self.parent.plotting_bot, plot_h, has_min_max_lines=False)
         ax.x = None
         ax.plot([0], [0], linestyle="")
+        ax.chan_db_info = None
         return ax
 
     def plot_multi_color_dots(self, c_data, chan_db_info, chan_id,
@@ -120,6 +121,7 @@ class Plotting:
             ax.x = x
         else:
             ax.linkedX = x
+        ax.chan_db_info = chan_db_info
         return ax
 
     def plot_up_down_dots(self, c_data, chan_db_info, chan_id, ax, linked_ax):
@@ -184,6 +186,7 @@ class Plotting:
             ax.x = x
         else:
             ax.linkedX = x
+        ax.chan_db_info = chan_db_info
         return ax
 
     def plot_time_dots(self, c_data, chan_db_info, chan_id, ax, linked_ax):
@@ -226,6 +229,7 @@ class Plotting:
             ax.x_list = x_list
         else:
             ax.linkedX = x_list
+        ax.chan_db_info = chan_db_info
         return ax
 
     def plot_lines_dots(self, c_data, chan_db_info, chan_id,
@@ -315,6 +319,7 @@ class Plotting:
         else:
             ax.linkedX = x_list
             ax.linkedY = y_list
+        ax.chan_db_info = chan_db_info
         return ax
 
     def plot_lines_s_rate(self, c_data, chan_db_info, chan_id, ax, linked_ax):
@@ -399,4 +404,5 @@ class Plotting:
                        zorder=constants.Z_ORDER['DOT'])
         ax.x_list = x_list
         ax.y_list = y_list
+        ax.chan_db_info = chan_db_info
         return ax
diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py
index a26463af3e4afec0fba9b2b2c21f72a72e6ba8d4..da53fd8fea8b450de097386e6fbbb483377cfb63 100755
--- a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py
+++ b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py
@@ -604,7 +604,8 @@ class PlottingWidget(QtWidgets.QScrollArea):
                     pass
 
                 if new_min_y is not None:
-                    self.plotting_axes.set_axes_ylim(ax, new_min_y, new_max_y)
+                    self.plotting_axes.set_axes_ylim(
+                        ax, new_min_y, new_max_y, ax.chan_db_info)
 
     def draw(self):
         """
diff --git a/sohstationviewer/view/util/functions.py b/sohstationviewer/view/util/functions.py
index 254f32030c796164cd0399d3e7d938174df27c8d..58e3faab6a4945964b1f8094712631ebc74479ef 100644
--- a/sohstationviewer/view/util/functions.py
+++ b/sohstationviewer/view/util/functions.py
@@ -328,5 +328,49 @@ def get_index_from_time(chan_data: List[np.ndarray], tm: float, val: float) \
     return list_idx, section_idx
 
 
+def remove_not_found_chans(
+        chan_order: List[str], actual_chans: List[str],
+        processing_log: List[Tuple[str, LogType]]) -> List[str]:
+    """
+    Remove channels that are not found in actual_chans from chan_order.
+
+    :param chan_order: list of channels in order that user wants to plot
+    :param actual_chans: The actual channel list
+    :param processing_log: The log list to keep track with not found channels
+    :return: chan_order from which not found channels have been removed.
+    """
+    not_found_chans = [c for c in chan_order if c not in actual_chans]
+    if not_found_chans != []:
+        msg = (f"No data found for the following channels: "
+               f"{', '.join(not_found_chans)}")
+        processing_log.append((msg, LogType.WARNING))
+    return [c for c in chan_order if c not in not_found_chans]
+
+
+def replace_actual_question_chans(
+        chan_order: List[str], actual_chans: List[str]) -> List[str]:
+    """
+    Remove channels end with '?' from chan_order and replace with corresponding
+        channels found in actual channels.
+
+    :param chan_order: The list of channel that have channels end with '?'
+    :param actual_chans: The actual channel list
+    :return: chan_order that have channels end with '?' replaced by actual
+        channels.
+    """
+    question_chans = [c for c in chan_order if c.endswith('?')]
+    for qc in question_chans:
+        actual_question_chans = [c for c in list(actual_chans)
+                                 if qc[:-1] == c[:-1]]
+        if actual_question_chans:
+            question_idx = chan_order.index(qc)
+            chan_order.remove(qc)
+            # replace a question channel with the actual channels that it
+            # represent for
+            chan_order[question_idx:question_idx] = \
+                sorted(actual_question_chans)
+    return chan_order
+
+
 if __name__ == '__main__':
     create_table_of_content_file(Path('../../../documentation'))
diff --git a/tests/test_view/test_util_functions.py b/tests/test_view/test_util_functions.py
index bc59a41236a024290fa3540f5cf9a7106229de2e..8ad6187487f9eb673f656267f49908fc7ddfced9 100644
--- a/tests/test_view/test_util_functions.py
+++ b/tests/test_view/test_util_functions.py
@@ -9,7 +9,8 @@ from sohstationviewer.view.util.functions import (
     get_soh_messages_for_view, log_str, is_doc_file,
     create_search_results_file, create_table_of_content_file,
     check_chan_wildcards_format, check_masspos, get_total_miny_maxy,
-    extract_netcodes, get_index_from_time
+    extract_netcodes, get_index_from_time, remove_not_found_chans,
+    replace_actual_question_chans
 )
 
 from sohstationviewer.view.util.enums import LogType
@@ -501,3 +502,36 @@ class TestGetIndexFromTime(TestCase):
                 self.plotting_data['CH2'], 3, 4)
             self.assertEqual(list_idx, 1)
             self.assertEqual(section_idx, 0)
+
+
+class RemoveNotFoundChansClass(TestCase):
+    def test_remove_not_found_chans(self):
+        chan_order = ['A', 'B', 'C', 'D']
+        actual_chans = ['C', 'D', 'E', 'F']
+        processing_log = []
+        expected_new_chan_order = ['C', 'D']
+        expected_processing_log = [
+            ("No data found for the following channels: A, B",
+             LogType.WARNING)]
+
+        ret = remove_not_found_chans(chan_order, actual_chans, processing_log)
+        self.assertListEqual(ret, expected_new_chan_order)
+        self.assertEqual(processing_log, expected_processing_log)
+
+
+class ReplaceActualQuestChans(TestCase):
+    def test_question_chans_in_actual_chans(self):
+        chan_order = ['A', 'B', 'C?', 'D']
+        actual_chans = ['C1', 'C3', 'C2', 'D', 'E', 'F']
+        expected_new_chan_order = ['A', 'B', 'C1', 'C2', 'C3', 'D']
+
+        ret = replace_actual_question_chans(chan_order, actual_chans)
+        self.assertListEqual(ret, expected_new_chan_order)
+
+    def test_question_chans_not_in_actual_chans(self):
+        chan_order = ['A?', 'B', 'C', 'D']
+        actual_chans = ['C', 'D', 'E', 'F']
+        expected_new_chan_order = ['A?', 'B', 'C', 'D']
+
+        ret = replace_actual_question_chans(chan_order, actual_chans)
+        self.assertListEqual(ret, expected_new_chan_order)