Example #1
0
 def push_constellation(self, ecu_groups, busses, bus_connections):
     ''' this method receives the initial constellation of the 
         environment and publishes it to the handlers 
     
         Input:  ecu_groups        list        list of lists: [[ecu_list, ecu_spec],[ecu_list, ecu_spec],...]
                 busses            list        list of lists: [[bus_list, ecu_spec],[bus_list, ecu_spec],...]
                 bus_connections   list        list of lists [[bus_id, ecu_id], [bus_id, ecu_id],...]
         Output: -
     '''        
     # set eventline handlers
     for ecu in ecu_groups: 
         try:
             General().register_eventline_tags(ecu[0][0].ecuSW.comm_mod._tags)
         except:
             pass
     
     # push the constellation
     push_list = [MonitorInput([ecu_groups, busses, bus_connections], MonitorTags.CONSELLATION_INFORMATION, \
                         None, 0, None, None, None, None, None, None)]
     self._input_handler.publish(push_list, RefList())        
         
     # push initial ecu ids
     
     push_list = MonitorInput(APICore()._ecu_list_from_groups(ecu_groups), MonitorTags.ECU_ID_LIST, \
                         None, 0, None, None, None, None, None, None)
     self._input_handler.publish(push_list, RefList())   
Example #2
0
 def __init__(self, sim_env, bus_id, data_rate, avg_ecu_dist=2):
     ''' Constructor
         
         Input:    sim_env        simpy.Environment         environment in which this Bus acts
                   bus_id         string                    id of this Bus object
                   data_rate      float                     datarate of this bus
                   avg_ecu_dist   float                     average distance between two connected ECUs
             
         Output:   -                  
     '''
     AbstractCANBus.__init__(self, sim_env, bus_id, data_rate, avg_ecu_dist)
     
     # bus objects
     self.current_message = None  # current message on the bus [sender_ecu, message]
     self.set_settings()
     self.monitor_list = RefList()
     self._used_prop_times = {}
     self.gateways = []
     self.first = True
     
     # synchronization objects
     self.pot_messages = []  # gathers all potential messages that want to be sent at a certain point in time
     self.sync_1 = simpy.Store(self.sim_env, capacity=1)  # if the decision, who is allowed to sent is done this synchronizer starts the transmission
     self.sync_2 = simpy.Store(self.sim_env, capacity=1)  # if store is empty then the channel is busy
     self.sync_send = simpy.Store(self.sim_env, capacity=1)  # store that is full if sending and free else
     self.subscribers = 0  # number of ECUs waiting for the channel to be freed
         
     # project parameters
     self.SCB_GATHER_MSGS = time.SCB_GATHER_MSGS
     self.SCB_GRAB_PRIO_MSG = time.SCB_GRAB_PRIO_MSG
     self.SCB_PROPAGATION_DELAY = time.SCB_PROPAGATION_DELAY
     self.SCB_SENDING_TIME = time.SCB_SENDING_TIME
     self.SCB_WRITE_TO_TRANSCEIVER_BUFFER = time.SCB_WRITE_TO_TRANSCEIVER_BUFFER
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
        
        # add tags
        self._tags = ["AUTH_SEND_TIME_BEFORE_ENCRYPTION", "AUTH_SEND_TIME_AFTER_ENCRYPTION", "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"]        
    
        # NEW: Add key pair - public and private
        assymetric_encryption_algorithm = AsymAuthMechEnum.RSA
        assymetric_encryption_key_length = AuKeyLengthEnum.bit_512
        assymetric_encryption_option = 65537
        self.priv_key, self.pub_key = encryption_tools.asy_get_key_pair(assymetric_encryption_algorithm, assymetric_encryption_key_length, assymetric_encryption_option)
        PublicKeyManager().add_key(self._ecu_id, self.pub_key) # make public key available to everybody

        # Create a certificate from root L3 along L3 L31 L311
        [certificate, root_certificates_to_verify_this_certificate, priv_key] = GeneralSpecPreset().certificate_manager.generate_valid_ecu_cert(self._ecu_id, CAEnum.CA_L311, 0, float('inf'))
        self.my_certificate = certificate
        self.my_root_certificates = root_certificates_to_verify_this_certificate
Example #4
0
 def __init__(self, input_handler_chain=False):   
     QObject.__init__(self)                
     
     self.sim_env = None
     self.monitored = []
     self.t_period = 1
     self.sample_time = 0.5  # Sample time in which the information is read from the objects
     self._show_time = True
     self._last_run_elements = RefList()
     self._init_input_handlers(input_handler_chain)
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        self._have_session_key = False
        self._session_key = None
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
        
        # add tags
        self._tags = ["AUTH_SEND_TIME_BEFORE_ENCRYPTION", "AUTH_SEND_TIME_AFTER_ENCRYPTION", "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"]        
    
        # NEW: Add key pair - public and private
        assymetric_encryption_algorithm = AsymAuthMechEnum.RSA
        assymetric_encryption_key_length = AuKeyLengthEnum.bit_512
        assymetric_encryption_option = 65537
        self.priv_key, self.pub_key = encryption_tools.asy_get_key_pair(assymetric_encryption_algorithm, assymetric_encryption_key_length, assymetric_encryption_option)
        PublicKeyManager().add_key(self._ecu_id, self.pub_key) # make public key available to everybody

        # Create a certificate from root L3 along L3 L31 L311
        [certificate, root_certificates_to_verify_this_certificate, self.certificate_private_key] = GeneralSpecPreset().certificate_manager.generate_valid_ecu_cert(self._ecu_id, CAEnum.CA_L311, 0, float('inf'))
        self.my_certificate = certificate
        self.my_root_certificates = root_certificates_to_verify_this_certificate
Example #6
0
    def __init__(self, export_options=False, file_path=False, ignore=False):
        AbstractInterpreter.__init__(self, export_options, file_path)
        if ignore: return

        # CSV Output
        self.ecu_ids = RefList()
        self.first = True
Example #7
0
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()

        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
        
        # add tags
        self._tags = ["AUTH_SEND_TIME_BEFORE_ENCRYPTION", "AUTH_SEND_TIME_AFTER_ENCRYPTION", "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"]        
    
        # NEW: Add key pair - public and private
        assymetric_encryption_algorithm = AsymAuthMechEnum.RSA
        assymetric_encryption_key_length = AuKeyLengthEnum.bit_512
        assymetric_encryption_option = 65537
        self.priv_key, self.pub_key = encryption_tools.asy_get_key_pair(assymetric_encryption_algorithm, assymetric_encryption_key_length, assymetric_encryption_option)
        PublicKeyManager().add_key(self._ecu_id, self.pub_key) # make public key available to everybody
        
        self.first_message = False
Example #9
0
 def __init__(self, input_handler_chain=False):   
     QObject.__init__(self)                
     
     self.sim_env = None
     self.monitored = []
     self.t_period = 1
     self.sample_time = 0.5  # Sample time in which the information is read from the objects
     self._show_time = True
     self._last_run_elements = RefList()
     self._init_input_handlers(input_handler_chain)
Example #10
0
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()

        # initialize
        self._init_layers(self.sim_env, self.MessageClass)

        # add tags
        self._tags = [
            "AUTH_SEND_TIME_BEFORE_ENCRYPTION",
            "AUTH_SEND_TIME_AFTER_ENCRYPTION",
            "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION",
            "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"
        ]

        # NEW: Add key pair - public and private
        assymetric_encryption_algorithm = AsymAuthMechEnum.RSA
        assymetric_encryption_key_length = AuKeyLengthEnum.bit_512
        assymetric_encryption_option = 65537
        self.priv_key, self.pub_key = encryption_tools.asy_get_key_pair(
            assymetric_encryption_algorithm, assymetric_encryption_key_length,
            assymetric_encryption_option)
        PublicKeyManager().add_key(
            self._ecu_id,
            self.pub_key)  # make public key available to everybody

        self.first_message = False
Example #11
0
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()

        # initialize
        self._init_layers(self.sim_env, self.MessageClass)

        # add tags
        self._tags = [
            "AUTH_SEND_TIME_BEFORE_ENCRYPTION",
            "AUTH_SEND_TIME_AFTER_ENCRYPTION",
            "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION",
            "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"
        ]
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
        
        # add tags
        self._tags = ["AUTH_SEND_TIME_BEFORE_ENCRYPTION", "AUTH_SEND_TIME_AFTER_ENCRYPTION", "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"]        
    def __init__(self, sim_env, bus_id, data_rate, avg_ecu_dist=2):
        ''' Constructor
            
            Input:    sim_env        simpy.Environment         environment in which this Bus acts
                      bus_id         string                    id of this Bus object
                      data_rate      float                     datarate of this bus
                      avg_ecu_dist   float                     average distance between two connected ECUs
                
            Output:   -                  
        '''
        AbstractCANBus.__init__(self, sim_env, bus_id, data_rate, avg_ecu_dist)
        
        # bus objects
        self.current_message = None  # current message on the bus [sender_ecu, message]
        self.set_settings()
        self.monitor_list = RefList()
        self._used_prop_times = {}
        self.gateways = []
        self.first = True
        
        # synchronization objects
        self.pot_messages = []  # gathers all potential messages that want to be sent at a certain point in time
        self.sync_1 = simpy.Store(self.sim_env, capacity=1)  # if the decision, who is allowed to sent is done this synchronizer starts the transmission
        self.sync_2 = simpy.Store(self.sim_env, capacity=1)  # if store is empty then the channel is busy
        self.subscribers = 0  # number of ECUs waiting for the channel to be freed
        self.current_message_length_bit = 0
            
        # project parameters
        self.SCB_GATHER_MSGS = time.SCB_GATHER_MSGS
        self.SCB_GRAB_PRIO_MSG = time.SCB_GRAB_PRIO_MSG
        self.SCB_PROPAGATION_DELAY = time.SCB_PROPAGATION_DELAY
        self.SCB_SENDING_TIME = time.SCB_SENDING_TIME
        self.SCB_WRITE_TO_TRANSCEIVER_BUFFER = time.SCB_WRITE_TO_TRANSCEIVER_BUFFER

        # ECUs Datalink layers willing to send
        self._willing_dll = []
