#!/usr/bin/env python

__author__ = "Richard Clubb"
__copyrights__ = "Copyright 2018, the python-uds project"
__credits__ = ["Richard Clubb"]

__license__ = "MIT"
__maintainer__ = "Richard Clubb"
__email__ = "richard.clubb@embeduk.com"
__status__ = "Development"


import xml.etree.ElementTree as ET

from uds.uds_config_tool.UtilityFunctions import isDiagServiceTransmissionOnly
from uds.uds_communications.Uds.Uds import Uds
from uds.uds_config_tool.SupportedServices.DiagnosticSessionControlContainer import DiagnosticSessionControlContainer
from uds.uds_config_tool.FunctionCreation.DiagnosticSessionControlMethodFactory import DiagnosticSessionControlMethodFactory
from uds.uds_config_tool.SupportedServices.ECUResetContainer import ECUResetContainer
from uds.uds_config_tool.FunctionCreation.ECUResetMethodFactory import ECUResetMethodFactory
from uds.uds_config_tool.SupportedServices.ReadDataByIdentifierContainer import ReadDataByIdentifierContainer
from uds.uds_config_tool.FunctionCreation.ReadDataByIdentifierMethodFactory import ReadDataByIdentifierMethodFactory
from uds.uds_config_tool.SupportedServices.WriteDataByIdentifierContainer import WriteDataByIdentifierContainer
from uds.uds_config_tool.FunctionCreation.WriteDataByIdentifierMethodFactory import WriteDataByIdentifierMethodFactory
from uds.uds_config_tool.SupportedServices.ClearDTCContainer import ClearDTCContainer
from uds.uds_config_tool.FunctionCreation.ClearDTCMethodFactory import ClearDTCMethodFactory
from uds.uds_config_tool.SupportedServices.ReadDTCContainer import ReadDTCContainer
from uds.uds_config_tool.FunctionCreation.ReadDTCMethodFactory import ReadDTCMethodFactory
from uds.uds_config_tool.SupportedServices.InputOutputControlContainer import InputOutputControlContainer
from uds.uds_config_tool.FunctionCreation.InputOutputControlMethodFactory import InputOutputControlMethodFactory
from uds.uds_config_tool.SupportedServices.RoutineControlContainer import RoutineControlContainer
from uds.uds_config_tool.FunctionCreation.RoutineControlMethodFactory import RoutineControlMethodFactory
from uds.uds_config_tool.SupportedServices.RequestDownloadContainer import RequestDownloadContainer
from uds.uds_config_tool.FunctionCreation.RequestDownloadMethodFactory import RequestDownloadMethodFactory

from uds.uds_config_tool.SupportedServices.SecurityAccessContainer import SecurityAccessContainer
from uds.uds_config_tool.FunctionCreation.SecurityAccessMethodFactory import SecurityAccessMethodFactory
from uds.uds_config_tool.SupportedServices.RequestUploadContainer import RequestUploadContainer
from uds.uds_config_tool.FunctionCreation.RequestUploadMethodFactory import RequestUploadMethodFactory
from uds.uds_config_tool.SupportedServices.TransferDataContainer import TransferDataContainer
from uds.uds_config_tool.FunctionCreation.TransferDataMethodFactory import TransferDataMethodFactory
from uds.uds_config_tool.SupportedServices.TransferExitContainer import TransferExitContainer
from uds.uds_config_tool.FunctionCreation.TransferExitMethodFactory import TransferExitMethodFactory
from uds.uds_config_tool.SupportedServices.TesterPresentContainer import TesterPresentContainer
from uds.uds_config_tool.FunctionCreation.TesterPresentMethodFactory import TesterPresentMethodFactory
from uds.uds_config_tool.ISOStandard.ISOStandard import IsoServices, IsoRoutineControlType, IsoInputOutputControlOptionRecord, IsoReadDTCSubfunction, IsoReadDTCStatusMask as Mask

from uds.uds_config_tool.ISOStandard.ISOStandard import IsoServices

class UdsContainerAccess:
    containers: list = []


def get_serviceIdFromXmlElement(diagServiceElement, xmlElements):

    requestKey = diagServiceElement.find('REQUEST-REF').attrib['ID-REF']
    requestElement = xmlElements[requestKey]
    params = requestElement.find('PARAMS')
    for i in params:
        try:
            if(i.attrib['SEMANTIC'] == 'SERVICE-ID'):
                return int(i.find('CODED-VALUE').text)
        except:
            pass

    return None


