|  |  |  | #include "stdafx.h"
 | 
					
						
							|  |  |  | #include "MessageTx_ISO15765.h"
 | 
					
						
							|  |  |  | #include "constants_ISO15765.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //in microseconsa
 | 
					
						
							|  |  |  | #define TIMEOUT_FC 250000 //Flow Control
 | 
					
						
							|  |  |  | #define TIMEOUT_CF 250000 //Consecutive Frames
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MessageTx_ISO15765::MessageTx_ISO15765(
 | 
					
						
							|  |  |  | 	std::shared_ptr<J2534Connection> connection_in,
 | 
					
						
							|  |  |  | 	PASSTHRU_MSG& to_send,
 | 
					
						
							|  |  |  | 	std::shared_ptr<J2534MessageFilter> filter
 | 
					
						
							|  |  |  | ) : MessageTxTimeoutable(connection_in, to_send), filter(filter), frames_sent(0),
 | 
					
						
							|  |  |  | consumed_count(0), txInFlight(FALSE), sendAll(FALSE), block_size(0), numWaitFrames(0), didtimeout(FALSE), issuspended(FALSE){
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CANid = ((uint8_t)fullmsg.Data[0]) << 24 | ((uint8_t)fullmsg.Data[1]) << 16 |
 | 
					
						
							|  |  |  | 		((uint8_t)fullmsg.Data[2]) << 8 | ((uint8_t)fullmsg.Data[3]);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	payload = fullmsg.Data.substr(addressLength());
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (check_bmask(fullmsg.TxFlags, ISO15765_ADDR_TYPE))
 | 
					
						
							|  |  |  | 		data_prefix = fullmsg.Data[4];
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (payload.size() <= (7 - data_prefix.size())) {
 | 
					
						
							|  |  |  | 		isMultipart = FALSE;
 | 
					
						
							|  |  |  | 		auto framepayload = data_prefix + std::string(1, (char)payload.size()) + payload;
 | 
					
						
							|  |  |  | 		if (check_bmask(this->fullmsg.TxFlags, ISO15765_FRAME_PAD))
 | 
					
						
							|  |  |  | 			framepayload += std::string(8 - framepayload.size(), '\x00');
 | 
					
						
							|  |  |  | 		framePayloads.push_back(framepayload);
 | 
					
						
							|  |  |  | 	} else {
 | 
					
						
							|  |  |  | 		isMultipart = TRUE;
 | 
					
						
							|  |  |  | 		unsigned long first_payload_len = 6 - data_prefix.size(); // 5 or 6
 | 
					
						
							|  |  |  | 		std::string framepayload = data_prefix +
 | 
					
						
							|  |  |  | 			(char)(0x10 | ((payload.size() >> 8) & 0xF)) +
 | 
					
						
							|  |  |  | 			(char)(payload.size() & 0xFF) +
 | 
					
						
							|  |  |  | 			payload.substr(0, first_payload_len);
 | 
					
						
							|  |  |  | 		framePayloads.push_back(framepayload);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		unsigned int pktnum = 1;
 | 
					
						
							|  |  |  | 		uint8_t CFDatSize = 7 - data_prefix.size();
 | 
					
						
							|  |  |  | 		while (TRUE) {
 | 
					
						
							|  |  |  | 			framepayload = data_prefix + (char)(0x20 | (pktnum % 0x10)) +
 | 
					
						
							|  |  |  | 				payload.substr(first_payload_len + (CFDatSize * (pktnum-1)), CFDatSize);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (check_bmask(this->fullmsg.TxFlags, ISO15765_FRAME_PAD))
 | 
					
						
							|  |  |  | 				framepayload += std::string(8 - framepayload.size(), '\x00');
 | 
					
						
							|  |  |  | 			framePayloads.push_back(framepayload);
 | 
					
						
							|  |  |  | 			if (first_payload_len + (CFDatSize * pktnum) >= payload.size()) break;
 | 
					
						
							|  |  |  | 			pktnum++;
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | };
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | unsigned int MessageTx_ISO15765::addressLength() {
 | 
					
						
							|  |  |  | 	return check_bmask(fullmsg.TxFlags, ISO15765_ADDR_TYPE) ? 5 : 4;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void MessageTx_ISO15765::execute() {
 | 
					
						
							|  |  |  | 	if (didtimeout || issuspended) return;
 | 
					
						
							|  |  |  | 	if (this->frames_sent >= this->framePayloads.size()) return;
 | 
					
						
							|  |  |  | 	if (block_size == 0 && !sendAll && this->frames_sent > 0) return;
 | 
					
						
							|  |  |  | 	if (block_size > 0 && !sendAll) block_size--;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (auto conn_sp = this->connection.lock()) {
 | 
					
						
							|  |  |  | 		if (auto panda_dev_sp = conn_sp->getPandaDev()) {
 | 
					
						
							|  |  |  | 			auto& outFramePayload = this->framePayloads[this->frames_sent];
 | 
					
						
							|  |  |  | 			if (panda_dev_sp->panda->can_send(this->CANid, check_bmask(this->fullmsg.TxFlags, CAN_29BIT_ID),
 | 
					
						
							|  |  |  | 				(const uint8_t*)outFramePayload.c_str(), (uint8_t)outFramePayload.size(), panda::PANDA_CAN1) == FALSE) {
 | 
					
						
							|  |  |  | 				return;
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			this->txInFlight = TRUE;
 | 
					
						
							|  |  |  | 			this->frames_sent++;
 | 
					
						
							|  |  |  | 			panda_dev_sp->txMsgsAwaitingEcho.push(shared_from_this());
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //Returns TRUE if receipt is consumed by the msg, FALSE otherwise.
 | 
					
						
							|  |  |  | BOOL MessageTx_ISO15765::checkTxReceipt(J2534Frame frame) {
 | 
					
						
							|  |  |  | 	if (!txInFlight) return FALSE;
 | 
					
						
							|  |  |  | 	if (frame.Data.size() >= addressLength() + 1 && (frame.Data[addressLength()] & 0xF0) == FRAME_FLOWCTRL) return FALSE;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (frame.Data == fullmsg.Data.substr(0, 4) + framePayloads[frames_sent - 1] &&
 | 
					
						
							|  |  |  | 		((this->fullmsg.TxFlags & CAN_29BIT_ID) == (frame.RxStatus & CAN_29BIT_ID))) { //Check receipt is expected
 | 
					
						
							|  |  |  | 		txInFlight = FALSE; //Received the expected receipt. Allow another msg to be sent.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (this->recvCount == 0 && this->framePayloads.size() > 1)
 | 
					
						
							|  |  |  | 			scheduleTimeout(TIMEOUT_FC);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (frames_sent == framePayloads.size()) { //Check message done
 | 
					
						
							|  |  |  | 			if (auto conn_sp = std::static_pointer_cast<J2534Connection_ISO15765>(this->connection.lock())) {
 | 
					
						
							|  |  |  | 				unsigned long flags = (filter == nullptr) ? fullmsg.TxFlags : this->filter->flags;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				J2534Frame outframe(ISO15765);
 | 
					
						
							|  |  |  | 				outframe.Timestamp = frame.Timestamp;
 | 
					
						
							|  |  |  | 				outframe.RxStatus = TX_MSG_TYPE | TX_INDICATION | (flags & (ISO15765_ADDR_TYPE | CAN_29BIT_ID));
 | 
					
						
							|  |  |  | 				outframe.Data = frame.Data.substr(0, addressLength());
 | 
					
						
							|  |  |  | 				conn_sp->addMsgToRxQueue(outframe);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (conn_sp->loopback) {
 | 
					
						
							|  |  |  | 					J2534Frame outframe(ISO15765);
 | 
					
						
							|  |  |  | 					outframe.Timestamp = frame.Timestamp;
 | 
					
						
							|  |  |  | 					outframe.RxStatus = TX_MSG_TYPE | (flags & (ISO15765_ADDR_TYPE | CAN_29BIT_ID));
 | 
					
						
							|  |  |  | 					outframe.Data = this->fullmsg.Data;
 | 
					
						
							|  |  |  | 					conn_sp->addMsgToRxQueue(outframe);
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			} //TODO what if fails
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							|  |  |  | 			//Restart timeout if we are waiting for a flow control frame.
 | 
					
						
							|  |  |  | 			//FC frames are required when we are not sending all, the
 | 
					
						
							|  |  |  | 			//current block_size batch has not been sent, a FC message has
 | 
					
						
							|  |  |  | 			//already been received (differentiating from first frame), the
 | 
					
						
							|  |  |  | 			//message is not finished, and there is more than one frame in
 | 
					
						
							|  |  |  | 			//the message.
 | 
					
						
							|  |  |  | 			if (block_size == 0 && recvCount != 0 && !sendAll && !this->isFinished() && this->framePayloads.size() > 1)
 | 
					
						
							|  |  |  | 				scheduleTimeout(TIMEOUT_CF);
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		return TRUE;
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return FALSE;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BOOL MessageTx_ISO15765::isFinished() {
 | 
					
						
							|  |  |  | 	return this->frames_sent == this->framePayloads.size() && !txInFlight;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BOOL MessageTx_ISO15765::txReady() {
 | 
					
						
							|  |  |  | 	return block_size > 0 || sendAll || this->frames_sent == 0;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void MessageTx_ISO15765::reset() {
 | 
					
						
							|  |  |  | 	frames_sent = 0;
 | 
					
						
							|  |  |  | 	consumed_count = 0;
 | 
					
						
							|  |  |  | 	block_size = 0;
 | 
					
						
							|  |  |  | 	txInFlight = FALSE;
 | 
					
						
							|  |  |  | 	sendAll = FALSE;
 | 
					
						
							|  |  |  | 	numWaitFrames = 0;
 | 
					
						
							|  |  |  | 	didtimeout = FALSE;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void MessageTx_ISO15765::onTimeout() {
 | 
					
						
							|  |  |  | 	didtimeout = TRUE;
 | 
					
						
							|  |  |  | 	if (auto conn_sp = std::static_pointer_cast<J2534Connection_ISO15765>(this->connection.lock())) {
 | 
					
						
							|  |  |  | 		if (auto panda_dev_sp = conn_sp->getPandaDev()) {
 | 
					
						
							|  |  |  | 			panda_dev_sp->removeConnectionTopAction(conn_sp, shared_from_this());
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void MessageTx_ISO15765::flowControlContinue(uint8_t block_size, std::chrono::microseconds separation_time) {
 | 
					
						
							|  |  |  | 	this->issuspended = FALSE;
 | 
					
						
							|  |  |  | 	this->block_size = block_size;
 | 
					
						
							|  |  |  | 	this->delay = separation_time;
 | 
					
						
							|  |  |  | 	this->sendAll = block_size == 0;
 | 
					
						
							|  |  |  | 	this->recvCount++;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void MessageTx_ISO15765::flowControlWait(unsigned long N_WFTmax) {
 | 
					
						
							|  |  |  | 	this->issuspended = TRUE;
 | 
					
						
							|  |  |  | 	this->recvCount++;
 | 
					
						
							|  |  |  | 	this->numWaitFrames++;
 | 
					
						
							|  |  |  | 	this->sendAll = FALSE;
 | 
					
						
							|  |  |  | 	this->block_size = block_size;
 | 
					
						
							|  |  |  | 	this->delay = std::chrono::microseconds(0);
 | 
					
						
							|  |  |  | 	//Docs are vague on if 0 means NO WAITS ALLOWED or NO LIMIT TO WAITS.
 | 
					
						
							|  |  |  | 	//It is less likely to cause issue if NO LIMIT is assumed.
 | 
					
						
							|  |  |  | 	if (N_WFTmax > 0 && this->numWaitFrames > N_WFTmax) {
 | 
					
						
							|  |  |  | 		this->onTimeout(); //Trigger self destruction of message.
 | 
					
						
							|  |  |  | 	} else {
 | 
					
						
							|  |  |  | 		scheduleTimeout(TIMEOUT_FC);
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void MessageTx_ISO15765::flowControlAbort() {
 | 
					
						
							|  |  |  | 	this->recvCount++; //Invalidate future timeout actions.
 | 
					
						
							|  |  |  | 	this->onTimeout(); //Trigger self destruction of message.
 | 
					
						
							|  |  |  | }
 |