Example #15
0
class Monitor(QObject):    
    
    def __init__(self, input_handler_chain=False):   
        QObject.__init__(self)                
        
        self.sim_env = None
        self.monitored = []
        self.t_period = 1
        self.sample_time = 0.5  # Sample time in which the information is read from the objects
        self._show_time = True
        self._last_run_elements = RefList()
        self._init_input_handlers(input_handler_chain)
        
    def set_handler_chain(self, handler_chain):
        ''' this method allows the user to specify which
            information needs to be parsed '''
        self._input_handler = handler_chain

    def show_monitor_time(self, bool_show):
        ''' output the monitor publish time on every call'''
        self._show_time = bool_show 

    def _init_input_handlers(self, input_handler_chain):
        '''
        per default all input handlers are enabled. If the user wishes
        to have only some handlers parsing information a custom 
        InputHandlerChain has to be passed to the constructor
        '''
        
        if input_handler_chain:
            self._input_handler = input_handler_chain.handler()
        else: 
            self._input_handler = InputHandlerChain()
            cp = CheckpointHandler();self._input_handler.add_handler(cp)
            self._input_handler.add_handler(CanBusHandler())
            self._input_handler.add_handler(BufferHandler())
            el = EventlineHandler();self._input_handler.add_handler(el); General().eventline_handler = el
            self._input_handler.add_handler(ConstellationHandler())  
            self._input_handler = self._input_handler.handler()
           
    @ try_ex
    def push_constellation(self, ecu_groups, busses, bus_connections):
        ''' this method receives the initial constellation of the 
            environment and publishes it to the handlers 
        
            Input:  ecu_groups        list        list of lists: [[ecu_list, ecu_spec],[ecu_list, ecu_spec],...]
                    busses            list        list of lists: [[bus_list, ecu_spec],[bus_list, ecu_spec],...]
                    bus_connections   list        list of lists [[bus_id, ecu_id], [bus_id, ecu_id],...]
            Output: -
        '''        
        # set eventline handlers
        for ecu in ecu_groups: 
            try:
                General().register_eventline_tags(ecu[0][0].ecuSW.comm_mod._tags)
            except:
                pass
        
        # push the constellation
        push_list = [MonitorInput([ecu_groups, busses, bus_connections], MonitorTags.CONSELLATION_INFORMATION, \
                            None, 0, None, None, None, None, None, None)]
        self._input_handler.publish(push_list, RefList())        
            
        # push initial ecu ids
        
        push_list = MonitorInput(APICore()._ecu_list_from_groups(ecu_groups), MonitorTags.ECU_ID_LIST, \
                            None, 0, None, None, None, None, None, None)
        self._input_handler.publish(push_list, RefList())   
            
    
    def subscribe(self, obj, func_name, handlers=None):
        ''' in the specified time interval data will be 
            passed to the function func
            
            Subscribe not to the monitor but to specified handlers of it
            e.g. a GUI
        '''
        handler = self._input_handler
        while handler != None:
            if handler.__class__ in handlers:
                handler.subscribe(obj, func_name)
                print("object: %s subscribed to %s" % (obj, handler))
            handler = handler.next
        
    
    def connect(self, obj):
        ''' connects an arbitrary object to 
            the monitor. Then this object can pass 
            information to the Monitor via the monitor_update method
            
            A connection is only possible if the object is monitorable (i.e.
            has all necessary methods)'''
        try:
            if obj not in self.monitored:                
                # Check if this method is monitorable
                obj.monitor_update()
                self.monitored.append(obj)
                obj.set_monitor(self)            
        except:
            pass
           
    
    def monitor(self):
        ''' Gathering of information'''
        
        while True:
            ''' either invoked once in a certain time frame
                or invoked via force command '''
            try:
                
                ''' 1. Update all values -> write it to list '''
                for obj in self.monitored:
                    monitor_input_lst = obj.monitor_update()                    
                    self._last_run_elements.join(monitor_input_lst)
    
                ''' 2. Timeout'''
                yield self.sim_env.timeout(self.sample_time)
            except simpy.Interrupt:
                pass
            
    
    def monitor_publish(self):
        ''' 
            information is captured continuously and 
            send to the connected methods once in a while             
        '''
        
        while True:
            try:
                
                # get current time                
                if self._show_time:              
                    print("Current time %s " % self.sim_env.now)
                
                # if there is new stuff publish it now 
                self._input_handler.publish(self.sim_env.now, self._last_run_elements)  # calls all publishers in a chain
                
                self._last_run_elements.clear()
                
            except:
                ECULogger().log_traceback()
            
            # wait
            yield self.sim_env.timeout(self.t_period)
                    
     
    def set_monitor_env(self, val):
        self.sim_env = val
    
    
    def set_sample_time(self, t_sample):
        self.sample_time = t_sample
    
    
    def set_period(self, t_period):
        self.t_period = t_period
    
    
    def set_sim_process(self, sim_process):
        self.sim_process = sim_process
Example #16
0
class Monitor(QObject):    
    
    def __init__(self, input_handler_chain=False):   
        QObject.__init__(self)                
        
        self.sim_env = None
        self.monitored = []
        self.t_period = 1
        self.sample_time = 0.5  # Sample time in which the information is read from the objects
        self._show_time = True
        self._last_run_elements = RefList()
        self._init_input_handlers(input_handler_chain)
        
    def set_handler_chain(self, handler_chain):
        ''' this method allows the user to specify which
            information needs to be parsed '''
        self._input_handler = handler_chain

    def show_monitor_time(self, bool_show):
        ''' output the monitor publish time on every call'''
        self._show_time = bool_show 

    def _init_input_handlers(self, input_handler_chain):
        '''
        per default all input handlers are enabled. If the user wishes
        to have only some handlers parsing information a custom 
        InputHandlerChain has to be passed to the constructor
        '''
        
        if input_handler_chain:
            self._input_handler = input_handler_chain.handler()
        else: 
            self._input_handler = InputHandlerChain()
            self._input_handler.add_handler(CheckpointHandler())
            self._input_handler.add_handler(CanBusHandler())
            self._input_handler.add_handler(BufferHandler())
            self._input_handler.add_handler(EventlineHandler())  
            self._input_handler.add_handler(ConstellationHandler())  
            self._input_handler = self._input_handler.handler()
           
    @ try_ex
    def push_constellation(self, ecu_groups, busses, bus_connections):
        ''' this method receives the initial constellation of the 
            environment and publishes it to the handlers 
        
            Input:  ecu_groups        list        list of lists: [[ecu_list, ecu_spec],[ecu_list, ecu_spec],...]
                    busses            list        list of lists: [[bus_list, ecu_spec],[bus_list, ecu_spec],...]
                    bus_connections   list        list of lists [[bus_id, ecu_id], [bus_id, ecu_id],...]
            Output: -
        '''        
        # push the constellation
        push_list = [MonitorInput([ecu_groups, busses, bus_connections], MonitorTags.CONSELLATION_INFORMATION, \
                            None, 0, None, None, None, None, None, None)]
        self._input_handler.publish(push_list, RefList())        
            
        # push initial ecu ids
        
        push_list = MonitorInput(APICore()._ecu_list_from_groups(ecu_groups), MonitorTags.ECU_ID_LIST, \
                            None, 0, None, None, None, None, None, None)
        self._input_handler.publish(push_list, RefList())   
            
    
    def subscribe(self, obj, func_name, handlers=None):
        ''' in the specified time interval data will be 
            passed to the function func
            
            Subscribe not to the monitor but to specified handlers of it
            e.g. a GUI
        '''
        handler = self._input_handler
        while handler != None:
            if handler.__class__ in handlers:
                handler.subscribe(obj, func_name)
                print("object: %s subscribed to %s" % (obj, handler))
            handler = handler.next
        
    
    def connect(self, obj):
        ''' connects an arbitrary object to 
            the monitor. Then this object can pass 
            information to the Monitor via the monitor_update method
            
            A connection is only possible if the object is monitorable (i.e.
            has all necessary methods)'''
        try:
            if obj not in self.monitored:                
                # Check if this method is monitorable
                obj.monitor_update()
                self.monitored.append(obj)
                obj.set_monitor(self)            
        except:
            pass
           
    
    def monitor(self):
        ''' Gathering of information'''
        
        while True:
            ''' either invoked once in a certain time frame
                or invoked via force command '''
            try:
                
                ''' 1. Update all values -> write it to list '''
                for obj in self.monitored:
                    monitor_input_lst = obj.monitor_update()                    
                    self._last_run_elements.join(monitor_input_lst)
    
                ''' 2. Timeout'''
                yield self.sim_env.timeout(self.sample_time)
            except simpy.Interrupt:
                pass
            
    
    def monitor_publish(self):
        ''' 
            information is captured continuously and 
            send to the connected methods once in a while             
        '''
        
        while True:
            try:
                
                # get current time                
                if self._show_time:              
                    print("Current time %s " % self.sim_env.now)
                
                # if there is new stuff publish it now 
                self._input_handler.publish(self.sim_env.now, self._last_run_elements)  # calls all publishers in a chain
                
                self._last_run_elements.clear()
                
            except:
                ECULogger().log_traceback()
            
            # wait
            yield self.sim_env.timeout(self.t_period)
                    
     
    def set_monitor_env(self, val):
        self.sim_env = val
    
    
    def set_sample_time(self, t_sample):
        self.sample_time = t_sample
    
    
    def set_period(self, t_period):
        self.t_period = t_period
    
    
    def set_sim_process(self, sim_process):
        self.sim_process = sim_process