def fill_dictionary(xmlElement):
    temp_dictionary = {}
    for i in xmlElement:
        temp_dictionary[i.attrib['ID']] = i

    return temp_dictionary


def createUdsConnection(xmlFile, ecuName, ihexFile=None, **kwargs):

    root = ET.parse(xmlFile)

    # create any supported containers
    diagnosticSessionControlContainer = DiagnosticSessionControlContainer()
    ecuResetContainer = ECUResetContainer()
    rdbiContainer = ReadDataByIdentifierContainer()
    wdbiContainer = WriteDataByIdentifierContainer()
    clearDTCContainer = ClearDTCContainer()
    readDTCContainer = ReadDTCContainer()
    inputOutputControlContainer = InputOutputControlContainer()
    routineControlContainer = RoutineControlContainer()
    requestDownloadContainer = RequestDownloadContainer()
    securityAccessContainer = SecurityAccessContainer()
    requestUploadContainer = RequestUploadContainer()
    transferDataContainer = TransferDataContainer()
    transferExitContainer = TransferExitContainer()
    testerPresentContainer = TesterPresentContainer()
    sessionService_flag = False
    ecuResetService_flag = False
    rdbiService_flag = False
    wdbiService_flag = False
    securityAccess_flag = False
    clearDTCService_flag = False
    readDTCService_flag = False
    ioCtrlService_flag = False
    routineCtrlService_flag = False
    reqDownloadService_flag = False
    reqUploadService_flag = False
    transDataService_flag = False
    transExitService_flag = False
    testerPresentService_flag = False
    xmlElements = {}

    for child in root.iter():
        currTag = child.tag
        try:
            xmlElements[child.attrib['ID']] = child
        except KeyError:
            pass

    for key, value in xmlElements.items():
        if value.tag == 'DIAG-SERVICE':
            serviceId = get_serviceIdFromXmlElement(value, xmlElements)
            sdg = value.find('SDGS').find('SDG')
            humanName = ""
            for sd in sdg:
                try:
                    if sd.attrib['SI'] == 'DiagInstanceName':
                        humanName = sd.text
                except KeyError:
                    pass


            if serviceId == IsoServices.DiagnosticSessionControl:
                sessionService_flag = True

                requestFunc = DiagnosticSessionControlMethodFactory.create_requestFunction(value, xmlElements)
                diagnosticSessionControlContainer.add_requestFunction(requestFunc, humanName)

                negativeResponseFunction = DiagnosticSessionControlMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                diagnosticSessionControlContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)

                checkFunc = DiagnosticSessionControlMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                diagnosticSessionControlContainer.add_checkFunction(checkFunc, humanName)

                positiveResponseFunction = DiagnosticSessionControlMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                diagnosticSessionControlContainer.add_positiveResponseFunction(positiveResponseFunction, humanName)
                if diagnosticSessionControlContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(diagnosticSessionControlContainer)

            elif serviceId == IsoServices.EcuReset:
                ecuResetService_flag = True

                requestFunc = ECUResetMethodFactory.create_requestFunction(value, xmlElements)
                ecuResetContainer.add_requestFunction(requestFunc, humanName)

                negativeResponseFunction = ECUResetMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                ecuResetContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)

                try:
                    transmissionMode = value.attrib['TRANSMISSION-MODE']
                    if transmissionMode == "SEND-ONLY":
                        sendOnly_flag = True
                except:
                    sendOnly_flag = False

                if sendOnly_flag:
                    checkFunc = None
                    positiveResponseFunction = None
                else:
                    checkFunc = ECUResetMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                    positiveResponseFunction = ECUResetMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)

                ecuResetContainer.add_checkFunction(checkFunc, humanName)
                ecuResetContainer.add_positiveResponseFunction(positiveResponseFunction, humanName)
                if ecuResetContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(ecuResetContainer)
                pass

            elif serviceId == IsoServices.ReadDataByIdentifier:
                rdbiService_flag = True

                # The new code extends the range of functions required, in order to handle RDBI working for concatenated lists of DIDs ...
                requestFunctions = ReadDataByIdentifierMethodFactory.create_requestFunctions(value, xmlElements)
                rdbiContainer.add_requestSIDFunction(requestFunctions[0], humanName)  # ... note: this will now need to handle replication of this one!!!!
                rdbiContainer.add_requestDIDFunction(requestFunctions[1], humanName)


                negativeResponseFunction = ReadDataByIdentifierMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                rdbiContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)


                checkFunctions = ReadDataByIdentifierMethodFactory.create_checkPositiveResponseFunctions(value, xmlElements)
                rdbiContainer.add_checkSIDResponseFunction(checkFunctions[0], humanName)
                rdbiContainer.add_checkSIDLengthFunction(checkFunctions[1], humanName)
                rdbiContainer.add_checkDIDResponseFunction(checkFunctions[2], humanName)
                rdbiContainer.add_checkDIDLengthFunction(checkFunctions[3], humanName)


                positiveResponseFunction = ReadDataByIdentifierMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                rdbiContainer.add_positiveResponseFunction(positiveResponseFunction, humanName)
                
                if rdbiContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(rdbiContainer)

            elif serviceId == IsoServices.SecurityAccess:
                if isDiagServiceTransmissionOnly(value) == False:
                    requestFunction = SecurityAccessMethodFactory.create_requestFunction(value, xmlElements)
                    securityAccessContainer.add_requestFunction(requestFunction, humanName)

                    negativeResponseFunction = SecurityAccessMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                    securityAccessContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)

                    checkFunction = SecurityAccessMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                    securityAccessContainer.add_positiveResponseFunction(checkFunction, humanName)

                    securityAccess_flag = True

                    if securityAccessContainer not in UdsContainerAccess.containers:
                        UdsContainerAccess.containers.append(securityAccessContainer)

            elif serviceId == IsoServices.WriteDataByIdentifier:

                wdbiService_flag = True
                requestFunc = WriteDataByIdentifierMethodFactory.create_requestFunction(value, xmlElements)
                wdbiContainer.add_requestFunction(requestFunc, humanName)

                negativeResponseFunction = WriteDataByIdentifierMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                wdbiContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)

                checkFunc = WriteDataByIdentifierMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                wdbiContainer.add_checkFunction(checkFunc, humanName)

                positiveResponseFunction = WriteDataByIdentifierMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                wdbiContainer.add_positiveResponseFunction(positiveResponseFunction, humanName)

                if wdbiContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(wdbiContainer)

            elif serviceId == IsoServices.ClearDiagnosticInformation:
                clearDTCService_flag = True
                requestFunc = ClearDTCMethodFactory.create_requestFunction(value, xmlElements)
                clearDTCContainer.add_requestFunction(requestFunc, humanName)

                negativeResponseFunction = ClearDTCMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                clearDTCContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)

                checkFunc = ClearDTCMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                clearDTCContainer.add_checkFunction(checkFunc, humanName)

                positiveResponseFunction = ClearDTCMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                clearDTCContainer.add_positiveResponseFunction(positiveResponseFunction, humanName)

                if clearDTCContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(clearDTCContainer)

            elif serviceId == IsoServices.ReadDTCInformation:
                readDTCService_flag = True
                requestFunction, qualifier = ReadDTCMethodFactory.create_requestFunction(value, xmlElements)
                if qualifier != "":
                    readDTCContainer.add_requestFunction(requestFunction, "FaultMemoryRead"+qualifier)

                    negativeResponseFunction = ReadDTCMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                    readDTCContainer.add_negativeResponseFunction(negativeResponseFunction, "FaultMemoryRead"+qualifier)

                    checkFunction = ReadDTCMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                    readDTCContainer.add_checkFunction(checkFunction, "FaultMemoryRead"+qualifier)

                    positiveResponseFunction = ReadDTCMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                    readDTCContainer.add_positiveResponseFunction(positiveResponseFunction, "FaultMemoryRead"+qualifier)

                    if readDTCContainer not in UdsContainerAccess.containers:
                        UdsContainerAccess.containers.append(readDTCContainer)

            elif serviceId == IsoServices.InputOutputControlByIdentifier:
                ioCtrlService_flag = True
                requestFunc, qualifier  = InputOutputControlMethodFactory.create_requestFunction(value, xmlElements)
                if qualifier != "":
                    inputOutputControlContainer.add_requestFunction(requestFunc, humanName+qualifier)

                    negativeResponseFunction = InputOutputControlMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                    inputOutputControlContainer.add_negativeResponseFunction(negativeResponseFunction, humanName+qualifier)

                    checkFunc = InputOutputControlMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                    inputOutputControlContainer.add_checkFunction(checkFunc, humanName+qualifier)

                    positiveResponseFunction = InputOutputControlMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                    inputOutputControlContainer.add_positiveResponseFunction(positiveResponseFunction, humanName+qualifier)

                    if inputOutputControlContainer not in UdsContainerAccess.containers:
                        UdsContainerAccess.containers.append(inputOutputControlContainer)

            elif serviceId == IsoServices.RoutineControl:
                routineCtrlService_flag = True
                # We need a qualifier, as the human name for the start stop, and results calls are all the same, so they otherwise overwrite each other
                requestFunc, qualifier = RoutineControlMethodFactory.create_requestFunction(value, xmlElements)
                if qualifier != "":
                    routineControlContainer.add_requestFunction(requestFunc, humanName+qualifier)

                    negativeResponseFunction = RoutineControlMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                    routineControlContainer.add_negativeResponseFunction(negativeResponseFunction, humanName+qualifier)

                    checkFunc = RoutineControlMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                    routineControlContainer.add_checkFunction(checkFunc, humanName+qualifier)

                    positiveResponseFunction = RoutineControlMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                    routineControlContainer.add_positiveResponseFunction(positiveResponseFunction, humanName+qualifier)

                    if routineControlContainer not in UdsContainerAccess.containers:
                        UdsContainerAccess.containers.append(routineControlContainer)

            elif serviceId == IsoServices.RequestDownload:
                reqDownloadService_flag = True
                requestFunc = RequestDownloadMethodFactory.create_requestFunction(value, xmlElements)
                requestDownloadContainer.add_requestFunction(requestFunc, humanName)

                negativeResponseFunction = RequestDownloadMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                requestDownloadContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)

                checkFunc = RequestDownloadMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                requestDownloadContainer.add_checkFunction(checkFunc, humanName)

                positiveResponseFunction = RequestDownloadMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                requestDownloadContainer.add_positiveResponseFunction(positiveResponseFunction, humanName)

                if requestDownloadContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(requestDownloadContainer)

            elif serviceId == IsoServices.RequestUpload:
                reqUploadService_flag = True
                requestFunc = RequestUploadMethodFactory.create_requestFunction(value, xmlElements)
                requestUploadContainer.add_requestFunction(requestFunc, humanName)

                negativeResponseFunction = RequestUploadMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                requestUploadContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)

                checkFunc = RequestUploadMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                requestUploadContainer.add_checkFunction(checkFunc, humanName)

                positiveResponseFunction = RequestUploadMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                requestUploadContainer.add_positiveResponseFunction(positiveResponseFunction, humanName)

                if requestUploadContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(requestUploadContainer)

            elif serviceId == IsoServices.TransferData:
                transDataService_flag = True
                requestFunc = TransferDataMethodFactory.create_requestFunction(value, xmlElements)
                transferDataContainer.add_requestFunction(requestFunc, humanName)

                negativeResponseFunction = TransferDataMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                transferDataContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)

                checkFunc = TransferDataMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                transferDataContainer.add_checkFunction(checkFunc, humanName)

                positiveResponseFunction = TransferDataMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                transferDataContainer.add_positiveResponseFunction(positiveResponseFunction, humanName)

                if transferDataContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(transferDataContainer)

            elif serviceId == IsoServices.RequestTransferExit:
                transExitService_flag = True
                requestFunc = TransferExitMethodFactory.create_requestFunction(value, xmlElements)
                transferExitContainer.add_requestFunction(requestFunc, humanName)

                negativeResponseFunction = TransferExitMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                transferExitContainer.add_negativeResponseFunction(negativeResponseFunction, humanName)

                checkFunc = TransferExitMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                transferExitContainer.add_checkFunction(checkFunc, humanName)

                positiveResponseFunction = TransferExitMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                transferExitContainer.add_positiveResponseFunction(positiveResponseFunction, humanName)

                if transferExitContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(transferExitContainer)

            elif serviceId == IsoServices.TesterPresent:
                # Note: Tester Present is presented here as an exposed service, but it will typically not be called directly, as we'll hook it 
                # in to keep the session alive automatically if requested (details to come, but this is just getting the comms into place).
                testerPresentService_flag = True
                requestFunc = TesterPresentMethodFactory.create_requestFunction(value, xmlElements)
                testerPresentContainer.add_requestFunction(requestFunc, "TesterPresent")

                negativeResponseFunction = TesterPresentMethodFactory.create_checkNegativeResponseFunction(value, xmlElements)
                testerPresentContainer.add_negativeResponseFunction(negativeResponseFunction, "TesterPresent")

                checkFunc = TesterPresentMethodFactory.create_checkPositiveResponseFunction(value, xmlElements)
                testerPresentContainer.add_checkFunction(checkFunc, "TesterPresent")

                positiveResponseFunction = TesterPresentMethodFactory.create_encodePositiveResponseFunction(value, xmlElements)
                testerPresentContainer.add_positiveResponseFunction(positiveResponseFunction, "TesterPresent")

                if testerPresentContainer not in UdsContainerAccess.containers:
                    UdsContainerAccess.containers.append(testerPresentContainer)


    #need to be able to extract the reqId and resId
    outputEcu = Uds(ihexFile=ihexFile, **kwargs)

    # Bind any ECU Reset services that have been found
    if sessionService_flag:
        setattr(outputEcu, 'diagnosticSessionControlContainer', diagnosticSessionControlContainer)
        diagnosticSessionControlContainer.bind_function(outputEcu)

    # Bind any ECU Reset services that have been found
    if ecuResetService_flag:
        setattr(outputEcu, 'ecuResetContainer', ecuResetContainer)
        ecuResetContainer.bind_function(outputEcu)

    # Bind any rdbi services that have been found
    if rdbiService_flag:
        setattr(outputEcu, 'readDataByIdentifierContainer', rdbiContainer)
        rdbiContainer.bind_function(outputEcu)

    # Bind any security access services have been found
    if securityAccess_flag:
        setattr(outputEcu, 'securityAccessContainer', securityAccessContainer)
        securityAccessContainer.bind_function(outputEcu)

    # Bind any wdbi services have been found
    if wdbiService_flag:
        setattr(outputEcu, 'writeDataByIdentifierContainer', wdbiContainer)
        wdbiContainer.bind_function(outputEcu)

    # Bind any clear DTC services that have been found
    if clearDTCService_flag:
        setattr(outputEcu, 'clearDTCContainer', clearDTCContainer)
        clearDTCContainer.bind_function(outputEcu)

    # Bind any read DTC services that have been found
    if readDTCService_flag:
        setattr(outputEcu, 'readDTCContainer', readDTCContainer)
        readDTCContainer.bind_function(outputEcu)

    # Bind any input output control services that have been found
    if ioCtrlService_flag:
        setattr(outputEcu, 'inputOutputControlContainer', inputOutputControlContainer)
        inputOutputControlContainer.bind_function(outputEcu)

    # Bind any routine control services that have been found
    if routineCtrlService_flag:
        setattr(outputEcu, 'routineControlContainer', routineControlContainer)
        routineControlContainer.bind_function(outputEcu)

    # Bind any request download services that have been found
    if reqDownloadService_flag:
        setattr(outputEcu, 'requestDownloadContainer', requestDownloadContainer)
        requestDownloadContainer.bind_function(outputEcu)

    # Bind any request upload services that have been found
    if reqUploadService_flag:
        setattr(outputEcu, 'requestUploadContainer', requestUploadContainer)
        requestUploadContainer.bind_function(outputEcu)

    # Bind any transfer data services that have been found
    if transDataService_flag:
        setattr(outputEcu, 'transferDataContainer', transferDataContainer)
        transferDataContainer.bind_function(outputEcu)

    # Bind any transfer exit data services that have been found
    if transExitService_flag:
        setattr(outputEcu, 'transferExitContainer', transferExitContainer)
        transferExitContainer.bind_function(outputEcu)

    # Bind any tester present services that have been found
    if testerPresentService_flag:
        setattr(outputEcu, 'testerPresentContainer', testerPresentContainer)
        testerPresentContainer.bind_function(outputEcu)

    return outputEcu


