From 104a2d00d31ddbc1c797d4d443b8e6d68bf3242b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20M=C3=B6slinger?= <dominik.moeslinger@univie.ac.at>
Date: Tue, 6 Jul 2021 11:21:26 +0200
Subject: [PATCH] TST: Slightly changed logging behaviour, step_id and run_id
 added

progress_view.py can be sorted by execution_id
tooltip text-code implemented (not completly finished)
---
 Tst/confignator/confignator/config.py         |   2 +-
 Tst/progress_view/progress_view.py            | 701 +++++++++++++-----
 .../testlib/analyse_command_log.py            |  53 +-
 .../testlib/analyse_verification_log.py       |  18 +-
 Tst/testing_library/testlib/report.py         |  91 ++-
 Tst/testing_library/testlib/testing_logger.py |   8 +-
 Tst/tst/generator_templates/co_class.py       |   1 +
 Tst/tst/generator_templates/co_footer.py      |  10 +
 Tst/tst/generator_templates/co_header.py      |   1 +
 .../generator_templates/co_post_condition.py  |   6 +-
 .../co_post_condition_entry.py                |  10 +
 .../generator_templates/co_pre_condition.py   |   7 +-
 .../co_pre_condition_entry.py                 |  10 +
 Tst/tst/generator_templates/co_step.py        |  10 +-
 Tst/tst/generator_templates/run_header.py     |   3 +-
 Tst/tst/generator_templates/ver_class.py      |  14 +
 Tst/tst/generator_templates/ver_header.py     |   1 +
 Tst/tst/generator_templates/ver_step.py       |  17 +-
 18 files changed, 702 insertions(+), 261 deletions(-)

diff --git a/Tst/confignator/confignator/config.py b/Tst/confignator/confignator/config.py
index 251f53c..169cbf8 100755
--- a/Tst/confignator/confignator/config.py
+++ b/Tst/confignator/confignator/config.py
@@ -72,7 +72,7 @@ fmt += '%(thread)s\t'
 fmt += '%(threadName)s\t'
 
 logging_format = fmt
-logging_level_file = logging.DEBUG
+logging_level_file = logging.INFO
 logging_level_console = logging.WARNING
 module_logger = logging.getLogger(__name__)
 module_logger.setLevel(logging.DEBUG)
diff --git a/Tst/progress_view/progress_view.py b/Tst/progress_view/progress_view.py
index 5331bed..dd529da 100644
--- a/Tst/progress_view/progress_view.py
+++ b/Tst/progress_view/progress_view.py
@@ -30,7 +30,6 @@ logger.addHandler(hdlr=console_hdlr)
 file_hdlr = toolbox.create_file_handler(file=log_file_path)
 logger.addHandler(hdlr=file_hdlr)
 
-
 menu_xml = os.path.join(os.path.dirname(__file__), 'app_menu.xml')
 css_file = os.path.join(os.path.dirname(__file__), 'style_progress_view.css')
 
@@ -106,7 +105,7 @@ class TestProgressView(Gtk.ApplicationWindow):
 
         self.expander_states = []
 
-        self.progress_tree_store = Gtk.TreeStore(str, str, str, str, str, str, str, str, str, str)
+        self.progress_tree_store = Gtk.TreeStore(str, str, str, str, str, str, str, str, str, str, str)
 
         # monitoring the cmd and vrc log files for changes
         file_cmd = Gio.File.new_for_path(self.path_cmd)
@@ -174,7 +173,92 @@ class TestProgressView(Gtk.ApplicationWindow):
         self.path_frame.add(self.path_box)
         self.box.pack_start(self.path_frame, True, True, 0)
 
+        self.title_box = Gtk.HBox()
+        self.test_label = Gtk.Label()
+        self.test_label.set_markup('<big>Test Title: </big>')
+        self.test_title = Gtk.Label()
+        self.set_test_title()
+
+        self.title_box.pack_start(self.test_label, False, True, 0)
+        self.title_box.pack_start(self.test_title, False, True, 0)
+        self.box.pack_start(self.title_box, False, True, 20)
+
         # buttons for the tree view (expand all, collapse all)
