# standard library
import socket   
import sys
import copy
import threading
import os
import time
import datetime
import subprocess
import matplotlib.pyplot as plt
import queue
# local source
import DiscomDataExportLibrary as Pcl   
import DiscomDataExportConfig as Conf
# ext lib
import numpy as np

def RunExecution(ArchivePath1, ArchivePath2, absolute_tolerance, relative_tolerance, writeToFile, fileName, plotDifferences, plotFolder, clavis=Pcl.Quinter("*","*","*","*","*","*"), message_queue = queue.Queue(), message_event = threading.Event()):
    #################################################################################################################
    ################################# Redirect StdOut ###############################################################
    #################################################################################################################

    if(writeToFile):
        f = open(fileName,'w')

    def print_queue(*message):
        try:
            if(writeToFile):
                msg = " ".join(message)
                msg += "\n"
                f.write(msg)
            if isinstance(*message, str):
                message_queue.put(" ".join(message))
                message_event.set()
                print(*message)
        except:
            print_queue("End")
            #FIXME
    # Here we check if multiple Archives are compared, at the moment only the plotting for two Archives works 
    if len(ArchivePath2) > 1:
        plotDifferences = False
        print_queue("Plotting is not supported for multiple Archives. Sorry!")
    #################################################################################################################
    ################################# Data Client ###################################################################
    #################################################################################################################
    # Data Client
    class CustomDataClient(Pcl.DataClient):
        def __init__(self, clientName, iPAdress, iPPort, clientType, finish_event = Pcl.threading.Event()):
            self.Finish_Event = finish_event
            self.Data_Received = list()
            return super().__init__(clientName, iPAdress, iPPort, clientType)
        def Log(self,clientName,message,messageType):
            print_queue(message)
        def OnEndOfTest(self, measurementValues, errorList):
            self.Data_Received = measurementValues 
        def OnHistoricalDataQueryInfo(self, historicalDataQueryInfo):
            if historicalDataQueryInfo.Index == historicalDataQueryInfo.NrOfMeasurements:
                self.Finish_Event.set()
    #################################################################################################################
    ################################# ControlClientCall #############################################################
    #################################################################################################################
    # Control Client
    class CustomControlClient(Pcl.ControlClient):
        def Log(self,clientName,message,messageType):
            print_queue(message)
        def TriggerExecution(self,ArchivePath):
            # Define an ArchiveQuery
            ArchiveQueryObject = Pcl.ArchiveQueryObject()
            ArchiveQueryObject.Archives.clear()
            ArchiveQueryObject.Archives.append(ArchivePath)
            # Target the corresponding DataClient from this Skript
            ArchiveQueryObject.ClientId = "cdc"
            ArchiveQueryObject.DateFrom =        datetime.datetime(2015, 11, 23,  0,  0)
            ArchiveQueryObject.DateUntil =       datetime.datetime(2022, 11, 23,  0,  0)
            ArchiveQueryObject.TopN = 1
            ArchiveQueryObject.BaseTypeIds = list()
            ArchiveQueryObject.TypeIds = list()
            ArchiveQueryObject.TestBenchIds = list()
            ArchiveQueryObject.ResultIds = list()
            ArchiveQueryObject.TestKindIds = list()
            ArchiveQueryObject.PropertyIds = list()
            ArchiveQueryObject.SerialNumbers = list()

            self.TriggerHistoricalArchiveProcessing(ArchiveQueryObject)
    # wrapper didn't work as planed for multiple Archives, kept here because it could be usefull if plotting for multiple Archives has to be implimenet
    def loop_wrapper(func):
        def wrapper(V1,V2,Absolute_Tolerance,Relative_Tolerance,Clavis):
            bool_list = [func(V1,archive,Absolute_Tolerance,Relative_Tolerance,Clavis) for archive in V2]
            all_true = np.all(bool_list)
            return all_true
        return wrapper

    #################################################################################################################
    ################################# Compare Two Values ############################################################
    #################################################################################################################
    # Equal
    # https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
    def CompFloatEpsilon(V1,V2):
        if abs(V1 - V2) > max(abs(V1), abs(V2))* sys.float_info.epsilon:
            return False
        else:
            return True
    # Smaller than Tollerance
    def CompVal(a, b, abs_tol=0.0, rel_tol=1e-05):
        return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
    #numpy version of the code above, at the moment not used. First a test has to be established to determin if results are equal.
    def npCompVal(a, b, abs_tol=0.0, rel_tol=1e-05):
        return np.allclose(a, b, rel_tol, abs_tol)
    def npCompVal_diff_count(a, b, abs_tol=0.0, rel_tol=1e-05):
        return np.isclose(a, b, rel_tol, abs_tol)   
    def CompVal_Val(a, b):
        return abs(a-b)
    #################################################################################################################
    ################################# Compare SingleValue ###########################################################
    #################################################################################################################
        #self.Value = value              # Single, measured value (x)
        #self.Unit = unit                # uint32 enum (unit_t), unit of measured value
        #self.Limits = limits            # List<single>, limits against which the measured value is evaluated 
        #self.LimitNames = limitNames    # string [], array of limit names
    #@loop_wrapper
    def CompareSingleValue(SV1,SV2,Absolute_Tolerance,Relative_Tolerance,Clavis):
        if(SV1.Unit.SI_Unit != SV2.Unit.SI_Unit):
            print_queue("Different Unit in Clavis: " + Clavis.QuinterText())
            return False
        if not(CompVal( SV1.Value,SV2.Value,Absolute_Tolerance,Relative_Tolerance)):
            print_queue(str(SV1.Value) + ",",str(SV2.Value) + ",Abs_Diff: " + str(CompVal_Val(SV1.Value,SV2.Value)) )
            print_queue("SingleValue: " + Clavis.QuinterText())
            print_queue("######")
            return False
        return True
    #################################################################################################################
    ################################# Compare Vector ################################################################
    #################################################################################################################
        #self.Len = len                  # Uint32, number of data points
        #self.Delta = delta              # Single, increment between data points
        #self.Zero = zero                # Single, starting point of the vector
        #self.Values = values            # Single array, array of measured values (x)
        #self.Delta_Unit = delta_unit    # uint32 enum (unit_t) 
        #self.Unit = unit                # uint32 enum (unit_t)
        #self.Limits = limits            # List<single []>, limits against which the measured values are evaluated
        #self.LimitNames = limitNames    # string [], array of limit names
    def CompareVector(V1,V2,Absolute_Tolerance,Relative_Tolerance,Clavis):
        if(V1.Unit.SI_Unit != V2.Unit.SI_Unit):
            print_queue("Different Unit in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Len != V2.Len):
            print_queue(str(V1.Len),",",str(V2.Len))
            print_queue("Different Len in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Delta != V2.Delta):
            print_queue("Different Delta in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Zero != V2.Zero):
            print_queue("Different Zero in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Delta_Unit.SI_Unit != V2.Delta_Unit.SI_Unit):
            print_queue("Different Delta_Unit in Clavis: " + Clavis.QuinterText())
            return False
        # numpy version of the code below, not tested
        # V1_arr = np.asarray(V1.Values)
        # V2_arr = np.asarray(V2.Values)
        # if (npCompVal(V1_arr, V2_arr, Absolute_Tolerance,Relative_Tolerance)):
        #     print("CompareVector")
        #     return True
        # else:
        #     boolean_array = npCompVal_diff_count(V1_arr, V2_arr, Absolute_Tolerance,Relative_Tolerance)
        #     #values = V1_arr[~boolean_array]
        #     #values_v2 = V2_arr[~boolean_array]
        #     print_queue(V2.Unit.Symbol)
        #     print_queue("Vector: " + Clavis.QuinterText())
        #     print_queue("DiffCount: " + str(np.size(boolean_array) - np.count_nonzero(boolean_array)))
        #     print_queue("######")
        #     return False
            

        diffCount = 0
        for pos, value in enumerate(V1.Values):
            v1 = value
            v2 = V2.Values[pos]
            if not(CompVal( v1,v2,Absolute_Tolerance,Relative_Tolerance)):
                diffCount += 1
                #print_queue(str(v1),",",str(v2),",Diff: ",str(CompVal_Val(v1,v2)))
        if (diffCount > 0):
            print_queue("Vector: " + Clavis.QuinterText())
            print_queue("DiffCount: " + str(diffCount))
            print_queue("######")
            return False
        return True

    def PlotVector(V1,V2,Clavis):
        # numpy version
        Speed = V1.Zero + (np.arange(len(V1.Values)) * V1.Delta)
        Diff = np.subtract(V1.Values,V2.Values)
        plt.clf()
        plt.plot(Speed,V1.Values,'b-',label='Arc_1',linewidth=0.5)
        plt.plot(Speed,V2.Values,'g-',label='Arc_2',linewidth=0.5)
        plt.axhline(y=0,linewidth=0.5)
        plt.plot(Speed,Diff,'r-',label='diff',linewidth=0.5)
        plt.legend()
        plt.xlabel(Pcl.GetUnit(V1.Delta_Unit))
        plt.ylabel(Pcl.GetUnit(V1.Unit))
        plt.title(Clavis.QuinterText())
        clavis_text = Clavis.ClavisText()
        clavis_text = clavis_text.replace("/","_")
        clavis_text = clavis_text.replace("//","_")
        clavis_text = clavis_text.replace("\\","_")
        clavis_text = clavis_text.replace("\\\\","_")
        path = os.path.join(plotFolder,clavis_text+".png")
        plt.savefig(path)
    #################################################################################################################
    ################################# Compare Polygon2D #############################################################
    #################################################################################################################
        #self.Len = len                  # Uint32, number of data points
        #self.Values = values            # List<Tuple<single,single>>, tuple consists of measured value vs. control value (x, y)
        #self.Pos_Unit = pos_unit        # uint32 enum (unit_t)
        #self.Unit = unit                # uint32 enum (unit_t)
        #self.Limits = limits            # List<List<Tuple<single,single>>>, limits against which the measured values are evaluated
        #self.LimitNames = limitNames    # string [], array of limit names
    def ComparePolygon2D(V1,V2,Absolute_Tolerance,Relative_Tolerance,Clavis):
        if(V1.Unit.SI_Unit != V2.Unit.SI_Unit):
            print_queue("Different Unit in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Len != V2.Len):
            print_queue(str(V1.Len),",",str(V2.Len))
            print_queue("Different Len in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Pos_Unit.SI_Unit != V2.Pos_Unit.SI_Unit):
            print_queue("Different Pos_Unit in Clavis: " + Clavis.QuinterText())
            return False
        diffCount = 0
        posDiffCount = 0
        # numpy version of the code below, not tested
        # List of Tupels -> to numpy array, np.asarray -> compare arrays
        #V1_arr = V1.Values
        #V2_arr = V2.Values
        #print(V2_arr)
        # if (npCompVal(V1_arr, V2_arr, Absolute_Tolerance,Relative_Tolerance)):
        #     print("ComparePolygon2D")
        #     return True
        # else:
        #     boolean_array = npCompVal_diff_count(V1_arr, V2_arr, Absolute_Tolerance,Relative_Tolerance)
        #     #values = V1_arr[~boolean_array]
        #     #values_v2 = V2_arr[~boolean_array]
        #     #print_queue(V2.Unit.Symbol)
        #     print_queue("Polygon2D: " + Clavis.QuinterText())
        #     #print_queue("DiffCount: " + str(np.size(boolean_array) - np.count_nonzero(boolean_array)))
        #     #print_queue("PosDiffCount: " + str(posDiffCount))
        #     print_queue("######")
        #     return False
        for pos, value in enumerate(V1.Values):
            v11 = value[0]
            v12 = value[1]
            v21 = V2.Values[pos][0]
            v22 = V2.Values[pos][1]
            if not(CompVal( v11,v21,Absolute_Tolerance,Relative_Tolerance)):
                posDiffCount = posDiffCount + 1
                #print_queue(str(v11),",",str(v21),str(),",Diff: ",str(CompVal_Val(v11,v21)))
            if not(CompVal( v12,v22,Absolute_Tolerance,Relative_Tolerance)):
                diffCount = diffCount + 1
                #print_queue(str(v12),",",str(v22),str(),",Pos Diff: ",str(CompVal_Val(v12,v22)))
        if (diffCount > 0 or posDiffCount > 0):
            print_queue("Polygon2D: " + Clavis.QuinterText())
            print_queue("DiffCount: " + str(diffCount))
            print_queue("PosDiffCount: " + str(posDiffCount))
            print_queue("######")
            return False
        return True

    def PlotPolygon2D(V1,V2,Clavis):
        x1 = []
        x2 = []
        Val1 = []
        Val2 = []
        xDiff = []
        ValDiff = []
        for pos, value in enumerate(V1.Values):
            x1.append(value[0])
            Val1.append(value[1])
            x2.append(V2.Values[pos][0])
            Val2.append(V2.Values[pos][1])
            if not CompVal(Val2[pos],Val1[pos]):
                xDiff.append(value[0])
                ValDiff.append(Val2[pos]-Val1[pos])
        plt.clf()
        plt.plot(x1,Val1,'bo',label='Archive_1',markersize=2)
        plt.plot(x2,Val2,'go',label='Archive_2',markersize=2)
        plt.plot(xDiff,ValDiff,'ro',label='Difference',markersize=2)
        plt.axhline(y=0)
        plt.legend()
        plt.ylabel(Pcl.GetUnit(V1.Unit))
        plt.xlabel(Pcl.GetUnit(V2.Pos_Unit))
        plt.title(Clavis.QuinterText())
        clavis_text = Clavis.ClavisText()
        clavis_text = clavis_text.replace("/","_")
        clavis_text = clavis_text.replace("//","_")
        clavis_text = clavis_text.replace("\\","_")
        clavis_text = clavis_text.replace("\\\\","_")
        path = os.path.join(plotFolder,clavis_text+".png")
        plt.savefig(path)
    #################################################################################################################
    ################################# Compare VectorList ############################################################
    #################################################################################################################
        #self.Element_Len = element_len                  # Uint32, number of vectors
        #self.Len = len                                  # Uint32, number of data points
        #self.Element_Delta = element_delta              # Single, increment between vectors
        #self.Element_Zero = element_zero                # Single, starting point of the first vector
        #self.Values = values                            # List<single []>, list of arrays of measured values (x)
        #self.Pos_Unit = pos_unit                        # uint32 enum (unit_t)
        #self.Unit = unit                                # uint32 enum (unit_t) 
        #self.Element_Delta_Unit = element_delta_unit    # uint32 enum (unit_t)
        #self.Limits = limits                            # List<List< single []>>, limits against which the measured values are evaluated
        #self.LimitNames = limitNames                    # string [], array of limit names
    def CompareVectorList(V1,V2,Absolute_Tolerance,Relative_Tolerance,Clavis):
        if(V1.Unit.SI_Unit != V2.Unit.SI_Unit):
            print_queue("Different Unit in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Len != V2.Len):
            print_queue(str(V1.Len),",",str(V2.Len))
            print_queue("Different Len in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Pos_Unit.SI_Unit != V2.Pos_Unit.SI_Unit):
            print_queue("Different Pos_Unit in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Element_Len != V2.Element_Len):
            print_queue("Different Element_Len in Clavis: ",Clavis.QuinterText())
            return False
        if(V1.Element_Delta != V2.Element_Delta):
            print_queue("Different Element_Delta in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Element_Zero != V2.Element_Zero):
            print_queue("Different Element_Zero in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Element_Delta_Unit.SI_Unit != V2.Element_Delta_Unit.SI_Unit):
            print_queue("Different Element_Delta_Unit in Clavis: " + Clavis.QuinterText())
            return False
        # numpy version of the code below, not tested
        # V1_arr = V1.Values
        # V2_arr = V2.Values
        # if (npCompVal(V1_arr, V2_arr, Absolute_Tolerance,Relative_Tolerance)):
        #     print("CompareVectorList")
        #     return True
        # else:
        #     boolean_array = npCompVal_diff_count(V1_arr, V2_arr, Absolute_Tolerance,Relative_Tolerance)
        #     #values = V1_arr[~boolean_array]
        #     #values_v2 = V2_arr[~boolean_array]
        #     #print_queue(V2.Unit.Symbol)
        #     print_queue("VectorList: " + Clavis.QuinterText())
        #     # print_queue("DiffCount: " + str(diffCount))
        #     # print_queue("PosDiffCount: " + str(posDiffCount))
        #     print_queue("######")
        #     return False
        posDiffCount = 0
        diffCount = 0
        for pos, list in enumerate(V1.Values):
            for cor, value in enumerate(list):
                v1 = value
                v2 = V2.Values[pos][cor]
                if not(CompVal( v1,v2,Absolute_Tolerance,Relative_Tolerance)):
                    if cor == 0:
                        posDiffCount = posDiffCount +1
                    else:
                        diffCount = diffCount + 1
                    #if cor == 0:
                    #    print(str(v1),",",str(v2),",Pos Diff: ",str(CompVal_Val(v1,v2)))
                    #else:
                    #    print(str(v1),",",str(v2),",Pos:",str(V2.Values[pos][0]),",Diff: ",str(CompVal_Val(v1,v2)))
        if (diffCount > 0 or posDiffCount > 0):
            print_queue("VectorList: " + Clavis.QuinterText())
            print_queue("DiffCount: " + str(diffCount))
            print_queue("PosDiffCount: " + str(posDiffCount))
            print_queue("######")
            return False
        return True
    #################################################################################################################
    ################################# Compare Matrix ################################################################
    #################################################################################################################
        #self.Element_Len = element_len                  # Uint32, number of data points (z)
        #self.Len = len                                  # Uint32, number of data points (y)
        #self.Element_Delta = elemet_delta               # Single, increment z dimension
        #self.Element_Zero = element_zero                # Single, starting point z dimension
        #self.Delta = delta                              # Single, increment y dimension
        #self.Zero = zero                                # Single, starting point y dimension
        #self.Values = values                            # List<single []>, list of arrays of measured values (x)
        #self.Element_Delta_Unit = element_delta_unit    # uint32 enum (unit_t)
        #self.Delta_Unit = delta_unit                    # uint32 enum (unit_t)
        #self.Unit = unit                                # uint32 enum (unit_t)
    def CompareMatrix(V1,V2,Absolute_Tolerance,Relative_Tolerance,Clavis):
        if(V1.Element_Len != V2.Element_Len):
            print_queue("Different Element_Len in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Len != V2.Len):
            print_queue(str(V1.Len),",",str(V2.Len))
            print_queue("Different Len in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Element_Delta != V2.Element_Delta):
            print_queue("Different Element_Delta in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Element_Zero != V2.Element_Zero):
            print_queue("Different Element_Zero in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Delta != V2.Delta):
            print_queue("Different Delta in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Zero != V2.Zero):
            print_queue("Different Zero in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Element_Delta_Unit.SI_Unit != V2.Element_Delta_Unit.SI_Unit):
            print_queue("Different Element_Delta_Unit in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Delta_Unit.SI_Unit != V2.Delta_Unit.SI_Unit):
            print_queue("Different Delta_Unit in Clavis: " + Clavis.QuinterText())
            return False
        if(V1.Unit.SI_Unit != V2.Unit.SI_Unit):
            print_queue("Different Unit in Clavis: " + Clavis.QuinterText())
            return False
        diffCount = 0
        for pos, list in enumerate(V1.Values):
            for cor, value in enumerate(list):
                v1 = value
                v2 = V2.Values[pos][cor]
                if not(CompVal( v1,v2,Absolute_Tolerance,Relative_Tolerance)):
                    diffCount = diffCount + 1
                    #print(str(v1),",",str(v2),",Diff: ",str(CompVal_Val(v1,v2)))
        if (diffCount > 0):
            print_queue("Matrix: " + Clavis.QuinterText())
            print_queue("DiffCount: " + str(diffCount))
        if (diffCount > 0):
            print_queue("######")
            return False
        return True
    #################################################################################################################
    ################################# Compare Two MasterValueClass ##################################################
    #################################################################################################################
    def CompareMasterValueClass(MVC1,MVC2,Absolute_Tolerance,Relative_Tolerance):
        """Compares two MasterValueClass within Tolerance

        MVC1: MasterValueClass1
        MVC2: MasterValueClass2
        Tolerance: Tolerance of Differences

        Returns True If Equal
        """
        try:
            if(MVC1.DataType == Pcl.DataType.SingleValue and MVC2.DataType == Pcl.DataType.SingleValue):
                if(CompareSingleValue(MVC1.SingleValue,MVC2.SingleValue,Absolute_Tolerance,Relative_Tolerance,MVC1.Clavis)):
                    return True
            elif(MVC1.DataType == Pcl.DataType.Vector and MVC2.DataType == Pcl.DataType.Vector):
                if(CompareVector(MVC1.Vector,MVC2.Vector,Absolute_Tolerance,Relative_Tolerance,MVC1.Clavis)):
                    return True
                elif(plotDifferences):
                    PlotVector(MVC1.Vector,MVC2.Vector,MVC1.Clavis)
            elif(MVC1.DataType == Pcl.DataType.Polygon2D and MVC2.DataType == Pcl.DataType.Polygon2D):
                if(ComparePolygon2D(MVC1.Polygon2D,MVC2.Polygon2D,Absolute_Tolerance,Relative_Tolerance,MVC1.Clavis)):
                    return True
                elif(plotDifferences):
                    PlotPolygon2D(MVC1.Polygon2D,MVC2.Polygon2D,MVC1.Clavis)
            elif(MVC1.DataType == Pcl.DataType.VectorList and MVC2.DataType == Pcl.DataType.VectorList):
                if(CompareVectorList(MVC1.VectorList,MVC2.VectorList,Absolute_Tolerance,Relative_Tolerance,MVC1.Clavis)):
                    return True
            elif(MVC1.DataType == Pcl.DataType.Matrix and MVC2.DataType == Pcl.DataType.Matrix):
                if(CompareMatrix(MVC1.Matrix,MVC2.Matrix,Absolute_Tolerance,Relative_Tolerance,MVC1.Clavis)):
                    return True
            else:
                return False
        except:
            return False
        return False
    #################################################################################################################
    ################################# Compare Two DataSets ##########################################################
    #################################################################################################################
    def CompareDataReceived(Data_Received1,Data_Received2_list,Absolute_Tolerance,Relative_Tolerance):
        """Evaluates if two Lists of MasterValueCalsses are Equal within given Tolrance

        Data_Received1: List of MasterValueClass1
        Data_Received2: List of MasterValueClass2
        Tolerance: Tolerance of Differences in Percent ( ||a1/a2 - 1|| < Tolerance )

        """
        # Unfortunally we can not, without changeing a big part of the code, handing the list further down, so we have to set the loop to iterate over the archives here. 
        boole_list = []
        for Data_Received_2 in Data_Received2_list:
            if(len(Data_Received_1)>=len(Data_Received_2)):
                boole_val = CompareDataReceivedSorted(Data_Received1,Data_Received_2,Absolute_Tolerance,Relative_Tolerance)
            else:
                boole_val = CompareDataReceivedSorted(Data_Received_2,Data_Received1,Absolute_Tolerance,Relative_Tolerance)
            boole_list.append(boole_val)
        # Since CompareDataReceived returns a boolean we have to use np.isin here to get the same functionality. Note CompareDataReceivedSorted returns a boolean
        return not np.isin([False],boole_list)

    def CompareDataReceivedSorted(Data_Received1,Data_Received2,Absolute_Tolerance,Relative_Tolerance):
        equal = True
        for pos, MVC in enumerate(Data_Received1):

            foundClavis = False
            # Hoffentliche sind die Messwerte gleich sortiert:
            if( (pos < len(Data_Received2)) and MVC.Clavis.CompareClavis(Data_Received2[pos].Clavis)):
                foundClavis = True
                if not(CompareMasterValueClass(MVC,Data_Received2[pos],Absolute_Tolerance,Relative_Tolerance)):
                    equal = False
                    Clavis_Diff_List.append(MVC.Clavis)
            # Sonst Muessen wir auf die Suche gehen:
            else:
                for RefMVC in Data_Received2:
                    if(MVC.Clavis.CompareClavis(RefMVC.Clavis)):
                        foundClavis = True
                        if not(CompareMasterValueClass(MVC,RefMVC,Absolute_Tolerance,Relative_Tolerance)):
                            equal = False
                            Clavis_Diff_List.append(MVC.Clavis)
            # Den Messwert gab es nicht:
            if not(foundClavis):
                print_queue("Clavis not found: " + MVC.Clavis.QuinterText())
                equal = False
                Clavis_Diff_List.append(MVC.Clavis)
        if( len(Data_Received1) != len(Data_Received2) ):
            equal = False
        return equal
    # I build a function so that the 'main' is not that packed
    def get_the_data(ccc,cdc,finish_event,ArchivePath):
        Data_Received = list()
        ccc.TriggerExecution(ArchivePath)
        # wait until processing finishes
        finish_event.wait()
        finish_event.clear()
        Data_Received = copy.copy(cdc.Data_Received)
        cdc.Data_Received.clear()
        return Data_Received
    #################################################################################################################
    ################################# Main ##########################################################################
    #################################################################################################################
    # Save Quinter wich are not different
    Clavis_Diff_List = list()
    # Mode/Instrument/Parameter/Channel/Sensor/Location
    Quinter = clavis
    
    # does not work at the moment, users have to start the service by themself.
    use_service = False
    if use_service:
    # Thread fuer den Service starten
        path_pwd = os.getcwd()
        path_exe = os.path.join(path_pwd ,"ArchiveExtractorService", "ArchiveExtractorService.exe")
        pid = subprocess.Popen(path_exe)

    # Define an Event to get called when the Worker Thread finishes Historical Archive Query
    finish_event = Pcl.threading.Event()
    # Define Service Connection
    Service_IP = "127.0.0.1"
    Service_Port = 31001
    # Initialize Clients
    ccc = CustomControlClient("ccc",Service_IP,Service_Port,Pcl.eClientType.Controller)
    cdc = CustomDataClient("cdc",Service_IP,Service_Port,Pcl.eClientType.HistoricalData,finish_event)
    # Define Measurement Value Request
    cdc.ClavisList.append(Pcl.QuinterRequest(Quinter))
    # Connect the Clients to the Service
    cdc.Connect()
    ccc.Connect()

    ######################## Get the Data ########################

    Data_Received_1 = get_the_data(ccc,cdc,finish_event,ArchivePath1)

    #print(ArchivePath2)
    # Use list comprehension to get a list of Data Objects
    Data_Received_2 = [get_the_data(ccc,cdc,finish_event,archive) for archive in ArchivePath2]

    ######################## End all Processing ########################
    # Disconecct the Clients (Will also be called on Destruct)
    ccc.TriggerCommand(Pcl.ControlSocketCommand(cdc.ClientName,Pcl.eSocketCommandType.Shutdown))
    # Disconecct the Clients (Will also be called on Destruct)
    ccc.Disconnect()
    # Disconnect is not needed anymore it is handeld now by ccc.TriggerCommand(Pcl.ControlSocketCommand(cdc.ClientName,Pcl.eSocketCommandType.Shutdown))
    #cdc.Disconnect()

    
    print_queue("Comparing the Measurementvalues")
    print_queue("Absolute Tolerance:" + str(absolute_tolerance) )
    print_queue("Relative Tolerance:" + str(relative_tolerance) )
    print_queue(ArchivePath1,",",ArchivePath2)
    print_queue("#################################################################################################################")
    if( CompareDataReceived(Data_Received_1,Data_Received_2,absolute_tolerance,relative_tolerance) ):
        print_queue("Archives are Equal")

    print_queue("Follwing Measurement Values are Different:")
    # use only uniques for the Clavis_Diff_List
    Clavis_Diff_List = np.unique(Clavis_Diff_List)
    for clavis in Clavis_Diff_List:
        print_queue(clavis.QuinterText())
    if(len(Clavis_Diff_List)>0):
        print_queue("There were found" + str(len(Clavis_Diff_List)),"different Measurement Values.")
    # Closing File
    if(writeToFile):
        f.close()
        writeToFile = False
    if use_service:
        pid.kill()
    return Clavis_Diff_List

# Definiere Die Input Konstanten
# ArchivePath1 = "C:/Users/sthurm/Temp/2.rdt"
# ArchivePath2 = "C:/Users/sthurm/Temp/3.rdt"
# Absolute_Tolerance = 0
# Relative_Tolerance = 0
# writeToFile = False
# fileName = "C:/Users/sthurm/Temp/DiffOutput.txt"
# plotData = False
# plotFolder = "C:/Users/sthurm/Temp/Plots/"
# clavis = Pcl.Quinter("*","*","*","*","*","*")
# RunExecution(ArchivePath1, ArchivePath2, Absolute_Tolerance, Relative_Tolerance, writeToFile, fileName, plotData, plotFolder, clavis)