if __name__ == "__main__":

    a = createUdsConnection('Bootloader.odx', 'bootloader')

    a.diagnosticSessionControl('Default Session')
    a.ecuReset('Hard Reset',suppressResponse=False)
    a.readDataByIdentifier('ECU Serial Number')
    a.writeDataByIdentifier('ECU Serial Number','ABC0011223344556')
    a.clearDTC([0xF1, 0xC8, 0x55])
    #a.readDTC(IsoReadDTCSubfunction.reportNumberOfDTCByStatusMask, DTCStatusMask=Mask.confirmedDtc + Mask.testFailedSinceLastClear)
    #a.readDTC(IsoReadDTCSubfunction.reportDTCByStatusMask, DTCStatusMask=Mask.confirmedDtc + Mask.testFailedSinceLastClear)
    #a.readDTC(IsoReadDTCSubfunction.reportDTCSnapshotIdentification, DTCMaskRecord=[0xF1, 0xC8, 0x55], DTCSnapshotRecordNumber=0x34)
    #a.readDTC(IsoReadDTCSubfunction.reportDTCSnapshotRecordByDTCNumber, DTCMaskRecord=[0xF1, 0xC8, 0x55], DTCSnapshotRecordNumber=0x34)
    #a.readDTC(IsoReadDTCSubfunction.reportDTCSnapshotRecordByRecordNumber, DTCSnapshotRecordNumber=0x34)
    #a.readDTC(IsoReadDTCSubfunction.reportDTCExtendedDataRecordByDTCNumber, DTCMaskRecord=[0xF1, 0xC8, 0x55], DTCExtendedRecordNumber=0x12)
    #a.readDTC(IsoReadDTCSubfunction.reportNumberOfDTCBySeverityMaskRecord, DTCStatusMask=Mask.confirmedDtc + Mask.testFailedSinceLastClear, DTCSeverityMaskRecord=Mask.confirmedDtc)
    #a.readDTC(IsoReadDTCSubfunction.reportDTCBySeverityMaskRecord, DTCStatusMask=Mask.confirmedDtc + Mask.testFailedSinceLastClear, DTCSeverityMaskRecord=Mask.confirmedDtc)
    #a.readDTC(IsoReadDTCSubfunction.reportSeverityInformationOfDTC, DTCMaskRecord=[0xF1, 0xC8, 0x55])
    #a.readDTC(IsoReadDTCSubfunction.reportSupportedDTC)
    #a.readDTC(IsoReadDTCSubfunction.reportFirstTestFailedDTC)
    #a.readDTC(IsoReadDTCSubfunction.reportFirstConfirmedDTC)
    #a.readDTC(IsoReadDTCSubfunction.reportMostRecentTestFailedDTC)
    #a.readDTC(IsoReadDTCSubfunction.reportMostRecentConfirmedDTC)
    #a.readDTC(IsoReadDTCSubfunction.reportMirrorMemoryDTCByStatusMask, DTCStatusMask=Mask.confirmedDtc + Mask.testFailedSinceLastClear)
    #a.readDTC(IsoReadDTCSubfunction.reportMirrorMemoryDTCExtendedDataRecordByDTCNumber, DTCMaskRecord=[0xF1, 0xC8, 0x55], DTCExtendedRecordNumber=0x12)
    #a.readDTC(IsoReadDTCSubfunction.reportNumberOfMirrorMemoryDTCByStatusMask, DTCStatusMask=Mask.confirmedDtc + Mask.testFailedSinceLastClear)
    #a.readDTC(IsoReadDTCSubfunction.reportNumberOfEmissionsRelatedOBDDTCByStatusMask, DTCStatusMask=Mask.confirmedDtc + Mask.testFailedSinceLastClear)
    #a.readDTC(IsoReadDTCSubfunction.reportEmissionsRelatedOBDDTCByStatusMask, DTCStatusMask=Mask.confirmedDtc + Mask.testFailedSinceLastClear)
    a.inputOutputControl('Booster Target Speed',IsoInputOutputControlOptionRecord.adjust,[8000])
    a.routineControl('Erase Memory',IsoRoutineControlType.startRoutine,[('memoryAddress',[0x01]),('memorySize',[0xF000])])
    a.requestDownload(FormatIdentifier=[0x00],MemoryAddress=[0x40, 0x03, 0xE0, 0x00],MemorySize=[0x00, 0x00, 0x0E, 0x56])
    #a.requestUpload(FormatIdentifier=[0x00],MemoryAddress=[0x40, 0x03, 0xE0, 0x00],MemorySize=[0x00, 0x00, 0x0E, 0x56])   # Not tested or runnable at present
    a.transferData(0x01,[0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF])
    a.transferExit([0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF])
    a.testerPresent(suppressResponse=False)