+        self.make_button_box()
+        self.box.pack_start(self.box_buttons, False, True, 0)
+
+        self.btn_apply_css = Gtk.Button().new_from_icon_name('media-playlist-repeat-symbolic', Gtk.IconSize.BUTTON)
+        self.btn_apply_css.set_tooltip_text('Apply CSS')
+        self.btn_apply_css.connect('clicked', self.on_apply_css)
+        # self.box_buttons.pack_start(self.btn_apply_css, False, False, 0)
+
+        # --------------- tree view ---------------
+        self.sorted_model = Gtk.TreeModelSort(model=self.progress_tree_store)
+        self.sorted_model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
+        self.view = Gtk.TreeView(model=self.sorted_model)
+        self.view.set_grid_lines(True)
+        self.view.set_has_tooltip(True)
+        self.view.set_tooltip_column(10)
+        self.make_treeview()
+
+        self.view.expand_all()
+
+        self.scroll_win = Gtk.ScrolledWindow()
+        self.scroll_win.set_min_content_height(1200)
+        self.scroll_win.set_min_content_width(900)
+        self.scroll_win.add(self.view)
+        self.box.pack_start(self.scroll_win, True, True, 0)
+
+        self.connect('destroy', self.on_destroy)
+
+        # expand all entries
+        self.view.expand_all()
+
+        self.refresh_rate = 1
+        self.refresh_worker()
+
+        # for styling the application with CSS
+        context = self.get_style_context()
+        Gtk.StyleContext.add_class(context, 'tst-css')
+        self.on_apply_css()
+        self.show_all()
+        logger.debug('__init__ succeeded')
+
+    @staticmethod
+    def on_apply_css(*args):
+        logger.debug('Applying CSS')
+        style_provider = Gtk.CssProvider()
+        css = open(css_file, 'rb')  # rb needed for python 3 support
+        css_data = css.read()
+        css.close()
+        style_provider.load_from_data(css_data)
+        Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
+                                                 style_provider,
+                                                 Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+        Gtk.StyleContext.reset_widgets(Gdk.Screen.get_default())
+
+    def on_open(self, simple_action, parameter):
+        """
+        Menu -> Open: choose a Test specification file. The command log and verification log files will be loaded
+        automatically. Using the path in the configuration file.
+        :param Gio.SimpleAction simple_action: The object which received the signal
+        :param parameter: the parameter to the activation, or None if it has no parameter
+        """
+        dialog = Gtk.FileChooserDialog('Please choose a Test Specification',
+                                       self,
+                                       Gtk.FileChooserAction.OPEN,
+                                       (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
+        self.add_filters(dialog)
+        response = dialog.run()
+        if response == Gtk.ResponseType.OK:
+            file_selected = dialog.get_filename()
+            # ToDo: get the path for all 3 (json, cmd, vrc) and load them
+            self.open_test_files(None, self.get_log_file_paths_from_json_file_name(file_selected))
+        elif response == Gtk.ResponseType.CANCEL:
+            pass
+        dialog.destroy()
+
+    def make_button_box(self):
         self.box_buttons = Gtk.Box()
         self.box_buttons.set_orientation(Gtk.Orientation.HORIZONTAL)
         self.btn_exp_all = Gtk.Button()
@@ -189,24 +273,39 @@ class TestProgressView(Gtk.ApplicationWindow):
         self.btn_rld_all.set_label('Reload all')
         self.btn_rld_all.connect('clicked', self.on_reload_all)
         self.box_buttons.pack_start(self.btn_rld_all, False, False, 0)
-        self.box.pack_start(self.box_buttons, False, True, 0)
 
-        self.btn_apply_css = Gtk.Button().new_from_icon_name('media-playlist-repeat-symbolic', Gtk.IconSize.BUTTON)
-        self.btn_apply_css.set_tooltip_text('Apply CSS')
-        self.btn_apply_css.connect('clicked', self.on_apply_css)
-        # self.box_buttons.pack_start(self.btn_apply_css, False, False, 0)
+        self.sort_label = Gtk.Label()
+        self.sort_label.set_text('Sort by Execution')
+        self.box_buttons.pack_end(self.sort_label, False, True, 0)
 
-        # --------------- tree view ---------------
-        self.sorted_model = Gtk.TreeModelSort(model=self.progress_tree_store)
-        self.sorted_model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
-        self.view = Gtk.TreeView(model=self.sorted_model)
-        self.view.set_grid_lines(True)
+        self.sort_button = Gtk.Switch()
+        self.sort_button.connect("notify::active", self.on_remake_treeview)
+        self.sort_button.set_active(False)
+        self.box_buttons.pack_end(self.sort_button, False, True, 0)
+
+        self.sort_label2 = Gtk.Label()
+        self.sort_label2.set_text('Sort by Steps')
+        self.box_buttons.pack_end(self.sort_label2, False, True, 0)
+
+    def make_treeview(self):
         # self.view.set_enable_tree_lines(True)
 
+        if self.sort_button.get_active():  # Only if sorted by executions
+            # column 0
+            renderer_number = Gtk.CellRendererText()
+            #renderer_number.set_property('scale', 2)
+            renderer_number.set_property('single-paragraph-mode', True)
+            execution_number = Gtk.TreeViewColumn('Run ', renderer_number, text=11)
+            execution_number.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+            execution_number.set_resizable(True)
+            execution_number.set_min_width(50)
+            self.view.append_column(execution_number)
+
         # column 1
         renderer_number = Gtk.CellRendererText()
-        renderer_number.set_property('scale', 2)
-        renderer_number.set_property('single-paragraph-mode', True)
+        if not self.sort_button.get_active():  # Only big if sorted by steps, otherwise normal size
+            renderer_number.set_property('scale', 2)
+            renderer_number.set_property('single-paragraph-mode', True)
         column_number = Gtk.TreeViewColumn('Step', renderer_number, text=8)
         column_number.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
         column_number.set_resizable(True)
@@ -272,64 +371,14 @@ class TestProgressView(Gtk.ApplicationWindow):
         column_result.set_min_width(50)
         # column_result.set_cell_data_func(cell_renderer=renderer_result, func=set_bkgrd_clr, func_data=None)
         self.view.append_column(column_result)
-
-        self.view.expand_all()
-
-        self.scroll_win = Gtk.ScrolledWindow()
-        self.scroll_win.set_min_content_height(1200)
-        self.scroll_win.set_min_content_width(900)
-        self.scroll_win.add(self.view)
-        self.box.pack_start(self.scroll_win, True, True, 0)
-
-        self.connect('destroy', self.on_destroy)
-
-        # expand all entries
-        self.view.expand_all()
-
-        self.refresh_rate = 1
-        self.refresh_worker()
-
-        # for styling the application with CSS
-        context = self.get_style_context()
-        Gtk.StyleContext.add_class(context, 'tst-css')
-        self.on_apply_css()
-        self.show_all()
-        logger.debug('__init__ succeeded')
-
-    @staticmethod
-    def on_apply_css(*args):
-        logger.debug('Applying CSS')
-        style_provider = Gtk.CssProvider()
-        css = open(css_file, 'rb')  # rb needed for python 3 support
-        css_data = css.read()
-        css.close()
-        style_provider.load_from_data(css_data)
-        Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
-                                                 style_provider,
-                                                 Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
-        Gtk.StyleContext.reset_widgets(Gdk.Screen.get_default())
-
-    def on_open(self, simple_action, parameter):
-        """
-        Menu -> Open: choose a Test specification file. The command log and verification log files will be loaded
-        automatically. Using the path in the configuration file.
-        :param Gio.SimpleAction simple_action: The object which received the signal
-        :param parameter: the parameter to the activation, or None if it has no parameter
-        """
-        dialog = Gtk.FileChooserDialog('Please choose a Test Specification',
-                                       self,
-                                       Gtk.FileChooserAction.OPEN,
-                                       (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
-        self.add_filters(dialog)
-        response = dialog.run()
-        if response == Gtk.ResponseType.OK:
-            file_selected = dialog.get_filename()
-            # ToDo: get the path for all 3 (json, cmd, vrc) and load them
-            self.open_test_files(None, self.get_log_file_paths_from_json_file_name(file_selected))
-        elif response == Gtk.ResponseType.CANCEL:
-            pass
-        dialog.destroy()
-
+        return
+
+    def tooltip_treeview(self, widget, *args):
+        #print(widget)
+        #print(args)
+        #print(self.view.get_path_at_pos(args[0], args[1]))
+        pass
+        return True
     def get_log_file_paths_from_json_file_name(self, filename):
         from testlib import testing_logger
         paths = {}
@@ -491,6 +540,22 @@ class TestProgressView(Gtk.ApplicationWindow):
     def refresh_worker(self):
         GLib.timeout_add_seconds(self.refresh_rate, self.on_reload_all)
 
+    def on_remake_treeview(self, *args):
+        if self.sort_button.get_active():
+            self.progress_tree_store = Gtk.TreeStore(str, str, str, str, str, str, str, str, str, str, str, str, str)
+        else:
+            self.progress_tree_store = Gtk.TreeStore(str, str, str, str, str, str, str, str, str, str, str)
+
+        self.sorted_model = Gtk.TreeModelSort(model=self.progress_tree_store)
+        self.sorted_model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
+        self.view.set_model(self.sorted_model)
+        all_columns = self.view.get_columns()
+        for column in all_columns:
+            self.view.remove_column(column)
+        self.make_treeview()
+        self.on_reload_all()
+
+
     def on_reload_all(self, *args):
         if self.path_json:
             self.load_json(self.path_json)
@@ -517,7 +582,7 @@ class TestProgressView(Gtk.ApplicationWindow):
     # ------------------- model functions ----------------------
     @staticmethod
     def build_row_list(row=None, step_number=None, exec_date=None, entry_type=None, version=None, status=None, tcs=None,
-                       result=None, step_desc=None):
+                       result=None, step_desc=None, tooltip_text=None, exec_int=None, exec_desc=None, sort_button_active=False):
         """
         Builds or updates a row of the TreeStore. For a new row, the argument 'row' is expected to be None.
         :param Gtk.TreeModelRow row: a row of the tree model
@@ -546,6 +611,8 @@ class TestProgressView(Gtk.ApplicationWindow):
             else:
                 return 'failed'
 
+        sort_adjustment = 1 if sort_button_active else 0
+
         entry_background = None
         if exec_date is not None:
             exec_date = datetime.datetime.strftime(exec_date, testing_logger.date_time_format)
@@ -558,6 +625,12 @@ class TestProgressView(Gtk.ApplicationWindow):
 
         if row is not None:
             # update existing row
+            row[10] = 'Hello'
+            if sort_button_active:
+                if exec_int:
+                    row[12] = exec_int
+                if exec_desc:
+                    row[11] = exec_desc
             if step_number is not None:
                 row[0] = step_number
             if exec_date is not None:
@@ -577,19 +650,36 @@ class TestProgressView(Gtk.ApplicationWindow):
                 row[8] = step_desc
         else:
             # build a new row
-            row = [
-                step_number,
-                exec_date,
-                entry_type,
-                version,
-                status,
-                tcs,
-                result_value,
-                entry_background,
-                step_desc,
-                result_cell_color
-
-            ]
+            if sort_button_active:
+                row = [
+                    step_number,
+                    exec_date,
+                    entry_type,
+                    version,
+                    status,
+                    tcs,
+                    result_value,
+                    entry_background,
+                    step_desc,
+                    result_cell_color,
+                    tooltip_text,
+                    exec_desc,
+                    exec_int
+                ]
+            else:
+                row = [
+                    step_number,
+                    exec_date,
+                    entry_type,
+                    version,
+                    status,
+                    tcs,
+                    result_value,
+                    entry_background,
+                    step_desc,
+                    result_cell_color,
+                    tooltip_text
+                ]
         # set the background color of the rows
         if row[2] == 'command':
             entry_background = row_cmd_color
@@ -603,7 +693,6 @@ class TestProgressView(Gtk.ApplicationWindow):
         return row
 
     def add_detailed_row(self, inner_row_iter, tree_store):
-
         detailed_info=[]
         for count, item in enumerate(tree_store[inner_row_iter]):
             if count in [0,7,9]:  # Stepnumber, colour, colour
@@ -611,7 +700,10 @@ class TestProgressView(Gtk.ApplicationWindow):
             elif count == 1:
                 detailed_info.append('Description:')
             elif count == 2:
-                detailed_info.append('Code')
+                if self.test_model:
+                    detailed_info.append('{}'.format(self.test_model.description))
+                else:
+                    detailed_info.append('Json File has to be given')
             elif count == 4:
                 if tree_store[inner_row_iter][2] == 'command':
                     detailed_info.append('Command Code:')
@@ -623,11 +715,27 @@ class TestProgressView(Gtk.ApplicationWindow):
                 detailed_info.append('Code')
             elif count in [3,6,8]:
                 detailed_info.append('')
+            elif count == 10:
+                detailed_info.append('')
 
         new_row_iter = tree_store.append(inner_row_iter, detailed_info)
 
+    def set_test_title(self):
+        json_title = self.test_model.name if self.test_model else None
+        test_desc = self.test_model.description if self.test_model else None
+        cmd_title = self.text_path_cmd_btn.get_filename()
+        vrc_title = self.text_path_vrc_btn.get_filename()
+        if json_title:
+            self.test_title.set_markup('<b><big>{}</big></b>\t{}'.format(json_title.split('/')[-1], test_desc))
+        elif cmd_title:
+            self.test_title.set_markup('<b><big>{}</big></b>\t{}'.format(cmd_title.split('/')[-1], test_desc))
+        elif vrc_title:
+            self.test_title.set_markup('<b><big>{}</big></b>\t{}'.format(vrc_title.split('/')[-1], test_desc))
+        else:
+            self.test_title.set_text('')
 
     def load_json(self, filepath):
+
         if not os.path.isfile(filepath):
             message = 'load_file: no file found for the path {}'.format(filepath)
             logger.warning(message)
@@ -641,9 +749,11 @@ class TestProgressView(Gtk.ApplicationWindow):
             if data_from_file is not None:
                 self.test_model = data_model.TestSpecification()
                 self.test_model.decode_from_json(json_data=data_from_file)
-                self.load_model_into_tree_store(self.progress_tree_store, self.test_model)
+                if not self.sort_button.get_active():  # Only add json steps, if sorted by steps
+                    self.load_model_into_tree_store(self.progress_tree_store, self.test_model)
             else:
                 logger.warning('load_file: could not read from the JSON test spec')
+            self.set_test_title()
 
     def load_cmd(self, filepath):
         if not os.path.isfile(filepath):
@@ -654,6 +764,7 @@ class TestProgressView(Gtk.ApplicationWindow):
             # analyse the command log
             self.cmd_steps = analyse_command_log.get_steps_and_commands(filepath)
             self.load_cmd_into_tree_store(self.progress_tree_store, self.cmd_steps)
+            self.set_test_title()
 
     def load_vrc(self, filepath):
         if not os.path.isfile(filepath):
@@ -664,6 +775,7 @@ class TestProgressView(Gtk.ApplicationWindow):
             # analyse the verification log
             self.vrc_steps = analyse_verification_log.get_verification_steps(filepath)
             self.load_vrc_into_tree_store(self.progress_tree_store, self.vrc_steps)
+            self.set_test_title()
 
     def load_model_into_tree_store(self, tree_store, test_model):
 
@@ -684,7 +796,8 @@ class TestProgressView(Gtk.ApplicationWindow):
                 step_desc = 'Step ' + str(step_number[:-2])
 
                 new_drawer_row = self.build_row_list(step_number=str(step_number),
-                                                     step_desc=step_desc)
+                                                     step_desc=step_desc,
+                                                     sort_button_active=self.sort_button.get_active())
 
                 tree_store.append(None, new_drawer_row)
                 tree_store_steps.append(step_number)
@@ -728,64 +841,164 @@ class TestProgressView(Gtk.ApplicationWindow):
         """
         # collect the information if the drawer rows are expanded, in order to restore this states
         self.gather_expanded_states(tree_store)
+        if self.sort_button.get_active():
+            # check which executions are already in the tree store
+            tree_store_exec = {}
+            for row in tree_store:
+                if row[12]:
+                    tree_store_exec[row[12]] = []
+            # check which steps are in each execution
+            for row in tree_store:
+                if row[12]:
+                    for item in row.iterchildren():
+                        if item[0]:
+                            tree_store_exec[row[12]].append(item[0])
+
+            all_exec_numbers = {}
+            # get all executions
+            for item in cmd_steps:
+                all_exec_numbers[str(item['run_id'])] = []
+            # get all steps for every execution
+            for item in cmd_steps:
+                all_exec_numbers[str(item['run_id'])].append(item['step'])
+
+            # make execution drawers
+            for exec_num in all_exec_numbers.keys():
+                if not exec_num in tree_store_exec.keys():
+                    #exec_desc = 'Run ' + str(exec_num)
+                    exec_desc = str(exec_num)
+                    new_drawer_row = self.build_row_list(exec_int=str(exec_num),
+                                                         exec_desc=exec_desc,
+                                                         sort_button_active=self.sort_button.get_active())
+                    tree_store.append(None, new_drawer_row)
+                    tree_store_exec[exec_num] = []
+
+            # make step drawers for every execution drawer
+            for row in tree_store:
+                if row[12]:
+                    exec_num = row[12]
+                    for step_num in all_exec_numbers[exec_num]:
+                        if not step_num in tree_store_exec[exec_num]:
+                            step_desc = 'Step ' + str(step_num[:-2])
+                            new_step_row = self.build_row_list(step_number=str(step_num),
+                                                                step_desc=step_desc,
+                                                                sort_button_active=self.sort_button.get_active())
+                            new_row_iter = tree_store.append(row.iter, new_step_row)
+                            tree_store_exec[exec_num].append(step_num)
+
+            # clear all command rows, before adding
+            for row in tree_store:
+                if row[12]:
+                    for step_row in row.iterchildren():
+                        for item in step_row.iterchildren():
+                            if item[2] == 'command':
+                                tree_store.remove(item.iter)
+
+            # add rows for command
+            for row in tree_store:
+                if row[12]:
+                    exec_num = row[12]
+                    for step_row in row.iterchildren():
+                        if step_row[0]:
+                            step_num = step_row[0]
+                            for item in cmd_steps:
+                                if item['run_id'] == exec_num and item['step'] == step_num:
+                                    new_row_list = self.build_row_list(entry_type='command',
+                                                                       version=item['version'],
+                                                                       exec_date=item['exec_date'],
+                                                                       sort_button_active=self.sort_button.get_active(),
+                                                                       tooltip_text=item['descr'])
+                                    new_row_iter = tree_store.append(step_row.iter, new_row_list)
+                                    new_row = tree_store[new_row_iter]
+                                    # add the information if the step was executed or had an exception
+                                    if 'exception' in item:
+                                        self.build_row_list(row=new_row,
+                                                            status='EXCEPTION',
+                                                            result=False,
+                                                            sort_button_active=self.sort_button.get_active())
+                                    else:
+                                        if 'end_timestamp' in item:
+                                            self.build_row_list(row=new_row,
+                                                                status='executed',
+                                                                result=True,
+                                                                sort_button_active=self.sort_button.get_active())
+                                    # add the TC's
+                                    tcs_str = ''
+                                    for telecommand in item['tcs']:
+                                        if tcs_str != '':
+                                            tcs_str += ', '
+                                        tcs_str += telecommand.tc_kind()
+                                    self.build_row_list(row=new_row,
+                                                        tcs=tcs_str,
+                                                        sort_button_active=self.sort_button.get_active())
+
+                                    #self.add_detailed_row(new_row_iter, tree_store)
 
-        # check which step numbers are already in the tree_store
-        tree_store_steps = []
-        for row in tree_store:
-            step_number_tree_store = row[0:1][0]
-            tree_store_steps.append(step_number_tree_store)
-        # add drawer for each step which is not in the tree_store already
-        for item in cmd_steps:
-            step_number = item['step']
-            if step_number not in tree_store_steps:
-                step_desc = 'Step ' + str(step_number)
-                new_drawer_row = self.build_row_list(step_number=str(step_number),
-                                                     step_desc=step_desc)
-                tree_store.append(None, new_drawer_row)
-                tree_store_steps.append(step_number)
-        # clear all command rows, before adding
-        for row in tree_store:
-            for item in row.iterchildren():
-                if item[2] == 'command':
-                    tree_store.remove(item.iter)
-        # add rows for command
-        for row in tree_store:
-            step_number_tree_store = row[0:1][0]
+        else:
+            # check which step numbers are already in the tree_store
+            tree_store_steps = []
+            for row in tree_store:
+                step_number_tree_store = row[0:1][0]
+                tree_store_steps.append(step_number_tree_store)
+            # add drawer for each step which is not in the tree_store already
             for item in cmd_steps:
-                step_number_cmd = item['step']
-                if step_number_tree_store == step_number_cmd:
-                    # already_exists = False
-                    # for i in row.iterchildren():
-                    #     if datetime.datetime.strftime(item['exec_date'], testing_logger.date_time_format) == i[1]:
-                    #         already_exists = True
-                    # # add a new row
-                    # if not already_exists:
-                    new_row_list = self.build_row_list(entry_type='command',
-                                                       version=item['version'],
-                                                       exec_date=item['exec_date'])
-                    new_row_iter = tree_store.append(row.iter, new_row_list)
-                    new_row = tree_store[new_row_iter]
-
-                    # add the information if the step was executed or had an exception
-                    if 'exception' in item:
-                        self.build_row_list(row=new_row,
-                                            status='EXCEPTION',
-                                            result=False)
-                    else:
-                        if 'end_timestamp' in item:
+                step_number = item['step']
+                if step_number not in tree_store_steps:
+                    step_desc = 'Step ' + str(step_number[:-2])
+                    new_drawer_row = self.build_row_list(step_number=str(step_number),
+                                                         step_desc=step_desc,
+                                                         sort_button_active=self.sort_button.get_active())
+                    tree_store.append(None, new_drawer_row)
+                    tree_store_steps.append(step_number)
+            # clear all command rows, before adding
+            for row in tree_store:
+                for item in row.iterchildren():
+                    if item[2] == 'command':
+                        tree_store.remove(item.iter)
+            # add rows for command
+            for row in tree_store:
+                #self.view.set_tooltip_column()
+                step_number_tree_store = row[0:1][0]
+                for item in cmd_steps:
+                    step_number_cmd = item['step']
+                    if step_number_tree_store == step_number_cmd:
+                        # already_exists = False
+                        # for i in row.iterchildren():
+                        #     if datetime.datetime.strftime(item['exec_date'], testing_logger.date_time_format) == i[1]:
+                        #         already_exists = True
+                        # # add a new row
+                        # if not already_exists:
+                        new_row_list = self.build_row_list(entry_type='command',
+                                                           version=item['version'],
+                                                           exec_date=item['exec_date'],
+                                                           sort_button_active=self.sort_button.get_active(),
+                                                           tooltip_text=item['descr'])
+                        new_row_iter = tree_store.append(row.iter, new_row_list)
+                        new_row = tree_store[new_row_iter]
+
+                        # add the information if the step was executed or had an exception
+                        if 'exception' in item:
                             self.build_row_list(row=new_row,
-                                                status='executed',
-                                                result=True)
-                    # add the TC's
-                    tcs_str = ''
-                    for telecommand in item['tcs']:
-                        if tcs_str != '':
-                            tcs_str += ', '
-                        tcs_str += telecommand.tc_kind()
-                    self.build_row_list(row=new_row,
-                                        tcs=tcs_str)
-
-                    self.add_detailed_row(new_row_iter, tree_store)
+                                                status='EXCEPTION',
+                                                result=False,
+                                                sort_button_active=self.sort_button.get_active())
+                        else:
+                            if 'end_timestamp' in item:
+                                self.build_row_list(row=new_row,
+                                                    status='executed',
+                                                    result=True,
+                                                    sort_button_active=self.sort_button.get_active())
+                        # add the TC's
+                        tcs_str = ''
+                        for telecommand in item['tcs']:
+                            if tcs_str != '':
+                                tcs_str += ', '
+                            tcs_str += telecommand.tc_kind()
+                        self.build_row_list(row=new_row,
+                                            tcs=tcs_str,
+                                            sort_button_active=self.sort_button.get_active())
+
+                        self.add_detailed_row(new_row_iter, tree_store)
 
         self.restore_expanded_states(tree_store)
 
@@ -793,59 +1006,157 @@ class TestProgressView(Gtk.ApplicationWindow):
 
         # collect the information if the drawer rows are expanded, in order to restore this states
         self.gather_expanded_states(tree_store)
-        # check which step numbers are already in the tree_store
-        tree_store_steps = []
-        for row in tree_store:
-            step_number_tree_store = row[0:1][0]
-            tree_store_steps.append(step_number_tree_store)
-        # add drawer for each step which is not in the tree_store already
-        for item in vrc_steps:
-            step_number = item['step']
-            if step_number not in tree_store_steps:
-                step_desc = 'Step ' + str(step_number)
-                new_drawer_row = self.build_row_list(step_number=str(step_number),
-                                                     step_desc=step_desc)
-                tree_store.append(None, new_drawer_row)
-                tree_store_steps.append(step_number)
-        # clear all verification rows, before adding
-        for row in tree_store:
-            for item in row.iterchildren():
-                if item[2] == 'verification':
-                    tree_store.remove(item.iter)
-        # add row for verification
-        for row in tree_store:
-            step_number_tree_store = row[0:1][0]
+
+        if self.sort_button.get_active():
+            # check which executions are already in the tree store
+            tree_store_exec = {}
+            for row in tree_store:
+                if row[12]:
+                    tree_store_exec[row[12]] = []
+            # check which steps are in each execution
+            for row in tree_store:
+                if row[12]:
+                    for item in row.iterchildren():
+                        if item[0]:
+                            tree_store_exec[row[12]].append(item[0])
+
+            all_exec_numbers = {}
+            # get all executions
             for item in vrc_steps:
-                step_number_vrc = item['step']
-                if step_number_tree_store == step_number_vrc:
-                    # already_exists = False
-                    # for i in row.iterchildren():
-                    #     if datetime.datetime.strftime(item['exec_date'], testing_logger.date_time_format) == i[1]:
-                    #         already_exists = True
-                    # # add a new row
-                    # if not already_exists:
-                    new_row_list = self.build_row_list(entry_type='verification',
-                                                       version=item['version'],
-                                                       exec_date=item['exec_date'])
-                    new_row_iter = tree_store.append(row.iter, new_row_list)
-                    new_row = tree_store[new_row_iter]
-                    # add the information if the step was executed or had an exception
-                    if 'exception' in item:
-                        self.build_row_list(row=new_row,
-                                            status='EXCEPTION')
-                    else:
-                        if 'end_timestamp' in item:
-                            self.build_row_list(row=new_row,
-                                                status='executed')
-                    if 'result' in item:
-                        if item['result'] is True:
+                all_exec_numbers[str(item['run_id'])] = []
+            # get all steps for every execution
+            for item in vrc_steps:
+                all_exec_numbers[str(item['run_id'])].append(item['step'])
+
+            # make execution drawers
+            for exec_num in all_exec_numbers.keys():
+                if not exec_num in tree_store_exec.keys():
+                    #exec_desc = 'Run ' + str(exec_num)
+                    exec_desc = str(exec_num)
+                    new_drawer_row = self.build_row_list(exec_int=str(exec_num),
+                                                         exec_desc=exec_desc,
+                                                         sort_button_active=self.sort_button.get_active())
+                    tree_store.append(None, new_drawer_row)
+                    tree_store_exec[exec_num] = []
+
+            # make step drawers for every execution drawer
+            for row in tree_store:
+                if row[12]:
+                    exec_num = row[12]
+                    for step_num in all_exec_numbers[exec_num]:
+                        if not step_num in tree_store_exec[exec_num]:
+                            step_desc = 'Step ' + str(step_num[:-2])
+                            new_step_row = self.build_row_list(step_number=str(step_num),
+                                                                step_desc=step_desc,
+                                                                sort_button_active=self.sort_button.get_active())
+                            new_row_iter = tree_store.append(row.iter, new_step_row)
+                            tree_store_exec[exec_num].append(step_num)
+
+            # clear all verification rows, before adding
+            for row in tree_store:
+                if row[12]:
+                    for step_row in row.iterchildren():
+                        for item in step_row.iterchildren():
+                            if item[2] == 'verification':
+                                tree_store.remove(item.iter)
+
+            # add rows for verification
+            for row in tree_store:
+                if row[12]:
+                    exec_num = row[12]
+                    for step_row in row.iterchildren():
+                        if step_row[0]:
+                            step_num = step_row[0]
+                            for item in vrc_steps:
+                                if item['run_id'] == exec_num and item['step'] == step_num:
+                                    new_row_list = self.build_row_list(entry_type='verification',
+                                                                       version=item['version'],
+                                                                       exec_date=item['exec_date'],
+                                                                       sort_button_active=self.sort_button.get_active(),
+                                                                       tooltip_text=item['descr'])
+                                    new_row_iter = tree_store.append(step_row.iter, new_row_list)
+                                    new_row = tree_store[new_row_iter]
+                                    # add the information if the step was executed or had an exception
+                                    if 'exception' in item:
+                                        self.build_row_list(row=new_row,
+                                                            status='EXCEPTION',
+                                                            sort_button_active=self.sort_button.get_active())
+                                    else:
+                                        if 'end_timestamp' in item:
+                                            self.build_row_list(row=new_row,
+                                                                status='executed',
+                                                                sort_button_active=self.sort_button.get_active())
+                                    if 'result' in item:
+                                        if item['result'] is True:
+                                            self.build_row_list(row=new_row,
+                                                                result=True,
+                                                                sort_button_active=self.sort_button.get_active())
+                                        else:
+                                            self.build_row_list(row=new_row,
+                                                                result=False,
+                                                                sort_button_active=self.sort_button.get_active())
+
+        else:
+            # check which step numbers are already in the tree_store
+            tree_store_steps = []
+            for row in tree_store:
+                step_number_tree_store = row[0:1][0]
+                tree_store_steps.append(step_number_tree_store)
+            # add drawer for each step which is not in the tree_store already
+            for item in vrc_steps:
+                step_number = item['step']
+                if step_number not in tree_store_steps:
+                    step_desc = 'Step ' + str(step_number[:-2])
+                    new_drawer_row = self.build_row_list(step_number=str(step_number),
+                                                         step_desc=step_desc,
+                                                         sort_button_active=self.sort_button.get_active())
+                    tree_store.append(None, new_drawer_row)
+                    tree_store_steps.append(step_number)
+            # clear all verification rows, before adding
+            for row in tree_store:
+                for item in row.iterchildren():
+                    if item[2] == 'verification':
+                        tree_store.remove(item.iter)
+            # add row for verification
+            for row in tree_store:
+                step_number_tree_store = row[0:1][0]
+                for item in vrc_steps:
+                    step_number_vrc = item['step']
+                    if step_number_tree_store == step_number_vrc:
+                        # already_exists = False
+                        # for i in row.iterchildren():
+                        #     if datetime.datetime.strftime(item['exec_date'], testing_logger.date_time_format) == i[1]:
+                        #         already_exists = True
+                        # # add a new row
+                        # if not already_exists:
+                        new_row_list = self.build_row_list(entry_type='verification',
+                                                           version=item['version'],
+                                                           exec_date=item['exec_date'],
+                                                           sort_button_active=self.sort_button.get_active(),
+                                                           tooltip_text=item['descr'])
+                        new_row_iter = tree_store.append(row.iter, new_row_list)
+                        new_row = tree_store[new_row_iter]
+                        # add the information if the step was executed or had an exception
+                        if 'exception' in item:
                             self.build_row_list(row=new_row,
-                                                result=True)
+                                                status='EXCEPTION',
+                                                sort_button_active=self.sort_button.get_active())
                         else:
-                            self.build_row_list(row=new_row,
-                                                result=False)
-                    self.add_detailed_row(new_row_iter, tree_store)
-        self.restore_expanded_states(tree_store)
+                            if 'end_timestamp' in item:
+                                self.build_row_list(row=new_row,
+                                                    status='executed',
+                                                    sort_button_active=self.sort_button.get_active())
+                        if 'result' in item:
+                            if item['result'] is True:
+                                self.build_row_list(row=new_row,
+                                                    result=True,
+                                                    sort_button_active=self.sort_button.get_active())
+                            else:
+                                self.build_row_list(row=new_row,
+                                                    result=False,
+                                                    sort_button_active=self.sort_button.get_active())
+                        self.add_detailed_row(new_row_iter, tree_store)
+            self.restore_expanded_states(tree_store)
 
 
 def run():
diff --git a/Tst/testing_library/testlib/analyse_command_log.py b/Tst/testing_library/testlib/analyse_command_log.py
index ddf6c26..52d5252 100644
--- a/Tst/testing_library/testlib/analyse_command_log.py
+++ b/Tst/testing_library/testlib/analyse_command_log.py
@@ -80,18 +80,29 @@ def get_steps_and_commands(filename):
     :rtype: list of dict
     """
     steps = []
+    steps_start = []
+    steps_end = []
 
-    def new_step_template():
+    def new_step_template_start():
         return {'step': None, 'version': '', 'tcs': [], 'date': ''}
 
-    new_step = new_step_template()
+    def new_step_template_end():
+        return {'step': None, 'timestamp': '', 'step_id': ''}
+
+    new_step = new_step_template_start()
+    run_count = 1
 
     with open(filename, 'r') as fileobject:
         for line in fileobject:
+            #if report.key_word_found(line, report.cmd_test_start_keyword):
+                # Get general infos about the whole test, append to every step of this run
+                #general_step_info = report.parse_step_from_json_string(line, report.cmd_test_start_keyword)
+                #general_step_info['run_count'] = str(run_count)
+                #run_count += 1
             if report.key_word_found(line, report.cmd_step_keyword):
                 if new_step['step'] is not None:
-                    steps.append(new_step)
-                new_step = new_step_template()
+                    steps_start.append(new_step)
+                new_step = new_step_template_start()
                 # get date of the step execution
                 new_step['exec_date'] = testing_logger.extract_date(line)
                 # get the information about the step
@@ -100,6 +111,13 @@ def get_steps_and_commands(filename):
                     new_step['step'] = step_start_info['step']
                     new_step['start_timestamp'] = step_start_info['timestamp']
                     new_step['version'] = step_start_info['version']
+                    new_step['descr'] = step_start_info['descr']
+                    new_step['run_id'] = step_start_info['run_id']
+                    new_step['step_id'] = step_start_info['step_id']
+                #try:
+                #    new_step['general_run_info'] = general_step_info
+                #except:
+                #    new_step['general_run_info'] = None
             if tcid.key_word_found(line):
                 new_tc_id = tcid.TcId()
                 new_tc_id.parse_tc_id_from_json_string(line=line)
@@ -109,16 +127,29 @@ def get_steps_and_commands(filename):
             if report.key_word_found(line, report.cmd_step_keyword_done):
                 step_end_info = report.parse_step_from_json_string(line, report.cmd_step_keyword_done)
                 if step_end_info is not None:
-                    if new_step['step'] == step_end_info['step']:
-                        new_step['end_timestamp'] = step_end_info['timestamp']
-                    else:
-                        print('get_steps_and_commands: the step number in the step-end string is different than the'
-                              'step number of the last step-start string.')
+                    new_step_end = new_step_template_end()
+                    new_step_end['step'] = step_end_info['step']
+                    new_step_end['timestamp'] = step_end_info['timestamp']
+                    new_step_end['step_id'] = step_end_info['step_id']
+                    steps_end.append(new_step_end)
+                    #if new_step['step'] == step_end_info['step']:
+                    #    new_step['end_timestamp'] = step_end_info['timestamp']
+                    #else:
+                    #    print('get_steps_and_commands: the step number in the step-end string is different than the'
+                    #          'step number of the last step-start string.')
         if new_step['step'] is not None:
-            steps.append(new_step)
+            steps_start.append(new_step)
         fileobject.close()
 
-    return steps
+    if len(steps_end) > len(steps_start):
+        print('More steps ended than started, something went wrong')
+
+    for start_info in steps_start:
+        for end_info in steps_end:
+            if start_info['step_id'] == start_info['step_id']:
+                start_info['end_timestamp'] = end_info['timestamp']
+
+    return steps_start
 
 
 if __name__ == '__main__':
diff --git a/Tst/testing_library/testlib/analyse_verification_log.py b/Tst/testing_library/testlib/analyse_verification_log.py
index 2c3e385..ebc9549 100644
--- a/Tst/testing_library/testlib/analyse_verification_log.py
+++ b/Tst/testing_library/testlib/analyse_verification_log.py
@@ -15,16 +15,23 @@ def get_verification_steps(filename):
              end timestamp (CUC) of the verification and the result of the verification for this step
     :rtype: list of dict
     """
+
     vrc_start = []
     vrc_end = []
+    id_codes = {}
     with open(filename, 'r') as fileobject:
         for line in fileobject:
             if report.key_word_found(line, report.vrc_step_keyword):
                 new_dict = report.parse_step_from_json_string(line, report.vrc_step_keyword)
                 new_dict['exec_date'] = testing_logger.extract_date(line)
+                #new_dict['id_code'] = line.split('\t')[2]
+                #id_codes[line.split('\t')[2]] = int(line.split('\t')[2])
                 vrc_start.append(new_dict)
             if report.key_word_found(line, report.vrc_step_keyword_done):
-                vrc_end.append(report.parse_step_from_json_string(line, report.vrc_step_keyword_done))
+                new_dict = report.parse_step_from_json_string(line, report.vrc_step_keyword_done)
+                #new_dict['id_code'] = line.split('\t')[2]
+                #id_codes[line.split('\t')[2]] = int(line.split('\t')[2])
+                vrc_end.append(new_dict)
         fileobject.close()
 
     # print('\nfound {} steps:'.format(len(vrc_start)))
@@ -33,6 +40,9 @@ def get_verification_steps(filename):
     #
     # for item in vrc_start:
     #     print('Verification end for Step {} @ {}'.format(item['step'], item['timestamp']))
+    #exec_count = 1
+    if len(vrc_end) > len(vrc_start):
+        print('There are more steps finished than started, something went wrong')
 
     vrc_steps = []
     for item in vrc_start:
@@ -41,8 +51,12 @@ def get_verification_steps(filename):
         new_vrc_step['start_timestamp'] = item['timestamp']
         new_vrc_step['exec_date'] = item['exec_date']
         new_vrc_step['version'] = item['version']
+        new_vrc_step['descr'] = item['descr']
+        new_vrc_step['run_id'] = item['run_id']
+        new_vrc_step['step_id'] = item['step_id']
         for element in vrc_end:
-            if element['step'] == item['step']:
+            if element['step_id'] == item['step_id']:
+                #new_vrc_step['run_count'] = id_run_count[element['id_code']]
                 new_vrc_step['end_timestamp'] = element['timestamp']
                 new_vrc_step['result'] = element['result']
         vrc_steps.append(new_vrc_step)
diff --git a/Tst/testing_library/testlib/report.py b/Tst/testing_library/testlib/report.py
index b08ddb0..2ec9655 100644
--- a/Tst/testing_library/testlib/report.py
+++ b/Tst/testing_library/testlib/report.py
@@ -3,7 +3,7 @@
 Report - writing log entries
 ============================
 """
-import datetime
+from datetime import datetime
 import logging
 import collections
 import json
@@ -13,15 +13,23 @@ import confignator
 sys.path.append(confignator.get_option('paths', 'ccs'))
 import ccs_function_lib as cfl
 
-# create a logger
+# create logger
 logger = logging.getLogger(__name__)
+#now = datetime.now()  # current date and time
+#code = now.strftime("%Y%m%d%H%M%S")
+#extra = {'id_code': code}
+#logger = logging.LoggerAdapter(logger, extra)
+
+import datetime
 
+cmd_test_start_keyword = '#START TEST'
 cmd_step_keyword = '#COMMAND STEP'
 cmd_step_exception_keyword = 'EXCEPTION IN STEP'
 cmd_step_keyword_done = '#STEP DONE'  # ATTENTION! The _done keyword must not contain the start keyword
+vrc_test_start_keyword = '#START VERIFICATION'
 vrc_step_keyword = '#VERIFICATION FOR STEP'
 vrc_step_exception_keyword = 'EXCEPTION IN STEP'
-vrc_step_keyword_done = '#VERIFICATION DONE'  # ATTENTION! The _done keyword must not contain the start keyword
+vrc_step_keyword_done = '#VERIFICATION STEP DONE'  # ATTENTION! The _done keyword must not contain the start keyword
 
 
 def key_word_found(line, key_word):
@@ -40,7 +48,7 @@ def key_word_found(line, key_word):
     return found
 
 
-def encode_to_json_string(step_number, timestamp, step_version=None, step_result=None):
+def encode_to_json_string(step_number, timestamp, step_version=None, step_result=None, descr=None, run_id=None, step_id=None):
     """
     Make a JSON string out of the step number and timestamp
     :param step_number: number of the step
@@ -54,9 +62,21 @@ def encode_to_json_string(step_number, timestamp, step_version=None, step_result
         od['version'] = step_version
     if step_result is not None:
         od['result'] = step_result
+    if descr is not None:
+        od['descr'] = descr
+    if run_id is not None:
+        od['run_id'] = run_id
+    if step_id is not None:
+        od['step_id'] = step_id
     json_string = json.dumps(od)
     return json_string
 
+def make_json_string(*args, **kwargs):
+    od = {}
+    for key, value in kwargs.items():
+        od[str(key)] = value
+    json_string = json.dumps(od)
+    return json_string
 
 def parse_step_from_json_string(line, key_word):
     """
@@ -85,7 +105,7 @@ def parse_step_from_json_string(line, key_word):
             logger.error('parse_tc_id_from_json_string: parsing of the TC JSON string failed!')
 
 
-def command_step_begin(step_param, script_version, pool_name, step_start_cuc):
+def command_step_begin(step_param, script_version, pool_name, step_start_cuc, run_id, step_id):
     """
     Builds a string and writes it into the logging file. A keyword is set to enable a machine read out of the log file.
     All information of the step is written in a JSON string.
@@ -95,48 +115,57 @@ def command_step_begin(step_param, script_version, pool_name, step_start_cuc):
     :param step_start_cuc:
     :return:
     """
+    #print(step_param)
     logger.info('{} {} {}'.format(cmd_step_keyword,
                                   step_param['step_no'],
                                   encode_to_json_string(step_number=step_param['step_no'],
                                                         timestamp=step_start_cuc,
-                                                        step_version=script_version)))
-    logger.info(step_param['msg'])
+                                                        step_version=script_version,
+                                                        run_id=run_id,
+                                                        step_id=step_id,
+                                                        descr=step_param['descr'])))
+    logger.info(step_param['descr'])
     if 'comment' in step_param:
         if len(step_param['comment']) > 0:
             logger.info('Comment: {}'.format(step_param['comment']))
 
 
-def command_step_exception(step_param):
-    logger.warning('{} {}'.format(cmd_step_exception_keyword,
-                                  step_param['step_no']))
+def command_step_exception(step_param, step_id=None):
+    logger.warning('{} {} {}'.format(cmd_step_exception_keyword,
+                                  step_param['step_no'], make_json_string(step_id=step_id)))
 
 
-def command_step_end(step_param, step_end_cuc):
-    logger.info('{} {}\n'.format(cmd_step_keyword_done, encode_to_json_string(step_param['step_no'], step_end_cuc)))
+def command_step_end(step_param, step_end_cuc, step_id):
+    logger.info('{} {}\n'.format(cmd_step_keyword_done, encode_to_json_string(step_param['step_no'], step_end_cuc, step_id=step_id)))
 
 
-def verification_step_begin(step_param, script_version, pool_name, step_start_cuc):
+def verification_step_begin(step_param, script_version, pool_name, step_start_cuc, run_id, step_id):
+
     logger.info('{} {} {}'.format(vrc_step_keyword,
                                   step_param['step_no'],
                                   encode_to_json_string(step_number=step_param['step_no'],
                                                         timestamp=step_start_cuc,
-                                                        step_version=script_version)))
-    logger.info(step_param['msg'])
+                                                        step_version=script_version,
+                                                        run_id=run_id,
+                                                        step_id=step_id,
+                                                        descr=step_param['descr'])))
+    logger.info(step_param['descr'])
     if 'comment' in step_param:
         if len(step_param['comment']) > 0:
             logger.info('Comment: {}'.format(step_param['comment']))
 
 
-def verification_step_exception(step_param):
-    logger.warning('{} {}'.format(vrc_step_exception_keyword,
-                                  step_param['step_no']))
+def verification_step_exception(step_param, step_id=None):
+    logger.warning('{} {} {}'.format(vrc_step_exception_keyword,
+                                  step_param['step_no'], make_json_string(step_id=step_id)))
 
 
-def verification_step_end(step_param, step_result, step_end_cuc):
+def verification_step_end(step_param, step_result, step_end_cuc, step_id):
     logger.info('{} {} {}'.format(vrc_step_keyword_done,
                                   step_param['step_no'],
                                   encode_to_json_string(step_number=step_param['step_no'],
                                                         timestamp=step_end_cuc,
+                                                        step_id=step_id,
                                                         step_result=step_result)))
     if step_result is True:
         logger.info('Verification for step {} was passed successful. +++ OK +++\n'.format(step_param['step_no']))
@@ -154,12 +183,12 @@ class StepSummary:
     def had_exception(self):
         self.has_exception = True
 # --------------------------------------------
-
+# Command log output
 
 def write_log_step_header(step_param, pool_name, step_start_cuc):
     logger.info('STEP {} (starting from {})'
              .format(step_param['step_no'], step_start_cuc))
-    logger.info(step_param['msg'])
+    logger.info(step_param['descr'])
     if 'comment' in step_param:
         if len(step_param['comment']) > 0:
             logger.info('Comment: {}'.format(step_param['comment']))
@@ -171,13 +200,20 @@ def write_log_step_footer(step_param, step_result):
     else:
         logger.warning('Step {} failed.'.format(step_param['step_no']))
 
-
-def write_log_test_header(test, pool_name):
+def write_log_test_header(test, pool_name=None):
     logger.info('-------------------------------------------------------------------------------')
-    logger.info('Running test {}\n\t\t\t\t\tversion {}\n\t\t\t\t\tpoolname = {}\n\t\t\t\t\tCUC-timestamp of test '
-             'start = {}\n\t\t\t\t\tlocal time = {}'
-             .format(test.id, test.version, pool_name, cfl.get_last_pckt_time(pool_name=pool_name, string=False),
-                     datetime.datetime.now().isoformat()))
+    #logger.info('#Start Test: {}\n\t\t\t\t\tversion {}\n\t\t\t\t\tpoolname = {}\n\t\t\t\t\tCUC-timestamp of test '
+    #         'start = {}\n\t\t\t\t\tlocal time = {}'
+    #         .format(test.id, test.version, pool_name, cfl.get_last_pckt_time(pool_name=pool_name, string=False),
+    #                 datetime.datetime.now().isoformat()))
+    date_time = datetime.datetime.now().isoformat()
+    logger.info('{} {}'.format(cmd_test_start_keyword, make_json_string(test_name=test.id,
+    #                                                    version=test.version,
+                                                        pool_name=pool_name,
+                                                        cuc_start_time=cfl.get_last_pckt_time(pool_name=pool_name, string=False),
+                                                        local_start_time=date_time,
+                                                        run_id=test.run_id)))
+
     logger.info('#Description: {} \n'.format(test.description))
     if test.comment:
         logger.info('Comment: {}'.format(test.comment))
@@ -270,3 +306,4 @@ def write_postcondition_outcome(result):
         logger.info('Postconditions are fulfilled.\n')
     else:
         logger.warning('Postconditions are NOT fulfilled.\n')
+
diff --git a/Tst/testing_library/testlib/testing_logger.py b/Tst/testing_library/testlib/testing_logger.py
index 242c8d1..7c9fafb 100644
--- a/Tst/testing_library/testlib/testing_logger.py
+++ b/Tst/testing_library/testlib/testing_logger.py
@@ -7,6 +7,7 @@ import logging.config
 import os
 import datetime
 import confignator
+#from datetime import datetime
 
 from . import tools
 
@@ -73,7 +74,12 @@ def extract_date(line):
 
 
 def my_formatter():
-    return logging.Formatter(fmt='%(levelname)s\t%(asctime)s\tlogger: %(name)s:\t%(message)s')
+    #return logging.Formatter(fmt='%(levelname)s\t%(asctime)s\t%(id_code)s\t%(name)s:\t%(message)s')
+    return logging.Formatter(fmt='%(levelname)s\t%(asctime)s\t%(name)s:\t%(message)s')
+
+#def my_formatter_vrc():
+#    return logging.Formatter(fmt='%(levelname)s\t%(asctime)s\t%(id_code)s\t%(name)s:\t%(message)s')
+    #return logging.Formatter(fmt='%(levelname)s\t%(asctime)s\t%(name)s:\t%(message)s')
 
 
 def cmd_log_handler(file_name):
diff --git a/Tst/tst/generator_templates/co_class.py b/Tst/tst/generator_templates/co_class.py
index cfbf5c5..a16b433 100644
--- a/Tst/tst/generator_templates/co_class.py
+++ b/Tst/tst/generator_templates/co_class.py
@@ -15,6 +15,7 @@ class ${testSpecClassName}:
         self.integrity = True
         self.exceptions = []
         self.do_verification = do_verification
+        self.run_id = False
 
         # some tests are depended on other tests, thus information is stored on class level
         # insert class variables here
diff --git a/Tst/tst/generator_templates/co_footer.py b/Tst/tst/generator_templates/co_footer.py
index ceecf91..9372702 100644
--- a/Tst/tst/generator_templates/co_footer.py
+++ b/Tst/tst/generator_templates/co_footer.py
@@ -15,6 +15,8 @@
         """
         testing_logger.cmd_log_handler(__name__)
 
+        self.run_id = False
+        self.check_run_and_step_id(pool_name=pool_name)
         # log the header of this test
         report.write_log_test_header(test=self, pool_name=pool_name)
 
@@ -56,6 +58,8 @@
         # save the packet pool
         self.save_pool_in_file(pool_name=pool_name, save_pool=save_pool)
 
+        self.run_id = False
+
         # log the summary of this test
         self.successful_steps = report.write_log_test_footer(test=self)
 
@@ -70,3 +74,9 @@
         if save_pool is True:
             pool_file = tools.get_path_for_testing_logs() + self.id + '.tmpool'
             cfl.savepool(filename=pool_file, pool_name=pool_name)
+
+    def check_run_and_step_id(self, pool_name=None):
+        now = datetime.now()  # current date and time
+        if not self.run_id and pool_name:
+            self.run_id = now.strftime("%Y%m%d%H%M%S")
+        return now.strftime("%Y%m%d%H%M%S%f")
\ No newline at end of file
diff --git a/Tst/tst/generator_templates/co_header.py b/Tst/tst/generator_templates/co_header.py
index 2f220d2..b61b513 100644
--- a/Tst/tst/generator_templates/co_header.py
+++ b/Tst/tst/generator_templates/co_header.py
@@ -5,6 +5,7 @@ import os
 import time
 import importlib
 import threading
+from datetime import datetime
 import confignator
 ccs_path = confignator.get_option('paths', 'ccs')
 sys.path.append(ccs_path)
diff --git a/Tst/tst/generator_templates/co_post_condition.py b/Tst/tst/generator_templates/co_post_condition.py
index eaf7c8a..b2dabb5 100644
--- a/Tst/tst/generator_templates/co_post_condition.py
+++ b/Tst/tst/generator_templates/co_post_condition.py
@@ -1,5 +1,5 @@
     # VERIFY EVERY STEP ------------------------------------------------------------------------------------------------
-    def step_verification(self, pool_name, step_start_cuc, param, summary, tc_id, ver_file, ver_class, ver_func):
+    def step_verification(self, pool_name, step_start_cuc, param, summary, tc_id, ver_file, ver_class, ver_func, step_id):
         """
         This functions does the verification for every step
         :param pool_name: str
@@ -25,7 +25,7 @@
                 ver_instance_call = getattr(ver_file, ver_class)
                 instance = ver_instance_call()
                 ver_func_call = getattr(instance, ver_func)
-                success = ver_func_call(pool_name, start_cuc=step_start_cuc, tc_id=tc_id)
+                success = ver_func_call(pool_name, start_cuc=step_start_cuc, tc_id=tc_id, run_id=self.run_id, step_id=step_id)
                 summary.result = success
             except:
                 logger.exception('Exception in the Verification for Step {}'.format(param['step_no']))
@@ -44,7 +44,7 @@
         :return: True if all conditions were successfull.
         :rtype: bool
         """
-        # testing_logger.cmd_log_handler(__name__)
+        testing_logger.cmd_log_handler(__name__)
         success = False
         logger.info('establishing postconditions started')
 
diff --git a/Tst/tst/generator_templates/co_post_condition_entry.py b/Tst/tst/generator_templates/co_post_condition_entry.py
index 2a8786a..75c7e19 100644
--- a/Tst/tst/generator_templates/co_post_condition_entry.py
+++ b/Tst/tst/generator_templates/co_post_condition_entry.py
@@ -13,3 +13,13 @@ print('Hello, I am the whole post-condition')
 #reset = tc.reset_all_housekeepings(pool_name=pool_name)
 success = True
 
+
+
+#####-Save-#####
+
+
+
+#####-Save-#####
+
+
+
diff --git a/Tst/tst/generator_templates/co_pre_condition.py b/Tst/tst/generator_templates/co_pre_condition.py
index 390bca5..2c79e9b 100644
--- a/Tst/tst/generator_templates/co_pre_condition.py
+++ b/Tst/tst/generator_templates/co_pre_condition.py
@@ -7,7 +7,7 @@
         :return: bool
             True if the preconditions are fulfilled
         """
-        #testing_logger.cmd_log_handler(__name__)
+        testing_logger.cmd_log_handler(__name__)
         success = False
         logger.info('establishing preconditions started')
 
@@ -32,10 +32,11 @@
         :return: tc_id:
         """
         testing_logger.cmd_log_handler(__name__)
+        step_id = self.check_run_and_step_id(pool_name=pool_name)
         step_start_cuc = cfl.get_last_pckt_time(pool_name=pool_name, string=False)
         report.command_step_begin(step_param=param, script_version=self.version(), pool_name=pool_name,
-                                  step_start_cuc=step_start_cuc)
+                                  step_start_cuc=step_start_cuc, run_id=self.run_id, step_id=step_id)
 
         summary = report.StepSummary(step_number=param['step_no'])
         tc_id = None
-        return step_start_cuc, summary, tc_id
+        return step_start_cuc, summary, tc_id, step_id
diff --git a/Tst/tst/generator_templates/co_pre_condition_entry.py b/Tst/tst/generator_templates/co_pre_condition_entry.py
index 2a38ef3..503f308 100644
--- a/Tst/tst/generator_templates/co_pre_condition_entry.py
+++ b/Tst/tst/generator_templates/co_pre_condition_entry.py
@@ -17,3 +17,13 @@ success = True
 
 
 
+#####-Save-#####
+
+
+
+
+
+#####-Save-#####
+
+
+
diff --git a/Tst/tst/generator_templates/co_step.py b/Tst/tst/generator_templates/co_step.py
index 81e6629..d8b4591 100644
--- a/Tst/tst/generator_templates/co_step.py
+++ b/Tst/tst/generator_templates/co_step.py
@@ -2,10 +2,10 @@
     def step_${testStepNumber}(self, pool_name):
         param = {
             'step_no': '$testStepNumber',
-            'msg': '$testStepDescription',
+            'descr': '$testStepDescription',
             'comment': '$testStepComment'
         }
-        step_start_cuc, summary, tc_id = self.begin_steps(pool_name=pool_name, param=param)
+        step_start_cuc, summary, tc_id, step_id = self.begin_steps(pool_name=pool_name, param=param)
 
         try:
             ########---The defined step starts here---########
@@ -14,16 +14,16 @@
 
             ########---The defined step ends here---########
         except Exception as e:
-            report.command_step_exception(step_param=param)
+            report.command_step_exception(step_param=param, step_id=step_id)
             logger.exception('Exception in the try block of the step')
             summary.result = False
             summary.had_exception()
         finally:
             step_end_cuc = cfl.get_last_pckt_time(pool_name=pool_name, string=False)
-            report.command_step_end(step_param=param, step_end_cuc=step_end_cuc)
+            report.command_step_end(step_param=param, step_end_cuc=step_end_cuc, step_id=step_id)
 
         summary =self.step_verification(pool_name=pool_name, step_start_cuc=step_start_cuc, param=param, tc_id=tc_id,
                                         summary=summary, ver_file=${testSpecFileName}_verification,
-                                        ver_class="${testSpecClassName}Verification", ver_func="step_${testStepNumber}")
+                                        ver_class="${testSpecClassName}Verification", ver_func="step_${testStepNumber}", step_id=step_id)
 
         return summary