class MyProtocolCommModule(AbstractCommModule):
    
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
    
    def receive_msg(self):
        ''' receives messages via the tesla mechanism after
            they where authenticated. Then messages that were 
            authenticated are buffered in _messages_to_return
            and returned to higher layers sequentially on each 
            call of this method
            
            Input:     -
            Output:    message_data    object/string/...    message that was received
                       message_id      integer              id of received message
                       
            NOTE: 
                Accessing sender of a message 
                    -> USE: message_data.sender_id
                Accessing message_id of the received message 
                    -> USE: message_id
                Accessing message content
                    -> USE: message_data.get()
                
                message_data is of type SegData                
        '''

        while True:
                        
            # receive from lower layer
            [message_id, message_data] = yield self.sim_env.process(self.transp_lay.receive_msg())        
            
            # do something here e.g. if message_id = Handshake request received here, 
            # -> send something as response e.g. 
            # PLACE CODE HERE
            print("\n\nRECEIVER\nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + self._ecu_id + "\nReceived message:\n - ID: " + str(message_id) +"\n - Content: " + message_data.get())
        
        # push to higher layer
        return [message_id, message_data]

    def send_msg(self, sender_id, message_id, message):
        ''' this  method receives the message from the application
            layer and transmits it further to the transport layer
            or if required handles the security communication

            Input:  sender_id    string        ID of the ECU that wants to send the message
                    message_id   integer       identifier of the message that is to be sent
                    message      object        message that will be sent
            Output: -
        '''        
        # do something with received message
        # PLACE CODE HERE        
        print("\n\nSENDER - \nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + sender_id + "\nSending message:\n - ID: " + str(message_id)+"\n - Content: " + message.get())
        
        # Send message - here send your message with your message_id
        yield  self.sim_env.process(self.transp_lay.send_msg(sender_id, message_id, message))

            
    def _init_layers(self, sim_env, MessageClass):
        ''' Initializes the software layers 
            
            Input:  sim_env                        simpy.Environment        environment of this component                      
                    MessageClass                   AbstractBusMessage       class of the messages  how they are sent on the CAN Bus
            Output: -                   
        '''
        
        # create layers
        self.physical_lay = StdPhysicalLayer(sim_env)         
        self.datalink_lay = StdDatalinkLayer(sim_env) 
        self.transp_lay = SegmentTransportLayer(sim_env, MessageClass)
   
        # interconnect layers             
        self.datalink_lay.physical_lay = self.physical_lay        
        self.transp_lay.datalink_lay = self.datalink_lay           
        
        
    @property
    def ecu_id(self):
        return self._ecu_id
               
    @ecu_id.setter    
    def ecu_id(self, value):
        self._ecu_id = value          

    def monitor_update(self):
        ''' updates the monitor connected to this ecu
            
            Input:    -
            Output:   monitor_list    RefList    list of MonitorInputs
        '''
        items_1 = len(self.transp_lay.datalink_lay.controller.receive_buffer.items)
        items_2 = self.transp_lay.datalink_lay.transmit_buffer_size
        
        G().mon(self.monitor_list, MonitorInput(items_1, MonitorTags.BT_ECU_RECEIVE_BUFFER, self._ecu_id, self.sim_env.now))
        G().mon(self.monitor_list, MonitorInput(items_2, MonitorTags.BT_ECU_TRANSMIT_BUFFER, self._ecu_id, self.sim_env.now))
        
        self.monitor_list.clear_on_access()  # on the next access the list will be cleared        
        return self.monitor_list.get()
class MyProtocolCommModule(AbstractCommModule):
    
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
        
        # add tags
        self._tags = ["AUTH_SEND_TIME_BEFORE_ENCRYPTION", "AUTH_SEND_TIME_AFTER_ENCRYPTION", "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"]        
    
        # NEW: Add key pair - public and private
        assymetric_encryption_algorithm = AsymAuthMechEnum.RSA
        assymetric_encryption_key_length = AuKeyLengthEnum.bit_512
        assymetric_encryption_option = 65537
        self.priv_key, self.pub_key = encryption_tools.asy_get_key_pair(assymetric_encryption_algorithm, assymetric_encryption_key_length, assymetric_encryption_option)
        PublicKeyManager().add_key(self._ecu_id, self.pub_key) # make public key available to everybody
        
        self.first_message = False
        
    def receive_msg(self):
        
        while True:
                        
            # receive from lower layer
            [message_id, message_data] = yield self.sim_env.process(self.transp_lay.receive_msg())        
        
            # receiver information    
            print("\n\nRECEIVER\nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + self._ecu_id + "\nReceived message:\n - ID: " + str(message_id))
            
            # Assume it takes 0.5 seconds to e.g. decrypt this message
            uid = uuid.uuid4()
            # BEFORE PROCESSING
            print("\nECU "+ str(self._ecu_id) +"Time before message received: "+ str(self.sim_env.now))
            G().mon(self.monitor_list, MonitorInput([], "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message_data.get(), message_data.padded_size, 432, uid))           
            
            
            
            ''' Perform asymmetric decryption of the incoming message using its public key, e.g. lasting 0.5 seconds'''
            # get the public key of the sender
            senders_public_key = PublicKeyManager().get_key(message_data.sender_id)
            # use this key for decryption - also checks if this key is still valid
            received_cipher_message = message_data.get()
            clear_message = encryption_tools.asy_decrypt(received_cipher_message, senders_public_key, self.sim_env.now)            
            [received_timestamp, received_hashed_message, received_message] = clear_message
            yield self.sim_env.timeout(0.5)
            
            
            ''' Perform symmetric decryption, e.g. takes 0.02 seconds'''
            [received_symmetric_key, received_symmetrically_encrypted_message] = received_message
            received_clear_message = encryption_tools.sym_decrypt(received_symmetrically_encrypted_message, received_symmetric_key)
            yield self.sim_env.timeout(0.02)
            
            ''' Verify received hash of the message, e.g. takes 0.01 second '''
            hashed_message = HashedMessage(str(received_message), HashMechEnum.MD5) 
            is_same_hash = hashed_message.same_hash(received_hashed_message)
            yield self.sim_env.timeout(0.01)
            
            # AFTER PROCESSING
            print("\nECU "+ str(self._ecu_id) +"Time after message received: "+ str(self.sim_env.now))            
            G().mon(self.monitor_list, MonitorInput([], "AUTH_RECEIVE_TIME_AFTER_DECRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message_data.get(), message_data.padded_size, 432, uid))
            
        # push to higher layer
        return [message_id, received_clear_message]

    def send_msg(self, sender_id, message_id, message):
        # Sender information

        print("\n\nSENDER - \nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + sender_id + "\nSending message:\n - ID: " + str(message_id)+"\n - Content: " + message.get())
                
        # Message to be send 
        print("\nSize of the message we want to send: "+ str(message.padded_size))
        print("\nContent of the message: "+ str(message.get()))
        
        # Assume it takes 0.2 seconds to e.g. encrypt this message
        uid = uuid.uuid4()
        # BEFORE PROCESSING
        print("\nECU "+ str(self._ecu_id) +"Time before message sent: "+ str(self.sim_env.now))
        G().mon(self.monitor_list, MonitorInput([], "AUTH_SEND_TIME_BEFORE_ENCRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message.get(), message.padded_size, 432, uid))
        
               
        ''' Perform symmetric encryption (and key generation): send the message encrypted with a created key
        which e.g. takes 0.01 second'''
        algorithm = SymAuthMechEnum.AES
        key_length = AuKeyLengthEnum.bit_128
        algorithm_mode = SymAuthMechEnum.CBC
        sym_key = encryption_tools.sym_get_key(algorithm, key_length, algorithm_mode)
        # encrypt the message with the symmetric key we just created
        clear_message = message.get()
        cipher = encryption_tools.sym_encrypt(clear_message, sym_key)
        sym_cipher_message = [sym_key, cipher]
        yield self.sim_env.timeout(0.01)
        
        ''' Hash the message we want to send and send the hash with the message, e.g. takes 0.01 second '''
        hashed_message = HashedMessage(str(sym_cipher_message), HashMechEnum.MD5) 
        yield self.sim_env.timeout(0.01)
        
        ''' Perform asymmetric encryption: Encrypt my private key which takes e.g. 0.2 seconds'''
        timestamp =  self.sim_env.now  
        encrypted_size = 50 # byte - usualy calculated from the size of the original message and the encryption algorithm
        clear_text_of_message = [timestamp, hashed_message, sym_cipher_message]
        cipher_message = encryption_tools.asy_encrypt(clear_text_of_message, self.priv_key)
        cipher_message.valid_till = timestamp + 5 # add optional validity (e.g. 5 seconds)
        wrapped_cipher_message = SegData(cipher_message, encrypted_size)  
        yield self.sim_env.timeout(0.2)
        
        
        # AFTER PROCESSING
        G().mon(self.monitor_list, MonitorInput([], "AUTH_SEND_TIME_AFTER_ENCRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message.get(), message.padded_size, 432, uid))
        print("\nECU "+ str(self._ecu_id) +"Time after message sent: "+ str(self.sim_env.now))

        # Send message - here send your message with your message_id
        yield  self.sim_env.process(self.transp_lay.send_msg(sender_id, message_id, wrapped_cipher_message))

            
    def _init_layers(self, sim_env, MessageClass):
        ''' Initializes the software layers 
            
            Input:  sim_env                        simpy.Environment        environment of this component                      
                    MessageClass                   AbstractBusMessage       class of the messages  how they are sent on the CAN Bus
            Output: -                   
        '''
        
        # create layers
        self.physical_lay = StdPhysicalLayer(sim_env)         
        self.datalink_lay = StdDatalinkLayer(sim_env) 
        self.transp_lay = SegmentTransportLayer(sim_env, MessageClass)
   
        # interconnect layers             
        self.datalink_lay.physical_lay = self.physical_lay        
        self.transp_lay.datalink_lay = self.datalink_lay           
        
        
    @property
    def ecu_id(self):
        return self._ecu_id
               
    @ecu_id.setter    
    def ecu_id(self, value):
        self._ecu_id = value          

    def monitor_update(self):
        ''' updates the monitor connected to this ecu
            
            Input:    -
            Output:   monitor_list    RefList    list of MonitorInputs
        '''
        # register Monitoring tags to track
        #G().register_eventline_tags(self._tags)
        
        items_1 = len(self.transp_lay.datalink_lay.controller.receive_buffer.items)
        items_2 = self.transp_lay.datalink_lay.transmit_buffer_size
        
        G().mon(self.monitor_list, MonitorInput(items_1, MonitorTags.BT_ECU_RECEIVE_BUFFER, self._ecu_id, self.sim_env.now))
        G().mon(self.monitor_list, MonitorInput(items_2, MonitorTags.BT_ECU_TRANSMIT_BUFFER, self._ecu_id, self.sim_env.now))
        
        self.monitor_list.clear_on_access()  # on the next access the list will be cleared        
        return self.monitor_list.get()
Example #19
0
class RapidCANBus(AbstractCANBus):
    '''
    This class implements a CAN Bus that actively
    pulls messages from the ECUs buffers
    '''
    def __init__(self, sim_env, bus_id, data_rate, avg_ecu_dist=2):
        ''' Constructor
            
            Input:    sim_env        simpy.Environment         environment in which this Bus acts
                      bus_id         string                    id of this Bus object
                      data_rate      float                     datarate of this bus
                      avg_ecu_dist   float                     average distance between two connected ECUs
                
            Output:   -                  
        '''
        AbstractCANBus.__init__(self, sim_env, bus_id, data_rate, avg_ecu_dist)

        # bus objects
        self.current_message = None  # current message on the bus [sender_ecu, message]
        self.set_settings()
        self.monitor_list = RefList()
        self._used_prop_times = {}
        self.gateways = []
        self.first = True

        # synchronization objects
        self.pot_messages = [
        ]  # gathers all potential messages that want to be sent at a certain point in time
        self.sync_1 = simpy.Store(
            self.sim_env, capacity=1
        )  # if the decision, who is allowed to sent is done this synchronizer starts the transmission
        self.sync_2 = simpy.Store(
            self.sim_env,
            capacity=1)  # if store is empty then the channel is busy
        self.subscribers = 0  # number of ECUs waiting for the channel to be freed
        self.current_message_length_bit = 0

        # project parameters
        self.SCB_GATHER_MSGS = time.SCB_GATHER_MSGS
        self.SCB_GRAB_PRIO_MSG = time.SCB_GRAB_PRIO_MSG
        self.SCB_PROPAGATION_DELAY = time.SCB_PROPAGATION_DELAY
        self.SCB_SENDING_TIME = time.SCB_SENDING_TIME
        self.SCB_WRITE_TO_TRANSCEIVER_BUFFER = time.SCB_WRITE_TO_TRANSCEIVER_BUFFER

        # ECUs Datalink layers willing to send
        self._willing_dll = []

    def monitor_update(self):
        ''' returns the input for the monitor 
        
            Input:    -
            Output:   monitor_list    list    List of MonitorInput objects
        '''

        self.monitor_list.clear_on_access(
        )  # on the next access the list will be cleared
        return self.monitor_list.get()

    def release_willing(self, dll):
        ''' remove the ecus that are not willing to send
            anymore
            
            Input:    dll    AbstractDataLinkLayer    Datalink layer of the ECU that is not willing to send anymore
            Output:   -  
        '''
        self._willing_dll.remove(dll)

    def add_willing(self, dll):
        ''' add a Datalinklayer of an ECU that is willing to send
        
            Input:    dll    AbstractDataLinkLayer    Datalink layer of the ECU that is willing to send
            Output:   -  
        '''
        if dll not in self._willing_dll:
            self._willing_dll.append(dll)

    def notify_bus(self):
        ''' When the bus is empty it is set to a sleep mode that waits
            until the ecu notifies it. Using this method the ECU does so.
            Thus the Bus will only be active when any ecu sends.
            
            Input:  -
            Output: -
        '''
        if self.current_message != None: return
        else: self.sync_1.put(True)

    def process(self):
        ''' Constantly pull messages from all ECUs that are connected. Once the 
            Bus is done with one message it pulls the next message from the 
            connected ECUs.
            
            Input:   -
            Output:  -            
        '''
        stp = 0
        while True:

            # print time
            t = self.sim_env.now
            if t > stp:
                #                 print(self.sim_env.now)
                stp += 0.5

            # check which ECU sends
            current_minimum = float("inf")
            index = 0
            for dll in self._willing_dll:
                val = dll.first_queue_identifier()
                if val < current_minimum and val != False:
                    current_minimum = index
                index += 1

            # no ECU wiling to send: wait for notify
            if current_minimum == float("inf"):
                yield self.sync_1.get()
            else:
                self.current_message = self._willing_dll[
                    current_minimum].controller.transmit_buffer.get().message
                self.current_message_length_bit = self.current_message.msg_length_in_bit

            # transmit message
            if self.current_message != None:

                # monitor start
                monitor_note = self._monitor_transmission_start()

                # write to buffer
                yield self.sim_env.process(
                    self._wait_transmission_time_and_buffer())
                self._reset_transmission()

                # monitor end
                self._monitor_transmission_end(monitor_note)

                # if ecus buffer is now empty remove from willing
                if (len(self._willing_dll[current_minimum].controller.
                        transmit_buffer.queue) == 0):
                    self.release_willing(self._willing_dll[current_minimum])

    def set_settings(self):
        ''' sets the initial setting association between the settings variables
            and the actual parameter
        
            Input:   -
            Output:  -
        '''
        self.settings = {}

        # parameter
        self.settings['t_gather_msg'] = 'SCB_GATHER_MSGS'
        self.settings['t_grab_prio_msg'] = 'SCB_GRAB_PRIO_MSG'
        self.settings['t_propagation_delay'] = 'SCB_PROPAGATION_DELAY'
        self.settings['t_sending_time'] = 'SCB_SENDING_TIME'
        self.settings[
            't_write_to_transceiver_buffer'] = 'SCB_WRITE_TO_TRANSCEIVER_BUFFER'

    def wait_until_free(self):
        ''' when the channel is busy some ECUs can start this method in
            a simpy process. Once the channel is free this process ends 
            and the next ECU can start it's transmission             
            technically:
            count number of waiting processes and notifies them all once the channel is free
            
            Input:    -
            Output    -
        '''
        # add subscriber
        self.subscribers += 1
        yield self.sync_2.get()

        # release all receivers
        while self.subscribers > 1:

            self.sync_2.put(True)
            self.subscribers -= 1
        self.subscribers = 0

    def _extract_transmission_times(self):
        ''' calculates the time the current transmission takes
            
            Input:       -
            Output: t_propagation:    float    time it takes to propagate the message
                    t_sending         float    time it takes to send the message
        '''

        t_propagation = time.call(
            self.SCB_PROPAGATION_DELAY, self.avg_dist_between_ecus
        )  # either constant or calculated depending on config
        t_sending = time.call(
            self.SCB_SENDING_TIME, self.current_message_length_bit,
            proj.BUS_ECU_DATARATE
        )  # either constant or calculated depending on config

        return t_propagation, t_sending

    def _gateway_sends(self, ecu):
        ''' if gateway is the sender let it continue 
            and reset the message state
            
            Input:  ecu     AbstractECU    current ECU that sends the message            
            Output: bool    boolean        True if the message was sent by this ECU
        '''
        try:
            if ecu.ecu_id in self.current_message.gw_id:  # send message back and forth send could lead to errors: need to reset gw_id list
                return True
        except:
            return False
        return False

    def _get_highest_priority_msg(self, message_list):
        ''' returns the message with the highest priority
            
            Input:     message_list    list        list of messages
            Output:    message         object      message with highest priority (lowest message id)
        '''
        min_val = float("inf")
        message = None
        for cur_message in message_list:
            if min_val > cur_message.message_identifier:
                min_val = cur_message.message_identifier
                message = cur_message
        return message

    def _grab_highest_priority(self):
        ''' note the time it takes to select the message with
            the highest priority
        
            Input:     -
            Output:    -            
        '''
        if self.SCB_GRAB_PRIO_MSG != 0:
            G().to_t(self.sim_env, self.SCB_GRAB_PRIO_MSG, 'SCB_GRAB_PRIO_MSG',
                     self.__class__.__name__, self)
            return True
        return False

    def _monitor_transmission_start(self):
        ''' notes the start time when this message was put on the bus
            
            Input:  -
            Output: -
        '''
        # extract information
        uid = uuid.uuid4()
        tag = MonitorTags.CB_PROCESSING_MESSAGE
        c_id = self.comp_id
        sender_id = self.current_message.sender_id
        msg_id = self.current_message.message_identifier
        msg_uid = self.current_message.data.unique_id
        data = self.current_message.data.get()

        # extract further information
        msg = self.current_message
        size = self.current_message_length_bit / 8
        self.current_message.data.unique_id = msg_uid

        # send to monitor
        G().mon(
            self.monitor_list,
            MonitorInput(data, tag, c_id, self.sim_env.now, sender_id, msg_id,
                         msg, size, msg_id, uid.hex))
        return data, c_id, sender_id, msg_id, msg, size, uid

    def _monitor_transmission_end(self, mon_out):
        ''' notes the end time when this message was put on the bus
            
            Input:  -
            Output: -
        '''
        G().mon(self.monitor_list, MonitorInput(mon_out[0], MonitorTags.CB_DONE_PROCESSING_MESSAGE, \
                                                mon_out[1], self.sim_env.now, mon_out[2], mon_out[3], \
                                                mon_out[4], mon_out[5], -1, mon_out[6].hex))

    def _push_to_receivers(self):
        ''' writes the current message to all ecus that 
            are connected to this Bus
        
            Input:       -
            Output:      -
        '''
        # get gateways
        if self.first:
            self.gateways = [
                itm.ecu_id for itm in self.connected_ecus
                if isinstance(itm.ecu_id, UUID)
            ]
            self.first = False

        # send only to receivers
        if General(
        ).send_only_to_receivers and self.current_message.message_identifier not in can_registration.AUTH_MESSAGES:

            run_list = General().sender_receiver_map[
                self.current_message.sender_id][
                    self.current_message.message_identifier] + self.gateways

            for ecu in self.connected_ecus:

                if ecu.ecu_id not in run_list:
                    continue

                # Gateway:avoid sending to itself (loops)
                if self._gateway_sends(ecu): continue

                # ECU: avoid sending to itself
                if (ecu.ecu_id != self.current_message.sender_id):
                    self.current_message.current_bus = self.comp_id

                    ecu.ecuHW.transceiver.get(self.current_message)

        else:
            # iterate over receivers
            for ecu in self.connected_ecus:

                # Gateway:avoid sending to itself (loops)
                if self._gateway_sends(ecu): continue

                # ECU: avoid sending to itself
                if (ecu.ecu_id != self.current_message.sender_id):
                    self.current_message.current_bus = self.comp_id

                    ecu.ecuHW.transceiver.get(self.current_message)

    def _reset_transmission(self):
        ''' after one message was sent three things have to be reset
            the current message. The synchronizer for the selection
            of the next higher prioritized message to be sent and the 
            list that gathered the selected potential messages
            
            Input:     -
            Output:    -
        '''
        self.current_message = None  # message is not on the line anymore
        self.sync_2.put(True)  # channel is free again
        self.pot_messages = []  # reset

    def _sending_ok(self, t_propagation, t_sending):
        ''' checks if this message is sendable
            
            Input:  t_propagation:    float    time it takes to propagate the message
                    t_sending         float    time it takes to send the message
            Output: bool              boolean  true if the time is valid
        '''
        try:
            G().to_t(
                self.sim_env, t_propagation + t_sending +
                self.SCB_WRITE_TO_TRANSCEIVER_BUFFER,
                'SCB_PROPAGATION_DELAY+SCB_SENDING_TIME+SCB_WRITE_TO_TRANSCEIVER_BUFFER',
                self.__class__.__name__, self)
            if t_propagation + t_sending + self.SCB_WRITE_TO_TRANSCEIVER_BUFFER > 0:
                return True
            # logging.error("Error (skipped with time 0):t_propagation =%s, t_sending = %s, self.SCB_WRITE_TO_TRANSCEIVER_BUFFER = %s // msg_length_bit = %s" % \
            #              (t_propagation, t_sending, self.SCB_WRITE_TO_TRANSCEIVER_BUFFER, self.current_message_length_bit))
        except:
            return False
        return False

    def _try_logging_transmission(self, t_propagation, t_sending):
        ''' notes the times that it takes to send the messages. In case an erroneous message
            is sent this method logs the exception
        
            Input:  t_propagation:    float    time it takes to propagate the message
                    t_sending         float    time it takes to send the message
        '''

        try:
            # Log transmission
            L().log(300, self.sim_env.now, self.current_message.sender_id, float(self.current_message_length_bit) / 8.0, \
                    self.current_message_length_bit, self.comp_id, self.current_message.data.get(), t_propagation + t_sending)
        except:
            # Log traceback
            ECULogger().log_traceback()
            try:
                L().log(300, self.sim_env.now, self.current_message.sender_id, self.current_message.data, \
                        self.current_message_length_bit, self.comp_id, self.current_message.data, t_propagation + t_sending)
            except:
                pass

    def _wait_transmission_time_and_buffer(self):
        ''' this method times out for the duration of the transmission and
            then writes the sent messages to the receiving buffer of the ecu
            
            Input:       -
            Output:      -
        '''

        if not self.current_message_length_bit in self._used_prop_times:
            # sending times
            t_propagation, t_sending = self._extract_transmission_times()

            # duration of transmission
            wait_time = t_propagation + t_sending + self.SCB_WRITE_TO_TRANSCEIVER_BUFFER
            if wait_time <= 0: wait_time = 0.000001
            self._used_prop_times[self.current_message_length_bit] = wait_time

        else:

            wait_time = self._used_prop_times[self.current_message_length_bit]

        yield self.sim_env.timeout(wait_time)

        # put to connected ecus
        self._push_to_receivers()
class MyProtocolCommModule(AbstractCommModule):
    
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
        
        # add tags
        self._tags = ["AUTH_SEND_TIME_BEFORE_ENCRYPTION", "AUTH_SEND_TIME_AFTER_ENCRYPTION", "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"]        
    
        # NEW: Add key pair - public and private
        assymetric_encryption_algorithm = AsymAuthMechEnum.RSA
        assymetric_encryption_key_length = AuKeyLengthEnum.bit_512
        assymetric_encryption_option = 65537
        self.priv_key, self.pub_key = encryption_tools.asy_get_key_pair(assymetric_encryption_algorithm, assymetric_encryption_key_length, assymetric_encryption_option)
        PublicKeyManager().add_key(self._ecu_id, self.pub_key) # make public key available to everybody

        # Create a certificate from root L3 along L3 L31 L311
        [certificate, root_certificates_to_verify_this_certificate, priv_key] = GeneralSpecPreset().certificate_manager.generate_valid_ecu_cert(self._ecu_id, CAEnum.CA_L311, 0, float('inf'))
        self.my_certificate = certificate
        self.my_root_certificates = root_certificates_to_verify_this_certificate
         
        # verify certificate from root L311 with right root certificates
        #certificate_valid = encryption_tools.certificate_trustworthy(certificate, root_certificates_to_verify_this_certificate, self.sim_env.now)
        
        
        
    def receive_msg(self):
        
        while True:
                        
            # receive from lower layer
            [message_id, message_data] = yield self.sim_env.process(self.transp_lay.receive_msg())        
        
            # receiver information    
            print("\n\nRECEIVER\nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + self._ecu_id + "\nReceived message:\n - ID: " + str(message_id))
            
            # Assume it takes 0.5 seconds to e.g. decrypt this message
            uid = uuid.uuid4()
            # BEFORE PROCESSING
            print("\nECU "+ str(self._ecu_id) +"Time before message received: "+ str(self.sim_env.now))
            G().mon(self.monitor_list, MonitorInput([], "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message_data.get(), message_data.padded_size, 432, uid))           
            
            ''' receive and verify certificate '''
            [received_message, received_certificate] = message_data.get()
            # verify certificate - which works here as they all have the same CA authority that signed the certificate
            certificate_valid = encryption_tools.certificate_trustworthy(received_certificate, self.my_root_certificates, self.sim_env.now)
        
            
            # AFTER PROCESSING
            print("\nECU "+ str(self._ecu_id) +"Time after message received: "+ str(self.sim_env.now))            
            G().mon(self.monitor_list, MonitorInput([], "AUTH_RECEIVE_TIME_AFTER_DECRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message_data.get(), message_data.padded_size, 432, uid))
            
        # push to higher layer
        return [message_id, message_data]

    def send_msg(self, sender_id, message_id, message):
        # Sender information

        print("\n\nSENDER - \nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + sender_id + "\nSending message:\n - ID: " + str(message_id)+"\n - Content: " + message.get())
                
        # Message to be send 
        print("\nSize of the message we want to send: "+ str(message.padded_size))
        print("\nContent of the message: "+ str(message.get()))
        
        # Assume it takes 0.2 seconds to e.g. encrypt this message
        uid = uuid.uuid4()
        # BEFORE PROCESSING
        print("\nECU "+ str(self._ecu_id) +"Time before message sent: "+ str(self.sim_env.now))
        G().mon(self.monitor_list, MonitorInput([], "AUTH_SEND_TIME_BEFORE_ENCRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message.get(), message.padded_size, 432, uid))
        
        '''Send my certificate to the other ECUs ''' 
        message_to_send = SegData([message.get(), self.my_certificate],  50)
        
        # AFTER PROCESSING
        G().mon(self.monitor_list, MonitorInput([], "AUTH_SEND_TIME_AFTER_ENCRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message.get(), message.padded_size, 432, uid))
        print("\nECU "+ str(self._ecu_id) +"Time after message sent: "+ str(self.sim_env.now))

        # Send message - here send your message with your message_id
        yield  self.sim_env.process(self.transp_lay.send_msg(sender_id, message_id, message_to_send))

            
    def _init_layers(self, sim_env, MessageClass):
        ''' Initializes the software layers 
            
            Input:  sim_env                        simpy.Environment        environment of this component                      
                    MessageClass                   AbstractBusMessage       class of the messages  how they are sent on the CAN Bus
            Output: -                   
        '''
        
        # create layers
        self.physical_lay = StdPhysicalLayer(sim_env)         
        self.datalink_lay = StdDatalinkLayer(sim_env) 
        self.transp_lay = SegmentTransportLayer(sim_env, MessageClass)
   
        # interconnect layers             
        self.datalink_lay.physical_lay = self.physical_lay        
        self.transp_lay.datalink_lay = self.datalink_lay           
        
        
    @property
    def ecu_id(self):
        return self._ecu_id
               
    @ecu_id.setter    
    def ecu_id(self, value):
        self._ecu_id = value          

    def monitor_update(self):
        ''' updates the monitor connected to this ecu
            
            Input:    -
            Output:   monitor_list    RefList    list of MonitorInputs
        '''
        # register Monitoring tags to track
        #G().register_eventline_tags(self._tags)
        
        items_1 = len(self.transp_lay.datalink_lay.controller.receive_buffer.items)
        items_2 = self.transp_lay.datalink_lay.transmit_buffer_size
        
        G().mon(self.monitor_list, MonitorInput(items_1, MonitorTags.BT_ECU_RECEIVE_BUFFER, self._ecu_id, self.sim_env.now))
        G().mon(self.monitor_list, MonitorInput(items_2, MonitorTags.BT_ECU_TRANSMIT_BUFFER, self._ecu_id, self.sim_env.now))
        
        self.monitor_list.clear_on_access()  # on the next access the list will be cleared        
        return self.monitor_list.get()
Example #21
0
class MyProtocolCommModule(AbstractCommModule):
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()

        # initialize
        self._init_layers(self.sim_env, self.MessageClass)

        # add tags
        self._tags = [
            "AUTH_SEND_TIME_BEFORE_ENCRYPTION",
            "AUTH_SEND_TIME_AFTER_ENCRYPTION",
            "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION",
            "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"
        ]

    def receive_msg(self):

        while True:

            # receive from lower layer
            [message_id, message_data
             ] = yield self.sim_env.process(self.transp_lay.receive_msg())

            # receiver information
            print("\n\nRECEIVER\nTime: " + str(self.sim_env.now) +
                  "--Communication Layer: \nI am ECU " + self._ecu_id +
                  "\nReceived message:\n - ID: " + str(message_id) +
                  "\n - Content: " + message_data.get())

            # Assume it takes 0.5 seconds to e.g. decrypt this message
            uid = uuid.uuid4()
            # BEFORE PROCESSING
            print("\nECU " + str(self._ecu_id) +
                  "Time before message received: " + str(self.sim_env.now))
            G().mon(
                self.monitor_list,
                MonitorInput([], "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION",
                             self._ecu_id, self.sim_env.now, 123, message_id,
                             message_data.get(), message_data.padded_size, 432,
                             uid))

            yield self.sim_env.timeout(0.5)

            # AFTER PROCESSING
            print("\nECU " + str(self._ecu_id) +
                  "Time after message received: " + str(self.sim_env.now))
            G().mon(
                self.monitor_list,
                MonitorInput([], "AUTH_RECEIVE_TIME_AFTER_DECRYPTION",
                             self._ecu_id, self.sim_env.now, 123, message_id,
                             message_data.get(), message_data.padded_size, 432,
                             uid))

        # push to higher layer
        return [message_id, message_data]

    def send_msg(self, sender_id, message_id, message):
        # Sender information
        print("\n\nSENDER - \nTime: " + str(self.sim_env.now) +
              "--Communication Layer: \nI am ECU " + sender_id +
              "\nSending message:\n - ID: " + str(message_id) +
              "\n - Content: " + message.get())

        # Message to be send
        print("\nSize of the message we want to send: " +
              str(message.padded_size))
        print("\nContent of the message: " + str(message.get()))

        # Assume it takes 0.2 seconds to e.g. encrypt this message
        uid = uuid.uuid4()
        # BEFORE PROCESSING
        print("\nECU " + str(self._ecu_id) + "Time before message sent: " +
              str(self.sim_env.now))
        G().mon(
            self.monitor_list,
            MonitorInput([], "AUTH_SEND_TIME_BEFORE_ENCRYPTION",
                         self._ecu_id, self.sim_env.now, 123, message_id,
                         message.get(), message.padded_size, 432, uid))

        yield self.sim_env.timeout(0.2)

        # AFTER PROCESSING
        G().mon(
            self.monitor_list,
            MonitorInput([], "AUTH_SEND_TIME_AFTER_ENCRYPTION",
                         self._ecu_id, self.sim_env.now, 123, message_id,
                         message.get(), message.padded_size, 432, uid))
        print("\nECU " + str(self._ecu_id) + "Time after message sent: " +
              str(self.sim_env.now))

        # Send message - here send your message with your message_id
        yield self.sim_env.process(
            self.transp_lay.send_msg(sender_id, message_id, message))

    def _init_layers(self, sim_env, MessageClass):
        ''' Initializes the software layers 
            
            Input:  sim_env                        simpy.Environment        environment of this component                      
                    MessageClass                   AbstractBusMessage       class of the messages  how they are sent on the CAN Bus
            Output: -                   
        '''

        # create layers
        self.physical_lay = StdPhysicalLayer(sim_env)
        self.datalink_lay = StdDatalinkLayer(sim_env)
        self.transp_lay = SegmentTransportLayer(sim_env, MessageClass)

        # interconnect layers
        self.datalink_lay.physical_lay = self.physical_lay
        self.transp_lay.datalink_lay = self.datalink_lay

    @property
    def ecu_id(self):
        return self._ecu_id

    @ecu_id.setter
    def ecu_id(self, value):
        self._ecu_id = value

    def monitor_update(self):
        ''' updates the monitor connected to this ecu
            
            Input:    -
            Output:   monitor_list    RefList    list of MonitorInputs
        '''
        # register Monitoring tags to track
        #G().register_eventline_tags(self._tags)

        items_1 = len(
            self.transp_lay.datalink_lay.controller.receive_buffer.items)
        items_2 = self.transp_lay.datalink_lay.transmit_buffer_size

        G().mon(
            self.monitor_list,
            MonitorInput(items_1, MonitorTags.BT_ECU_RECEIVE_BUFFER,
                         self._ecu_id, self.sim_env.now))
        G().mon(
            self.monitor_list,
            MonitorInput(items_2, MonitorTags.BT_ECU_TRANSMIT_BUFFER,
                         self._ecu_id, self.sim_env.now))

        self.monitor_list.clear_on_access(
        )  # on the next access the list will be cleared
        return self.monitor_list.get()
Example #22
0
class MyProtocolCommModule(AbstractCommModule):
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()

        # initialize
        self._init_layers(self.sim_env, self.MessageClass)

    def receive_msg(self):
        ''' receives messages via the tesla mechanism after
            they where authenticated. Then messages that were 
            authenticated are buffered in _messages_to_return
            and returned to higher layers sequentially on each 
            call of this method
            
            Input:     -
            Output:    message_data    object/string/...    message that was received
                       message_id      integer              id of received message
                       
            NOTE: 
                Accessing sender of a message 
                    -> USE: message_data.sender_id
                Accessing message_id of the received message 
                    -> USE: message_id
                Accessing message content
                    -> USE: message_data.get()
                
                message_data is of type SegData                
        '''

        while True:

            # receive from lower layer
            [message_id, message_data
             ] = yield self.sim_env.process(self.transp_lay.receive_msg())

            # do something here e.g. if message_id = Handshake request received here,
            # -> send something as response e.g.
            # PLACE CODE HERE
            print("\n\nRECEIVER\nTime: " + str(self.sim_env.now) +
                  "--Communication Layer: \nI am ECU " + self._ecu_id +
                  "\nReceived message:\n - ID: " + str(message_id) +
                  "\n - Content: " + message_data.get())

        # push to higher layer
        return [message_id, message_data]

    def send_msg(self, sender_id, message_id, message):
        ''' this  method receives the message from the application
            layer and transmits it further to the transport layer
            or if required handles the security communication

            Input:  sender_id    string        ID of the ECU that wants to send the message
                    message_id   integer       identifier of the message that is to be sent
                    message      object        message that will be sent
            Output: -
        '''
        # do something with received message
        # PLACE CODE HERE
        print("\n\nSENDER - \nTime: " + str(self.sim_env.now) +
              "--Communication Layer: \nI am ECU " + sender_id +
              "\nSending message:\n - ID: " + str(message_id) +
              "\n - Content: " + message.get())

        # Send message - here send your message with your message_id
        yield self.sim_env.process(
            self.transp_lay.send_msg(sender_id, message_id, message))

    def _init_layers(self, sim_env, MessageClass):
        ''' Initializes the software layers 
            
            Input:  sim_env                        simpy.Environment        environment of this component                      
                    MessageClass                   AbstractBusMessage       class of the messages  how they are sent on the CAN Bus
            Output: -                   
        '''

        # create layers
        self.physical_lay = StdPhysicalLayer(sim_env)
        self.datalink_lay = StdDatalinkLayer(sim_env)
        self.transp_lay = SegmentTransportLayer(sim_env, MessageClass)

        # interconnect layers
        self.datalink_lay.physical_lay = self.physical_lay
        self.transp_lay.datalink_lay = self.datalink_lay

    @property
    def ecu_id(self):
        return self._ecu_id

    @ecu_id.setter
    def ecu_id(self, value):
        self._ecu_id = value

    def monitor_update(self):
        ''' updates the monitor connected to this ecu
            
            Input:    -
            Output:   monitor_list    RefList    list of MonitorInputs
        '''
        items_1 = len(
            self.transp_lay.datalink_lay.controller.receive_buffer.items)
        items_2 = self.transp_lay.datalink_lay.transmit_buffer_size

        G().mon(
            self.monitor_list,
            MonitorInput(items_1, MonitorTags.BT_ECU_RECEIVE_BUFFER,
                         self._ecu_id, self.sim_env.now))
        G().mon(
            self.monitor_list,
            MonitorInput(items_2, MonitorTags.BT_ECU_TRANSMIT_BUFFER,
                         self._ecu_id, self.sim_env.now))

        self.monitor_list.clear_on_access(
        )  # on the next access the list will be cleared
        return self.monitor_list.get()
class RapidCANBus(AbstractCANBus):
    '''
    This class implements a CAN Bus that actively
    pulls messages from the ECUs buffers
    '''
    
    def __init__(self, sim_env, bus_id, data_rate, avg_ecu_dist=2):
        ''' Constructor
            
            Input:    sim_env        simpy.Environment         environment in which this Bus acts
                      bus_id         string                    id of this Bus object
                      data_rate      float                     datarate of this bus
                      avg_ecu_dist   float                     average distance between two connected ECUs
                
            Output:   -                  
        '''
        AbstractCANBus.__init__(self, sim_env, bus_id, data_rate, avg_ecu_dist)
        
        # bus objects
        self.current_message = None  # current message on the bus [sender_ecu, message]
        self.set_settings()
        self.monitor_list = RefList()
        self._used_prop_times = {}
        self.gateways = []
        self.first = True
        
        # synchronization objects
        self.pot_messages = []  # gathers all potential messages that want to be sent at a certain point in time
        self.sync_1 = simpy.Store(self.sim_env, capacity=1)  # if the decision, who is allowed to sent is done this synchronizer starts the transmission
        self.sync_2 = simpy.Store(self.sim_env, capacity=1)  # if store is empty then the channel is busy
        self.subscribers = 0  # number of ECUs waiting for the channel to be freed
        self.current_message_length_bit = 0
            
        # project parameters
        self.SCB_GATHER_MSGS = time.SCB_GATHER_MSGS
        self.SCB_GRAB_PRIO_MSG = time.SCB_GRAB_PRIO_MSG
        self.SCB_PROPAGATION_DELAY = time.SCB_PROPAGATION_DELAY
        self.SCB_SENDING_TIME = time.SCB_SENDING_TIME
        self.SCB_WRITE_TO_TRANSCEIVER_BUFFER = time.SCB_WRITE_TO_TRANSCEIVER_BUFFER

        # ECUs Datalink layers willing to send
        self._willing_dll = []

    def monitor_update(self):
        ''' returns the input for the monitor 
        
            Input:    -
            Output:   monitor_list    list    List of MonitorInput objects
        '''
        
        self.monitor_list.clear_on_access()  # on the next access the list will be cleared        
        return self.monitor_list.get()
    
    def release_willing(self, dll):
        ''' remove the ecus that are not willing to send
            anymore
            
            Input:    dll    AbstractDataLinkLayer    Datalink layer of the ECU that is not willing to send anymore
            Output:   -  
        '''
        self._willing_dll.remove(dll)
        
    def add_willing(self, dll):
        ''' add a Datalinklayer of an ECU that is willing to send
        
            Input:    dll    AbstractDataLinkLayer    Datalink layer of the ECU that is willing to send
            Output:   -  
        '''
        if dll not in self._willing_dll:
            self._willing_dll.append(dll)
      
    def notify_bus(self):
        ''' When the bus is empty it is set to a sleep mode that waits
            until the ecu notifies it. Using this method the ECU does so.
            Thus the Bus will only be active when any ecu sends.
            
            Input:  -
            Output: -
        '''
        if self.current_message != None: return
        else: self.sync_1.put(True) 
        
    def process(self):
        ''' Constantly pull messages from all ECUs that are connected. Once the 
            Bus is done with one message it pulls the next message from the 
            connected ECUs.
            
            Input:   -
            Output:  -            
        '''
        stp = 0
        while True: 
            
            # print time
            t = self.sim_env.now
            if t > stp:
#                 print(self.sim_env.now)
                stp += 0.5
            
            # check which ECU sends
            current_minimum = float("inf"); index = 0            
            for dll in self._willing_dll:                
                val = dll.first_queue_identifier()
                if val < current_minimum and val != False:
                    current_minimum = index
                index += 1
            
            # no ECU wiling to send: wait for notify
            if current_minimum == float("inf"):
                yield self.sync_1.get() 
            else:
                self.current_message = self._willing_dll[current_minimum].controller.transmit_buffer.get().message                
                self.current_message_length_bit = self.current_message.msg_length_in_bit
                
            # transmit message
            if self.current_message != None:
                
                # monitor start
                monitor_note = self._monitor_transmission_start()
                
                # write to buffer
                yield self.sim_env.process(self._wait_transmission_time_and_buffer())
                self._reset_transmission()
                
                # monitor end
                self._monitor_transmission_end(monitor_note)
             
                # if ecus buffer is now empty remove from willing
                if(len(self._willing_dll[current_minimum].controller.transmit_buffer.queue) == 0):
                    self.release_willing(self._willing_dll[current_minimum])
                    
    def set_settings(self):
        ''' sets the initial setting association between the settings variables
            and the actual parameter
        
            Input:   -
            Output:  -
        '''
        self.settings = {}
        
        # parameter
        self.settings['t_gather_msg'] = 'SCB_GATHER_MSGS'
        self.settings['t_grab_prio_msg'] = 'SCB_GRAB_PRIO_MSG'
        self.settings['t_propagation_delay'] = 'SCB_PROPAGATION_DELAY'
        self.settings['t_sending_time'] = 'SCB_SENDING_TIME'
        self.settings['t_write_to_transceiver_buffer'] = 'SCB_WRITE_TO_TRANSCEIVER_BUFFER' 
              
    def wait_until_free(self):
        ''' when the channel is busy some ECUs can start this method in
            a simpy process. Once the channel is free this process ends 
            and the next ECU can start it's transmission             
            technically:
            count number of waiting processes and notifies them all once the channel is free
            
            Input:    -
            Output    -
        '''
        # add subscriber
        self.subscribers += 1                
        yield self.sync_2.get()
        
        # release all receivers
        while self.subscribers > 1:           
            
            self.sync_2.put(True)
            self.subscribers -= 1 
        self.subscribers = 0           
     
    def _extract_transmission_times(self):
        ''' calculates the time the current transmission takes
            
            Input:       -
            Output: t_propagation:    float    time it takes to propagate the message
                    t_sending         float    time it takes to send the message
        '''
        
        t_propagation = time.call(self.SCB_PROPAGATION_DELAY, self.avg_dist_between_ecus)  # either constant or calculated depending on config
        t_sending = time.call(self.SCB_SENDING_TIME, self.current_message_length_bit, proj.BUS_ECU_DATARATE)  # either constant or calculated depending on config
        
        return t_propagation, t_sending

    def _gateway_sends(self, ecu):
        ''' if gateway is the sender let it continue 
            and reset the message state
            
            Input:  ecu     AbstractECU    current ECU that sends the message            
            Output: bool    boolean        True if the message was sent by this ECU
        '''
        try:
            if ecu.ecu_id in self.current_message.gw_id:  # send message back and forth send could lead to errors: need to reset gw_id list
                return True
        except:
            return False
        return False
     
         
    def _get_highest_priority_msg(self, message_list):
        ''' returns the message with the highest priority
            
            Input:     message_list    list        list of messages
            Output:    message         object      message with highest priority (lowest message id)
        '''
        min_val = float("inf")
        message = None
        for cur_message in message_list:            
            if min_val > cur_message.message_identifier:
                min_val = cur_message.message_identifier
                message = cur_message
        return message
    
    def _grab_highest_priority(self):
        ''' note the time it takes to select the message with
            the highest priority
        
            Input:     -
            Output:    -            
        '''
        if self.SCB_GRAB_PRIO_MSG != 0:
            G().to_t(self.sim_env, self.SCB_GRAB_PRIO_MSG, 'SCB_GRAB_PRIO_MSG', self.__class__.__name__, self) 
            return True
        return False
    
    def _monitor_transmission_start(self):
        ''' notes the start time when this message was put on the bus
            
            Input:  -
            Output: -
        '''
        # extract information 
        uid = uuid.uuid4()
        tag = MonitorTags.CB_PROCESSING_MESSAGE
        c_id = self.comp_id
        sender_id = self.current_message.sender_id
        msg_id = self.current_message.message_identifier
        msg_uid = self.current_message.data.unique_id
        data = self.current_message.data.get();         
        
        # extract further information         
        msg = self.current_message
        size = self.current_message_length_bit / 8
        self.current_message.data.unique_id = msg_uid
        
        # send to monitor
        G().mon(self.monitor_list, MonitorInput(data, tag, c_id, self.sim_env.now, sender_id, msg_id, msg, size, msg_id, uid.hex))        
        return data, c_id, sender_id, msg_id, msg, size, uid
           
    def _monitor_transmission_end(self, mon_out):
        ''' notes the end time when this message was put on the bus
            
            Input:  -
            Output: -
        '''
        G().mon(self.monitor_list, MonitorInput(mon_out[0], MonitorTags.CB_DONE_PROCESSING_MESSAGE, \
                                                mon_out[1], self.sim_env.now, mon_out[2], mon_out[3], \
                                                mon_out[4], mon_out[5], -1, mon_out[6].hex))

    def _push_to_receivers(self):
        ''' writes the current message to all ecus that 
            are connected to this Bus
        
            Input:       -
            Output:      -
        '''
        # get gateways
        if self.first:
            self.gateways = [itm.ecu_id for itm in self.connected_ecus if isinstance(itm.ecu_id, UUID)]
            self.first = False
        
        # send only to receivers
        if General().send_only_to_receivers and self.current_message.message_identifier not in can_registration.AUTH_MESSAGES:     
            
            run_list = General().sender_receiver_map[self.current_message.sender_id][self.current_message.message_identifier] + self.gateways
                   
            for ecu in self.connected_ecus:

                if ecu.ecu_id not in run_list:
                    continue
                
                # Gateway:avoid sending to itself (loops)
                if self._gateway_sends(ecu): continue
    
                # ECU: avoid sending to itself
                if(ecu.ecu_id != self.current_message.sender_id):
                    self.current_message.current_bus = self.comp_id
    
                    ecu.ecuHW.transceiver.get(self.current_message)
        
        else:
            # iterate over receivers      
            for ecu in self.connected_ecus:
                
                # Gateway:avoid sending to itself (loops)
                if self._gateway_sends(ecu): continue
    
                # ECU: avoid sending to itself
                if(ecu.ecu_id != self.current_message.sender_id):
                    self.current_message.current_bus = self.comp_id
    
                    ecu.ecuHW.transceiver.get(self.current_message)

    
    def _reset_transmission(self):
        ''' after one message was sent three things have to be reset
            the current message. The synchronizer for the selection
            of the next higher prioritized message to be sent and the 
            list that gathered the selected potential messages
            
            Input:     -
            Output:    -
        '''
        self.current_message = None  # message is not on the line anymore
        self.sync_2.put(True)  # channel is free again
        self.pot_messages = []  # reset

    def _sending_ok(self, t_propagation, t_sending):
        ''' checks if this message is sendable
            
            Input:  t_propagation:    float    time it takes to propagate the message
                    t_sending         float    time it takes to send the message
            Output: bool              boolean  true if the time is valid
        '''
        try:
            G().to_t(self.sim_env, t_propagation + t_sending + self.SCB_WRITE_TO_TRANSCEIVER_BUFFER, 'SCB_PROPAGATION_DELAY+SCB_SENDING_TIME+SCB_WRITE_TO_TRANSCEIVER_BUFFER', self.__class__.__name__, self)
            if t_propagation + t_sending + self.SCB_WRITE_TO_TRANSCEIVER_BUFFER > 0:
                return True        
            # logging.error("Error (skipped with time 0):t_propagation =%s, t_sending = %s, self.SCB_WRITE_TO_TRANSCEIVER_BUFFER = %s // msg_length_bit = %s" % \
            #              (t_propagation, t_sending, self.SCB_WRITE_TO_TRANSCEIVER_BUFFER, self.current_message_length_bit))
        except: 
            return False
        return False
    
    def _try_logging_transmission(self, t_propagation, t_sending):
        ''' notes the times that it takes to send the messages. In case an erroneous message
            is sent this method logs the exception
        
            Input:  t_propagation:    float    time it takes to propagate the message
                    t_sending         float    time it takes to send the message
        '''
                        
        try:
            # Log transmission
            L().log(300, self.sim_env.now, self.current_message.sender_id, float(self.current_message_length_bit) / 8.0, \
                    self.current_message_length_bit, self.comp_id, self.current_message.data.get(), t_propagation + t_sending) 
        except:      
            # Log traceback
            ECULogger().log_traceback()
            try: 
                L().log(300, self.sim_env.now, self.current_message.sender_id, self.current_message.data, \
                        self.current_message_length_bit, self.comp_id, self.current_message.data, t_propagation + t_sending)
            except: pass
    
    def _wait_transmission_time_and_buffer(self):
        ''' this method times out for the duration of the transmission and
            then writes the sent messages to the receiving buffer of the ecu
            
            Input:       -
            Output:      -
        '''

        if not self.current_message_length_bit in self._used_prop_times:
            # sending times         
            t_propagation, t_sending = self._extract_transmission_times()    
            
            # duration of transmission    
            wait_time = t_propagation + t_sending + self.SCB_WRITE_TO_TRANSCEIVER_BUFFER 
            if wait_time <= 0: wait_time = 0.000001
            self._used_prop_times[self.current_message_length_bit] = wait_time      
            
        else:

            wait_time = self._used_prop_times[self.current_message_length_bit]
            
        yield self.sim_env.timeout(wait_time)
        
        # put to connected ecus
        self._push_to_receivers()
class MyProtocolCommModule(AbstractCommModule):
    
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        self._have_session_key = False
        self._session_key = None
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
        
        # add tags
        self._tags = ["AUTH_SEND_TIME_BEFORE_ENCRYPTION", "AUTH_SEND_TIME_AFTER_ENCRYPTION", "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"]        
    
        # NEW: Add key pair - public and private
        assymetric_encryption_algorithm = AsymAuthMechEnum.RSA
        assymetric_encryption_key_length = AuKeyLengthEnum.bit_512
        assymetric_encryption_option = 65537
        self.priv_key, self.pub_key = encryption_tools.asy_get_key_pair(assymetric_encryption_algorithm, assymetric_encryption_key_length, assymetric_encryption_option)
        PublicKeyManager().add_key(self._ecu_id, self.pub_key) # make public key available to everybody

        # Create a certificate from root L3 along L3 L31 L311
        [certificate, root_certificates_to_verify_this_certificate, self.certificate_private_key] = GeneralSpecPreset().certificate_manager.generate_valid_ecu_cert(self._ecu_id, CAEnum.CA_L311, 0, float('inf'))
        self.my_certificate = certificate
        self.my_root_certificates = root_certificates_to_verify_this_certificate
         
        # verify certificate from root L311 with right root certificates
        #certificate_valid = encryption_tools.certificate_trustworthy(certificate, root_certificates_to_verify_this_certificate, self.sim_env.now)

    def _get_time_for_session_decryption(self, encrypted_message):
        """
        This method computes the decryption time (as an example) based on the input message size
        :return:
        """

        # decrypted size
        size_to_decrypt = 24

        # decryption time
        algorithm = EnumTrafor().to_value(SymAuthMechEnum.AES)
        key_len = EnumTrafor().to_value(AuKeyLengthEnum.bit_128)

        library_tag = "Crypto_Lib_HW"  # available tags: ['CyaSSL', 'Crypto_Lib_HW', 'Crypto_Lib_SW']

        db_val = TimingDBMap().lookup_interpol(lib=library_tag, mode='DECRYPTION', \
                                               keylen=key_len, alg=algorithm, data_size=size_to_decrypt,
                                               description='some_description')

        decrypted_size = encrypted_message.msg_unencrpyted._size  # this is stored in the encrypted message

        decryption_time = db_val

        return decryption_time, decrypted_size

    def _get_time_for_session_encryption(self, encrypted_message):
        '''
        This method computes the encryption time (as an example) based on the input message size

        Arguments for the lookup_interpol function are:
        --> Checkout the content of the database in ECUSimulation/config/data/measurements.db
        --> There you can also add your own measurements
        looks for a value in the database. If none is found looks for variables
                    around it and tries to interpolate a value from the neighboring values

                    Input:  lib        string            value of library column in the DB
                            mode       string            mode requested of library column in the DB e.g. ENCRYPTION, DECRYPTION,...
                            alg        string            name of the algorithm of library column in the DB
                            alg_mode   string            name of algorithm mode of library column in the DB (e.g. CTR, ...)
                            keylen     integer           length of the key in bit of library column in the DB
                            exp        integer           size of the exponent when RSA is used
                            param_len  integer           length of the parameter whenn ECC is used (library column in the DB )
                            data_size  integer           size of the data of library column in the DB
                            ret_all    boolean           if this value is true the values for all data_sizes will be returned
                    Output: time       float             interpolated time from requested values in the database
        '''

        # encrypted size
        size_to_encrypt = encrypted_message._size

        # encryption time
        algorithm = EnumTrafor().to_value(SymAuthMechEnum.AES)
        key_len = EnumTrafor().to_value(AuKeyLengthEnum.bit_128)

        library_tag = "Crypto_Lib_HW" # available tags: ['CyaSSL', 'Crypto_Lib_HW', 'Crypto_Lib_SW']

        db_val = TimingDBMap().lookup_interpol(lib=library_tag, mode='ENCRYPTION', \
                                               keylen=key_len, alg=algorithm, data_size=size_to_encrypt,
                                               description='t_ecu_auth_reg_msg_validate_cert')

        encrypted_size = 24 # something you need to compute yourself or use the examples provided in ECUSimulation/components/security/ecu/types/impl_sec_mod_lwa.py

        encryption_time = db_val

        return encryption_time, encrypted_size


    def receive_msg(self):
        
        while True:
                        
            # receive from lower layer
            [message_id, message_data] = yield self.sim_env.process(self.transp_lay.receive_msg())

            # receiver information    
            print("\n\nRECEIVER\nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + self._ecu_id + "\nReceived message:\n - ID: " + str(message_id))

            # ALL PARTIES RECEIVED A SESSION KEY WE CAN DECRYPT THE MESSAGE HERE AND PASS IT TO THE
            # APPLICATION LAYER IF DECRYPTION WAS SUCCESSFUL
            if self._have_session_key and not message_id in [901, 902, 903]:
                # decryption
                decrypted_msg = sym_decrypt(message_data.get(), self._session_key)

                # compute decrypted time and size
                print("%s: Time before decryption %s" % (str(self._ecu_id), str(self.sim_env.now)))
                decryption_time, decrypted_size = self._get_time_for_session_decryption(message_data.get())
                yield self.sim_env.timeout(decryption_time) # time for decryption
                print("%s: Time after decryption %s" % (str(self._ecu_id), str(self.sim_env.now)))

                # if successful pass to app layer
                if decrypted_msg is None:
                    print("--- Decryption was not successful - probably this ECU did not get the session key")
                else:
                    message_data = decrypted_msg.get()
                    # push to higher layer
                    return [message_id, message_data]

            # PART 1 OF OUR SIMPLE PROTOCOL, THE PROTOCOL IS RECEIVED AND A SESSION-KEY IS GENERATED AND SEND
            # TO THE OTHER ECUS, ALSO THIS SESSION KEY IS STORED AND USED FOR THE REMAINING COMMUNICATION
            if message_id == 901:

                #receive and verify certificate
                [encrypted_msg, received_certificate, received_cert_private_key] = message_data.get()
                # verify certificate - which works here as they all have the same CA authority that signed the certificate
                certificate_valid = encryption_tools.certificate_trustworthy(received_certificate, self.my_root_certificates, self.sim_env.now)
                print("Certificate is valid? %s" % str(certificate_valid))

                # Encrypt the new message
                clear_message = asy_decrypt(encrypted_msg, received_cert_private_key, self.sim_env.now)
                print("Received CLEAR MESSAGE %s" % str(clear_message.get()))

                # Respond with a session key of size 30
                self._session_key = sym_get_key(SymAuthMechEnum.AES, AuKeyLengthEnum.bit_128)
                self._have_session_key = True

                sec_message_id = 902
                yield self.sim_env.process(self.transp_lay.send_msg(self._ecu_id, sec_message_id, SegData(self._session_key, 30)))

            # RECEIVE A SESSION KEY AND STORE IT
            if message_id == 902:
                # Read the message data -> which is session key
                self._session_key = message_data.get()
                self._have_session_key = True # return a all good message with id 903 of size 20
                sec_message_id = 903


    def send_msg(self, sender_id, message_id, message):
        # Sender information

        # INITIALLY SEND MY CERTIFICATE TO THE OTHER ECU
        if not self._have_session_key:
            print("Sending Certificate and my private key : ")
            # Encrypt message with the public key
            encrypted_msg = encryption_tools.asy_encrypt(message, self.my_certificate.pub_key_user)

            # Send the certificate and message and private key
            message_to_send = SegData([encrypted_msg, self.my_certificate, self.certificate_private_key], 50)
            sec_message_id = 901
            print("\n\nSENDER - \nTime: " + str(
                self.sim_env.now) + "--Communication Layer: \nI am ECU " + sender_id + "\nSending message:\n - ID: " + str(
                sec_message_id) + "\n - Content: " + str(message_to_send.get()))
            yield self.sim_env.process(self.transp_lay.send_msg(sender_id, sec_message_id, message_to_send))


        else:
            # IF THE RECEIVE MSG FUNCTION STORED A SESSION KEY THIS KEY IS NOW USED TO ENCRYPT A MESSAGE
            # AND TO SEND THE ENCRYPTED MESSAGE

            # Encrypt message
            encrypted_msg = sym_encrypt(message, self._session_key)
            print("\n\nSENDER - \nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + sender_id + "\nSending message:\n - ID: " + str(message_id)+"\n - Content: " + message.get())

            # Sending message now with the session key I have
            print("\nEncrypted the message")

            # Send message - here send your message with your message_id
            print("%s: Time before encryption %s" % (str(self._ecu_id), str(self.sim_env.now)))
            encryption_time, encrypted_size = self._get_time_for_session_encryption(message)
            yield self.sim_env.timeout(encryption_time) # apply time for encryption
            print("%s: Time after encryption %s"  % (str(self._ecu_id), str(self.sim_env.now)))

            yield self.sim_env.process(self.transp_lay.send_msg(sender_id, message_id, SegData(encrypted_msg, encrypted_size)))

            
    def _init_layers(self, sim_env, MessageClass):
        ''' Initializes the software layers 
            
            Input:  sim_env                        simpy.Environment        environment of this component                      
                    MessageClass                   AbstractBusMessage       class of the messages  how they are sent on the CAN Bus
            Output: -                   
        '''
        
        # create layers
        self.physical_lay = StdPhysicalLayer(sim_env)         
        self.datalink_lay = StdDatalinkLayer(sim_env) 
        self.transp_lay = SegmentTransportLayer(sim_env, MessageClass)
   
        # interconnect layers             
        self.datalink_lay.physical_lay = self.physical_lay        
        self.transp_lay.datalink_lay = self.datalink_lay           
        
        
    @property
    def ecu_id(self):
        return self._ecu_id
               
    @ecu_id.setter    
    def ecu_id(self, value):
        self._ecu_id = value          

    def monitor_update(self):
        ''' updates the monitor connected to this ecu
            
            Input:    -
            Output:   monitor_list    RefList    list of MonitorInputs
        '''
        # register Monitoring tags to track
        #G().register_eventline_tags(self._tags)
        
        items_1 = len(self.transp_lay.datalink_lay.controller.receive_buffer.items)
        items_2 = self.transp_lay.datalink_lay.transmit_buffer_size
        
        G().mon(self.monitor_list, MonitorInput(items_1, MonitorTags.BT_ECU_RECEIVE_BUFFER, self._ecu_id, self.sim_env.now))
        G().mon(self.monitor_list, MonitorInput(items_2, MonitorTags.BT_ECU_TRANSMIT_BUFFER, self._ecu_id, self.sim_env.now))
        
        self.monitor_list.clear_on_access()  # on the next access the list will be cleared        
        return self.monitor_list.get()
Example #25
0
class MyProtocolCommModule(AbstractCommModule):
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()

        # initialize
        self._init_layers(self.sim_env, self.MessageClass)

        # add tags
        self._tags = [
            "AUTH_SEND_TIME_BEFORE_ENCRYPTION",
            "AUTH_SEND_TIME_AFTER_ENCRYPTION",
            "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION",
            "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"
        ]

        # NEW: Add key pair - public and private
        assymetric_encryption_algorithm = AsymAuthMechEnum.RSA
        assymetric_encryption_key_length = AuKeyLengthEnum.bit_512
        assymetric_encryption_option = 65537
        self.priv_key, self.pub_key = encryption_tools.asy_get_key_pair(
            assymetric_encryption_algorithm, assymetric_encryption_key_length,
            assymetric_encryption_option)
        PublicKeyManager().add_key(
            self._ecu_id,
            self.pub_key)  # make public key available to everybody

        self.first_message = False

    def receive_msg(self):

        while True:

            # receive from lower layer
            [message_id, message_data
             ] = yield self.sim_env.process(self.transp_lay.receive_msg())

            # receiver information
            print("\n\nRECEIVER\nTime: " + str(self.sim_env.now) +
                  "--Communication Layer: \nI am ECU " + self._ecu_id +
                  "\nReceived message:\n - ID: " + str(message_id))

            # Assume it takes 0.5 seconds to e.g. decrypt this message
            uid = uuid.uuid4()
            # BEFORE PROCESSING
            print("\nECU " + str(self._ecu_id) +
                  "Time before message received: " + str(self.sim_env.now))
            G().mon(
                self.monitor_list,
                MonitorInput([], "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION",
                             self._ecu_id, self.sim_env.now, 123, message_id,
                             message_data.get(), message_data.padded_size, 432,
                             uid))
            ''' Perform asymmetric decryption of the incoming message using its public key, e.g. lasting 0.5 seconds'''
            # get the public key of the sender
            senders_public_key = PublicKeyManager().get_key(
                message_data.sender_id)
            # use this key for decryption - also checks if this key is still valid
            received_cipher_message = message_data.get()
            clear_message = encryption_tools.asy_decrypt(
                received_cipher_message, senders_public_key, self.sim_env.now)
            [received_timestamp, received_hashed_message,
             received_message] = clear_message
            yield self.sim_env.timeout(0.5)
            ''' Perform symmetric decryption, e.g. takes 0.02 seconds'''
            [received_symmetric_key,
             received_symmetrically_encrypted_message] = received_message
            received_clear_message = encryption_tools.sym_decrypt(
                received_symmetrically_encrypted_message,
                received_symmetric_key)
            yield self.sim_env.timeout(0.02)
            ''' Verify received hash of the message, e.g. takes 0.01 second '''
            hashed_message = HashedMessage(str(received_message),
                                           HashMechEnum.MD5)
            is_same_hash = hashed_message.same_hash(received_hashed_message)
            yield self.sim_env.timeout(0.01)

            # AFTER PROCESSING
            print("\nECU " + str(self._ecu_id) +
                  "Time after message received: " + str(self.sim_env.now))
            G().mon(
                self.monitor_list,
                MonitorInput([], "AUTH_RECEIVE_TIME_AFTER_DECRYPTION",
                             self._ecu_id, self.sim_env.now, 123, message_id,
                             message_data.get(), message_data.padded_size, 432,
                             uid))

        # push to higher layer
        return [message_id, received_clear_message]

    def send_msg(self, sender_id, message_id, message):
        # Sender information

        print("\n\nSENDER - \nTime: " + str(self.sim_env.now) +
              "--Communication Layer: \nI am ECU " + sender_id +
              "\nSending message:\n - ID: " + str(message_id) +
              "\n - Content: " + message.get())

        # Message to be send
        print("\nSize of the message we want to send: " +
              str(message.padded_size))
        print("\nContent of the message: " + str(message.get()))

        # Assume it takes 0.2 seconds to e.g. encrypt this message
        uid = uuid.uuid4()
        # BEFORE PROCESSING
        print("\nECU " + str(self._ecu_id) + "Time before message sent: " +
              str(self.sim_env.now))
        G().mon(
            self.monitor_list,
            MonitorInput([], "AUTH_SEND_TIME_BEFORE_ENCRYPTION",
                         self._ecu_id, self.sim_env.now, 123, message_id,
                         message.get(), message.padded_size, 432, uid))
        ''' Perform symmetric encryption (and key generation): send the message encrypted with a created key
        which e.g. takes 0.01 second'''
        algorithm = SymAuthMechEnum.AES
        key_length = AuKeyLengthEnum.bit_128
        algorithm_mode = SymAuthMechEnum.CBC
        sym_key = encryption_tools.sym_get_key(algorithm, key_length,
                                               algorithm_mode)
        # encrypt the message with the symmetric key we just created
        clear_message = message.get()
        cipher = encryption_tools.sym_encrypt(clear_message, sym_key)
        sym_cipher_message = [sym_key, cipher]
        yield self.sim_env.timeout(0.01)
        ''' Hash the message we want to send and send the hash with the message, e.g. takes 0.01 second '''
        hashed_message = HashedMessage(str(sym_cipher_message),
                                       HashMechEnum.MD5)
        yield self.sim_env.timeout(0.01)
        ''' Perform asymmetric encryption: Encrypt my private key which takes e.g. 0.2 seconds'''
        timestamp = self.sim_env.now
        encrypted_size = 50  # byte - usualy calculated from the size of the original message and the encryption algorithm
        clear_text_of_message = [timestamp, hashed_message, sym_cipher_message]
        cipher_message = encryption_tools.asy_encrypt(clear_text_of_message,
                                                      self.priv_key)
        cipher_message.valid_till = timestamp + 5  # add optional validity (e.g. 5 seconds)
        wrapped_cipher_message = SegData(cipher_message, encrypted_size)
        yield self.sim_env.timeout(0.2)

        # AFTER PROCESSING
        G().mon(
            self.monitor_list,
            MonitorInput([], "AUTH_SEND_TIME_AFTER_ENCRYPTION",
                         self._ecu_id, self.sim_env.now, 123, message_id,
                         message.get(), message.padded_size, 432, uid))
        print("\nECU " + str(self._ecu_id) + "Time after message sent: " +
              str(self.sim_env.now))

        # Send message - here send your message with your message_id
        yield self.sim_env.process(
            self.transp_lay.send_msg(sender_id, message_id,
                                     wrapped_cipher_message))

    def _init_layers(self, sim_env, MessageClass):
        ''' Initializes the software layers 
            
            Input:  sim_env                        simpy.Environment        environment of this component                      
                    MessageClass                   AbstractBusMessage       class of the messages  how they are sent on the CAN Bus
            Output: -                   
        '''

        # create layers
        self.physical_lay = StdPhysicalLayer(sim_env)
        self.datalink_lay = StdDatalinkLayer(sim_env)
        self.transp_lay = SegmentTransportLayer(sim_env, MessageClass)

        # interconnect layers
        self.datalink_lay.physical_lay = self.physical_lay
        self.transp_lay.datalink_lay = self.datalink_lay

    @property
    def ecu_id(self):
        return self._ecu_id

    @ecu_id.setter
    def ecu_id(self, value):
        self._ecu_id = value

    def monitor_update(self):
        ''' updates the monitor connected to this ecu
            
            Input:    -
            Output:   monitor_list    RefList    list of MonitorInputs
        '''
        # register Monitoring tags to track
        #G().register_eventline_tags(self._tags)

        items_1 = len(
            self.transp_lay.datalink_lay.controller.receive_buffer.items)
        items_2 = self.transp_lay.datalink_lay.transmit_buffer_size

        G().mon(
            self.monitor_list,
            MonitorInput(items_1, MonitorTags.BT_ECU_RECEIVE_BUFFER,
                         self._ecu_id, self.sim_env.now))
        G().mon(
            self.monitor_list,
            MonitorInput(items_2, MonitorTags.BT_ECU_TRANSMIT_BUFFER,
                         self._ecu_id, self.sim_env.now))

        self.monitor_list.clear_on_access(
        )  # on the next access the list will be cleared
        return self.monitor_list.get()
class MyProtocolCommModule(AbstractCommModule):
    
    def __init__(self, sim_env, ecu_id):
        ''' Constructor
            
            Input:  ecu_id         string                   id of the corresponding AbstractECU
                    sim_env        simpy.Environment        environment of this component
            Output:  -
        '''
        AbstractCommModule.__init__(self, sim_env)

        # local parameters
        self._ecu_id = ecu_id
        self._jitter_in = 1
        self.monitor_list = RefList()
        
        # initialize
        self._init_layers(self.sim_env, self.MessageClass)
        
        # add tags
        self._tags = ["AUTH_SEND_TIME_BEFORE_ENCRYPTION", "AUTH_SEND_TIME_AFTER_ENCRYPTION", "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", "AUTH_RECEIVE_TIME_AFTER_DECRYPTION"]        
    
    def receive_msg(self):
        
        while True:
                        
            # receive from lower layer
            [message_id, message_data] = yield self.sim_env.process(self.transp_lay.receive_msg())        
        
            # receiver information    
            print("\n\nRECEIVER\nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + self._ecu_id + "\nReceived message:\n - ID: " + str(message_id) +"\n - Content: " + message_data.get())
        
            # Assume it takes 0.5 seconds to e.g. decrypt this message
            uid = uuid.uuid4()
            # BEFORE PROCESSING
            print("\nECU "+ str(self._ecu_id) +"Time before message received: "+ str(self.sim_env.now))
            G().mon(self.monitor_list, MonitorInput([], "AUTH_RECEIVE_TIME_BEFORE_DECRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message_data.get(), message_data.padded_size, 432, uid))
            
            yield self.sim_env.timeout(0.5)
            
            # AFTER PROCESSING
            print("\nECU "+ str(self._ecu_id) +"Time after message received: "+ str(self.sim_env.now))            
            G().mon(self.monitor_list, MonitorInput([], "AUTH_RECEIVE_TIME_AFTER_DECRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message_data.get(), message_data.padded_size, 432, uid))
            
        # push to higher layer
        return [message_id, message_data]

    def send_msg(self, sender_id, message_id, message):
        # Sender information
        print("\n\nSENDER - \nTime: "+str(self.sim_env.now)+"--Communication Layer: \nI am ECU " + sender_id + "\nSending message:\n - ID: " + str(message_id)+"\n - Content: " + message.get())
                
        # Message to be send 
        print("\nSize of the message we want to send: "+ str(message.padded_size))
        print("\nContent of the message: "+ str(message.get()))
        
        # Assume it takes 0.2 seconds to e.g. encrypt this message
        uid = uuid.uuid4()
        # BEFORE PROCESSING
        print("\nECU "+ str(self._ecu_id) +"Time before message sent: "+ str(self.sim_env.now))
        G().mon(self.monitor_list, MonitorInput([], "AUTH_SEND_TIME_BEFORE_ENCRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message.get(), message.padded_size, 432, uid))
        
        yield self.sim_env.timeout(0.2)
        
        # AFTER PROCESSING
        G().mon(self.monitor_list, MonitorInput([], "AUTH_SEND_TIME_AFTER_ENCRYPTION", self._ecu_id, self.sim_env.now, 123, message_id, message.get(), message.padded_size, 432, uid))
        print("\nECU "+ str(self._ecu_id) +"Time after message sent: "+ str(self.sim_env.now))

        # Send message - here send your message with your message_id
        yield  self.sim_env.process(self.transp_lay.send_msg(sender_id, message_id, message))

            
    def _init_layers(self, sim_env, MessageClass):
        ''' Initializes the software layers 
            
            Input:  sim_env                        simpy.Environment        environment of this component                      
                    MessageClass                   AbstractBusMessage       class of the messages  how they are sent on the CAN Bus
            Output: -                   
        '''
        
        # create layers
        self.physical_lay = StdPhysicalLayer(sim_env)         
        self.datalink_lay = StdDatalinkLayer(sim_env) 
        self.transp_lay = SegmentTransportLayer(sim_env, MessageClass)
   
        # interconnect layers             
        self.datalink_lay.physical_lay = self.physical_lay        
        self.transp_lay.datalink_lay = self.datalink_lay           
        
        
    @property
    def ecu_id(self):
        return self._ecu_id
               
    @ecu_id.setter    
    def ecu_id(self, value):
        self._ecu_id = value          

    def monitor_update(self):
        ''' updates the monitor connected to this ecu
            
            Input:    -
            Output:   monitor_list    RefList    list of MonitorInputs
        '''
        # register Monitoring tags to track
        #G().register_eventline_tags(self._tags)
        
        items_1 = len(self.transp_lay.datalink_lay.controller.receive_buffer.items)
        items_2 = self.transp_lay.datalink_lay.transmit_buffer_size
        
        G().mon(self.monitor_list, MonitorInput(items_1, MonitorTags.BT_ECU_RECEIVE_BUFFER, self._ecu_id, self.sim_env.now))
        G().mon(self.monitor_list, MonitorInput(items_2, MonitorTags.BT_ECU_TRANSMIT_BUFFER, self._ecu_id, self.sim_env.now))
        
        self.monitor_list.clear_on_access()  # on the next access the list will be cleared        
        return self.monitor_list.get()