def notify(self, can_id, data, timestamp): """Feed incoming CAN message into this ecu. If a custom interface is used, this function must be called for each 29-bit standard message read from the CAN bus. :param int can_id: CAN-ID of the message (always 29-bit) :param bytearray data: Data part of the message (0 - 8 bytes) :param float timestamp: The timestamp field in a CAN message is a floating point number representing when the message was received since the epoch in seconds. Where possible this will be timestamped in hardware. """ mid = j1939.MessageId(can_id=can_id) pgn = j1939.ParameterGroupNumber() pgn.from_message_id(mid) if pgn.is_pdu2_format: # direct broadcast self.notify_subscribers(mid.priority, pgn.value, mid.source_address, j1939.ParameterGroupNumber.Address.GLOBAL, timestamp, data) return # peer to peer # pdu_specific is destination Address pgn_value = pgn.value & 0x1FF00 dest_address = pgn.pdu_specific # may be Address.GLOBAL # iterate all CAs to check if we have to handle this destination address if dest_address != j1939.ParameterGroupNumber.Address.GLOBAL: reject = True for ca in self._cas: if ca.message_acceptable(dest_address): reject = False break if reject == True: return if pgn_value == j1939.ParameterGroupNumber.PGN.ADDRESSCLAIM: for ca in self._cas: ca._process_addressclaim(mid, data, timestamp) elif pgn_value == j1939.ParameterGroupNumber.PGN.REQUEST: for ca in self._cas: if ca.message_acceptable(dest_address): ca._process_request(mid, dest_address, data, timestamp) elif pgn_value == j1939.ParameterGroupNumber.PGN.TP_CM: self._process_tp_cm(mid, dest_address, data, timestamp) elif pgn_value == j1939.ParameterGroupNumber.PGN.DATATRANSFER: self._process_tp_dt(mid, dest_address, data, timestamp) else: self.notify_subscribers(mid.priority, pgn_value, mid.source_address, dest_address, timestamp, data) return
def _send_address_claimed(self, address): # TODO: Normally the (initial) address claimed message must not be an auto repeat message. # We have to use a single-shot message instead! # After a (send-)error occurs we have to wait 0..153 msec before repeating. pgn = j1939.ParameterGroupNumber(0, 238, j1939.ParameterGroupNumber.Address.GLOBAL) mid = j1939.MessageId(priority=6, parameter_group_number=pgn.value, source_address=address) data = self._name.bytes self._ecu.send_message(mid.can_id, data)
def send_pgn(self, data_page, pdu_format, pdu_specific, priority, src_address, data): pgn = j1939.ParameterGroupNumber(data_page, pdu_format, pdu_specific) if len(data) <= 8: # send normal message mid = j1939.MessageId(priority=priority, parameter_group_number=pgn.value, source_address=src_address) self.send_message(mid.can_id, data) else: # init sequence buffer_hash = self._buffer_hash(src_address, pdu_specific) if buffer_hash in self._snd_buffer: # There is already a sequence active for this pair return False message_size = len(data) num_packets = int(message_size / 7) if (message_size % 7 == 0) else int(message_size / 7) + 1 if pdu_specific == j1939.ParameterGroupNumber.Address.GLOBAL: # send BAM self.send_tp_bam(src_address, priority, pgn.value, message_size, num_packets) # init new buffer for this connection self._snd_buffer[buffer_hash] = { "pgn": pgn.value, "priority": priority, "message_size": message_size, "num_packages": num_packets, "data": data, "state": ElectronicControlUnit.SendBufferState.SENDING_BM, "deadline": time.time() + ElectronicControlUnit.Timeout.Tb, 'src_address': src_address, 'dest_address': pdu_specific, 'next_packet_to_send': 0, } else: # send RTS/CTS # init new buffer for this connection self._snd_buffer[buffer_hash] = { "pgn": pgn.value, "priority": priority, "message_size": message_size, "num_packages": num_packets, "data": data, "state": ElectronicControlUnit.SendBufferState.WAITING_CTS, "deadline": time.time() + ElectronicControlUnit.Timeout.T3, 'src_address': src_address, 'dest_address': pdu_specific, } self.send_tp_rts(src_address, pdu_specific, priority, pgn.value, message_size, num_packets) self._job_thread_wakeup() return True
def send_message(self, priority, parameter_group_number, data): if self.state != ControllerApplication.State.NORMAL: raise RuntimeError( "Could not send message unless address claiming has finished") mid = j1939.MessageId(priority=priority, parameter_group_number=parameter_group_number, source_address=self._device_address) self._ecu.send_message(mid.can_id, data)
def send_acknowledgement(self, control_byte, group_function_value, address_acknowledged, pgn): data = [ control_byte, group_function_value, 0xFF, 0xFF, address_acknowledged, (pgn & 0xFF), ((pgn >> 8) & 0xFF), ((pgn >> 16) & 0xFF) ] mid = j1939.MessageId(priority=6, parameter_group_number=0x00E800, source_address=255) self.send_message(mid.can_id, data)
def send_tp_abort(self, src_address, dest_address, reason, pgn_value): pgn = j1939.ParameterGroupNumber(0, 236, dest_address) mid = j1939.MessageId(priority=7, parameter_group_number=pgn.value, source_address=src_address) data = [ ElectronicControlUnit.ConnectionMode.ABORT, reason, 0xFF, 0xFF, 0xFF, pgn_value & 0xFF, (pgn_value >> 8) & 0xFF, (pgn_value >> 16) & 0xFF ] self.send_message(mid.can_id, data)
def send_tp_rts(self, src_address, dest_address, priority, pgn_value, message_size, num_packets): pgn = j1939.ParameterGroupNumber(0, 236, dest_address) mid = j1939.MessageId(priority=priority, parameter_group_number=pgn.value, source_address=src_address) data = [ ElectronicControlUnit.ConnectionMode.RTS, message_size & 0xFF, (message_size >> 8) & 0xFF, num_packets, 0xFF, pgn_value & 0xFF, (pgn_value >> 8) & 0xFF, (pgn_value >> 16) & 0xFF ] self.send_message(mid.can_id, data)
def send_tp_dt(self, src_address, dest_address, data): pgn = j1939.ParameterGroupNumber(0, 235, dest_address) mid = j1939.MessageId(priority=7, parameter_group_number=pgn.value, source_address=src_address) self.send_message(mid.can_id, data)
def send_pgn(self, data_page, pdu_format, pdu_specific, priority, src_address, data): pgn = j1939.ParameterGroupNumber(data_page, pdu_format, pdu_specific) if len(data) <= 8: # send normal message mid = j1939.MessageId(priority=priority, parameter_group_number=pgn.value, source_address=src_address) self.send_message(mid.can_id, data) else: # if the PF is between 0 and 239, the message is destination dependent when pdu_specific != 255 # if the PF is between 240 and 255, the message can only be broadcast if (pdu_specific == j1939.ParameterGroupNumber.Address.GLOBAL ) or j1939.ParameterGroupNumber(0, pdu_format, pdu_specific).is_pdu2_format: dest_address = j1939.ParameterGroupNumber.Address.GLOBAL else: dest_address = pdu_specific # init sequence # known limitation: only one BAM can be sent in parallel to a destination node buffer_hash = self._buffer_hash(src_address, dest_address) if buffer_hash in self._snd_buffer: # There is already a sequence active for this pair return False message_size = len(data) num_packets = int(message_size / 7) if (message_size % 7 == 0) else int(message_size / 7) + 1 # if the PF is between 240 and 255, the message can only be broadcast if dest_address == j1939.ParameterGroupNumber.Address.GLOBAL: # send BAM self.send_tp_bam(src_address, priority, pgn.value, message_size, num_packets) # init new buffer for this connection self._snd_buffer[buffer_hash] = { "pgn": pgn.value, "priority": priority, "message_size": message_size, "num_packages": num_packets, "data": data, "state": ElectronicControlUnit.SendBufferState.SENDING_BM, "deadline": time.time() + ElectronicControlUnit.Timeout.Tb, 'src_address': src_address, 'dest_address': j1939.ParameterGroupNumber.Address.GLOBAL, 'next_packet_to_send': 0, } else: # send RTS/CTS pgn.pdu_specific = 0 # this is 0 for peer-to-peer transfer # init new buffer for this connection self._snd_buffer[buffer_hash] = { "pgn": pgn.value, "priority": priority, "message_size": message_size, "num_packages": num_packets, "data": data, "state": ElectronicControlUnit.SendBufferState.WAITING_CTS, "deadline": time.time() + ElectronicControlUnit.Timeout.T3, 'src_address': src_address, 'dest_address': pdu_specific, } self.send_tp_rts(src_address, pdu_specific, priority, pgn.value, message_size, num_packets) self._job_thread_wakeup() return True