diff --git a/Tst/tst/generator_templates/run_header.py b/Tst/tst/generator_templates/run_header.py
index 789600d..6f5726b 100644
--- a/Tst/tst/generator_templates/run_header.py
+++ b/Tst/tst/generator_templates/run_header.py
@@ -10,6 +10,7 @@ print('current working directory: {}'.format(os.getcwd()))
 import confignator
 ccs_path = confignator.get_option('paths', 'ccs')
 sys.path.append(ccs_path)
+import ccs_function_lib as cfl
 cfl.add_tst_import_paths()
 from testlib import tools
 from testlib import report
@@ -18,7 +19,7 @@ from testlib import tc
 from testlib import precond
 from testlib import testing_logger
 from testlib import sim
-logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
+logging.basicConfig(stream=sys.stdout, level=logging.INFO)
 import ${testSpecFileName}_command
 import ${testSpecFileName}_verification
 
diff --git a/Tst/tst/generator_templates/ver_class.py b/Tst/tst/generator_templates/ver_class.py
index 145044d..4e380dd 100644
--- a/Tst/tst/generator_templates/ver_class.py
+++ b/Tst/tst/generator_templates/ver_class.py
@@ -12,6 +12,7 @@ class ${testSpecClassName}Verification:
         self.precond_ok = False
         self.integrity = True
         self.exceptions = []
+        self.run_id = False
 
         # some tests are depended on other tests, thus information is stored on class level
         # insert class variables here
@@ -39,9 +40,22 @@ class ${testSpecClassName}Verification:
 
         # ------- analyze command log -> get step start CUC timestamps, TCid and step end CUC timestamps -------
         # ToDo
+        self.run_id = False
         steps = analyse_command_log.get_steps(filename=command_log_file)
         tcs = analyse_command_log.get_sent_tcs(filename=command_log_file)
         # ------- loop over the verification steps, show progress -------
         # ToDo
         # ------- show final result -------
         # ToDo
+
+    def vrc_step_begin(self, pool_name, param, run_id, step_id):
+        if run_id:
+            self.run_id = run_id
+        else:
+            if not self.run_id:
+                now = datetime.now()  # current date and time
+                self.run_id = now.strftime("%Y%m%d%H%M%S")
+
+        step_start_cuc = cfl.get_last_pckt_time(pool_name=pool_name, string=False)
+        report.verification_step_begin(step_param=param, script_version=self.version(), pool_name=pool_name,
+                                       step_start_cuc=step_start_cuc, run_id=self.run_id, step_id=step_id)
diff --git a/Tst/tst/generator_templates/ver_header.py b/Tst/tst/generator_templates/ver_header.py
index 3fa12b7..c27253d 100644
--- a/Tst/tst/generator_templates/ver_header.py
+++ b/Tst/tst/generator_templates/ver_header.py
@@ -12,6 +12,7 @@ sys.path.append(confignator.get_option('tst-paths', 'testing_library'))
 
 import ccs_function_lib as cfl
 
+from datetime import datetime
 from testlib import report
 from testlib import analyse_command_log
 from testlib import testing_logger
diff --git a/Tst/tst/generator_templates/ver_step.py b/Tst/tst/generator_templates/ver_step.py
index 4e4e675..5c4f771 100644
--- a/Tst/tst/generator_templates/ver_step.py
+++ b/Tst/tst/generator_templates/ver_step.py
@@ -1,28 +1,21 @@
     # STEP $testStepNumber --------------------------------------------------------------------------------------------------------
-    def step_$testStepNumber(self, pool_name, start_cuc=None, tc_id=None):
+    def step_$testStepNumber(self, pool_name, start_cuc=None, tc_id=None, run_id=None, step_id=None):
         testing_logger.ver_log_handler(__name__)
         param = {
             'step_no': '$testStepNumber',
-            'msg': '$testStepDescription',
+            'descr': '$testStepDescription',
             'comment': '$testStepComment'
         }
-        step_start_cuc = cfl.get_last_pckt_time(pool_name=pool_name, string=False)
-        report.verification_step_begin(step_param=param, script_version=self.version(), pool_name=pool_name, step_start_cuc=step_start_cuc)
-        # if online: use provided timestamp when the step started, use provided TcId
-        if start_cuc is not None:
-            pass
-        # if offline: read the command log file and extract the starting timestamp and TcId
-        else:
-            pass
+        self.vrc_step_begin(pool_name=pool_name, param=param, run_id=run_id, step_id=step_id)
 
         result = True
         try:
             $testStepVerificationCode
         except Exception as e:
-            report.verification_step_exception(step_param=param)
+            report.verification_step_exception(step_param=param, step_id=step_id)
             logger.exception('Exception in the try block of the step')
             result = False
         finally:
             step_end_cuc = cfl.get_last_pckt_time(pool_name=pool_name, string=False)
-            report.verification_step_end(step_param=param, step_result=result, step_end_cuc=step_end_cuc)
+            report.verification_step_end(step_param=param, step_result=result, step_end_cuc=step_end_cuc, step_id=step_id)
         return result
-- 
GitLab