#include "J1939Tp.h" /** @req J1939TP0003*/\r
#include "CanIf.h" /** @req J1939TP0172 */\r
#include "J1939Tp_Internal.h"\r
-#include "PdurR_J1939Tp.h"\r
+#include "PduR.h"\r
+#include <string.h>\r
\r
/** @req J1939TP0019 */\r
static J1939Tp_Internal_GlobalStateInfoType globalState = {\r
.State = J1939TP_OFF,\r
};\r
static const J1939Tp_ConfigType* J1939Tp_ConfigPtr;\r
-static J1939Tp_Internal_TxPgStateType txPgState[J1939TP_TX_PG_COUNT];\r
-\r
-\r
+static J1939Tp_Internal_TxPgStateInfoType txPgState[J1939TP_PG_COUNT];\r
\r
/** @req J1939TP0087 */\r
void J1939Tp_Init(const J1939Tp_ConfigType* ConfigPtr) {\r
#if (J1939TP_DEV_ERROR_DETECT == STD_ON)\r
- if (globalState == J1939TP_ON) {\r
+ if (globalState.State == J1939TP_ON) {\r
/** @req J1939TP0026 */\r
J1939Tp_Internal_ReportError(J1939TP_INIT_ID, J1939TP_E_REINIT);\r
return;\r
}\r
#endif\r
+ for (int i = 0; i < J1939TP_PG_COUNT; i++) {\r
+ J1939Tp_Internal_SetStatePg(i,J1939TP_IDLE);\r
+ }\r
J1939Tp_ConfigPtr = ConfigPtr;\r
globalState.State = J1939TP_ON; /** @req J1939TP0022 */\r
}\r
-void J1939Tp_RxIndication(PduIdType RxPduId, PduInfoType* PduInfoPtr) {\r
\r
+void J1939Tp_RxIndication(PduIdType RxPduId, PduInfoType* PduInfoPtr) {\r
+ J1939Tp_Internal_TxPgStateInfoType* txPgState = J1939Tp_Internal_GetPg(RxPduId);\r
+ uint8 NumPacketsToSend = 0;\r
+ switch (txPgState->State) {\r
+ case J1939TP_WAITING_FOR_CTS:\r
+ if (J1939Tp_Internal_CheckValidCts(PduInfoPtr,&NumPacketsToSend)) {\r
+ txPgState->DtToSendBeforeCtsCount = NumPacketsToSend;\r
+ txPgState->SentDtCount = 0;\r
+ J1939Tp_Internal_SetStatePg(RxPduId, J1939TP_SENDING_DT);\r
+ J1939Tp_Internal_SendDt(RxPduId, txPgState);\r
+ }\r
+ break;\r
+ case J1939TP_WAITING_FOR_END_OF_MSG_ACK:\r
+ J1939Tp_Internal_CheckValidEndOfMsgAck(txPgState,PduInfoPtr);\r
+ default:\r
+ break;\r
+ }\r
}\r
\r
-void J1939_MainFunction(void) {\r
- for (int i = 0; i < J1939TP_TX_PG_COUNT; i++) {\r
- switch (J1939Tp_Internal_GetTxPg(i)->State) {\r
- case J1939TP_WAITING_FOR_CM:\r
-\r
+void J1939Tp_MainFunction(void) {\r
+ for (int i = 0; i < J1939TP_PG_COUNT; i++) {\r
+ J1939Tp_Internal_TxPgStateInfoType* txPgState = J1939Tp_Internal_GetPg(i);\r
+ switch (txPgState->State) {\r
+ case J1939TP_WAITING_FOR_CTS:\r
+ J1939Tp_Internal_IncAndCheckT3Timer(i,txPgState);\r
+ break;\r
+ case J1939TP_WAITING_FOR_END_OF_MSG_ACK:\r
+ J1939Tp_Internal_IncAndCheckT3Timer(i,txPgState);\r
break;\r
default:\r
break;\r
}\r
}\r
}\r
+void J1939Tp_TxConfirmation(PduIdType TxPduId) {\r
+ J1939Tp_Internal_TxPgStateInfoType* pgState = J1939Tp_Internal_GetPg(TxPduId);\r
+ switch (pgState->State) {\r
+ case J1939TP_SENDING_DT:\r
+ if (J1939Tp_Internal_WaitForEndOfMsgAck(pgState)) {\r
+ J1939Tp_Internal_ResetT3(pgState);\r
+ J1939Tp_Internal_SetStatePg(TxPduId, J1939TP_WAITING_FOR_END_OF_MSG_ACK);\r
+ } else if (J1939Tp_Internal_WaitForCts(pgState)) {\r
+ J1939Tp_Internal_SetStatePg(TxPduId, J1939TP_WAITING_FOR_CTS);\r
+ } else {\r
+ J1939Tp_Internal_SendDt(TxPduId,pgState);\r
+ }\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+}\r
\r
/** @req J1939TP0180 */\r
Std_ReturnType J1939Tp_ChangeParameterRequest(PduIdType SduId, TPParameterType Parameter, uint16 value) {\r
/** @req J1939TP0096 */\r
Std_ReturnType J1939Tp_Transmit(PduIdType TxSduId, const PduInfoType* TxInfoPtr) {\r
#if J1939TP_DEV_ERROR_DETECT\r
- if (globalState == J1939TP_OFF) {\r
+ if (globalState.State == J1939TP_OFF) {\r
J1939Tp_Internal_ReportError(J1939TP_TRANSMIT_ID,J1939TP_E_UNINIT);\r
}\r
#endif\r
- if (TxInfoPtr->SduLength <= 8) {\r
- PduR_J1939TpProvideTxBuffer(TxSduId, &TxInfoPtr,0);\r
- if (CanIf_Transmit(TxSduId,TxInfoPtr) == E_OK) {\r
+ if (TxInfoPtr->SduLength <= 8) { // direct transmit\r
+ PduInfoType* ToSendTxInfoPtr;\r
+ PduR_J1939TpProvideTxBuffer(TxSduId, &ToSendTxInfoPtr, TxInfoPtr->SduLength);\r
+ if (CanIf_Transmit(TxSduId,ToSendTxInfoPtr) == E_OK) {\r
PduR_J1939TpTxConfirmation(TxSduId, NTFRSLT_OK);\r
} else {\r
PduR_J1939TpTxConfirmation(TxSduId, NTFRSLT_E_NOT_OK);\r
}\r
} else {\r
- uint32 pgn = J1939Tp_Internal_ConfGetTxPg(TxSduId)->Pgn;\r
+ uint32 pgn = J1939Tp_Internal_ConfGetPg(TxSduId)->Pgn;\r
uint8 pf = J1939Tp_Internal_GetPf(pgn);\r
J1939Tp_ProtocolType protocol = J1939Tp_Internal_GetProtocol(pf);\r
switch (protocol) { /** @req J1939TP0039*/\r
break;\r
case J1939TP_PROTOCOL_CMDT:\r
J1939Tp_Internal_SendRts(TxSduId,TxInfoPtr);\r
- J1939Tp_Internal_SetStateTxPg(TxSduId,J1939TP_RTS_SENT);\r
+ J1939Tp_Internal_TxPgStateInfoType* txPgState = J1939Tp_Internal_GetPg(TxSduId);\r
+ J1939Tp_Internal_ResetT3(txPgState);\r
+ txPgState->TotalMessageSize = TxInfoPtr->SduLength;\r
+ txPgState->TotalBytesSent = 0;\r
+ txPgState->TotalSentDtCount = 0;\r
+ txPgState->PduRPdu = TxSduId;\r
+ J1939Tp_Internal_SetStatePg(TxSduId,J1939TP_WAITING_FOR_CTS);\r
break;\r
}\r
}\r
return E_OK;\r
}\r
\r
-void J1939Tp_TxIndication(PduIdType TxPduId) {\r
- J1939Tp_Internal_TxPgStateInfoType* pgState = J1939Tp_Internal_GetTxPg(TxPduId);\r
- switch (pgState) {\r
- case J1939TP_RTS_SENT:\r
- J1939Tp_Internal_SetStateTxPg(TxSduId,J1939TP_WAIT_FOR_CM);\r
- break;\r
- default:\r
- break;\r
- }\r
+static inline const J1939Tp_PgType* J1939Tp_Internal_ConfGetPg(PduIdType pduId) {\r
+ return &(J1939Tp_ConfigPtr->Pgs[pduId]);\r
}\r
-static inline void J1939Tp_Internal_SetStateTxPg(uint32 txPduId,J1939Tp_Internal_TxPgStateType state) {\r
+static inline boolean J1939Tp_Internal_WaitForCts(J1939Tp_Internal_TxPgStateInfoType* pgState) {\r
+ return pgState->SentDtCount == pgState->DtToSendBeforeCtsCount;\r
+}\r
+\r
+static inline boolean J1939Tp_Internal_WaitForEndOfMsgAck(J1939Tp_Internal_TxPgStateInfoType* pgState) {\r
+ return pgState->TotalMessageSize == pgState->TotalBytesSent;\r
+}\r
+\r
+static inline void J1939Tp_Internal_SetStatePg(uint32 txPduId,J1939Tp_Internal_TxPgStateType state) {\r
txPgState[txPduId].State = state;\r
}\r
-static inline J1939Tp_Internal_TxPgStateInfoType* J1939Tp_Internal_GetTxPg(uint32 txPduId) {\r
+static inline J1939Tp_Internal_TxPgStateInfoType* J1939Tp_Internal_GetPg(uint32 txPduId) {\r
return &(txPgState[txPduId]);\r
}\r
static inline const J1939Tp_ChannelType* J1939Tp_Internal_ConfGetTxChannel(uint32 txPduId) {\r
- return J1939Tp_ConfigPtr->TxPgs[txPduId].Channel;\r
+ return J1939Tp_ConfigPtr->Pgs[txPduId].Channel;\r
+}\r
+\r
+\r
+\r
+static inline boolean J1939Tp_Internal_CheckValidEndOfMsgAck(J1939Tp_Internal_TxPgStateInfoType* pgState, PduInfoType* PduInfoPtr) {\r
+ if (PduInfoPtr->SduLength != ENDOFMSGACK_SIZE) {\r
+ return false;\r
+ }\r
+ if (PduInfoPtr->SduDataPtr[ENDOFMSGACK_BYTE_CONTROL] != ENDOFMSGACK_CONTROL_VALUE) {\r
+ return false;\r
+ }\r
+ return true;\r
+}\r
+static inline void J1939Tp_Internal_IncAndCheckT3Timer(PduIdType pduId, J1939Tp_Internal_TxPgStateInfoType* pgState) {\r
+ txPgState->T3 += (J1939TP_MAIN_FUNCTION_PERIOD*1000);\r
+ if (txPgState->T3 > J1939TP_T3_TIMEOUT_MS) {\r
+ txPgState->State = J1939TP_IDLE;\r
+ PduR_J1939TpTxConfirmation(J1939Tp_Internal_ConfGetPg(pduId)->NSdu,NTFRSLT_E_NOT_OK);\r
+ /* TODO: Call det here */\r
+ }\r
}\r
+static uint8 J1939Tp_Internal_GetPf(uint32 pgn) {\r
+ return (pgn && 0x0000FF00) >> 8;\r
+}\r
+static J1939Tp_ProtocolType J1939Tp_Internal_GetProtocol(uint8 pf) {\r
+ if (pf < 240) {\r
+ return J1939TP_PROTOCOL_CMDT;\r
+ } else {\r
+ return J1939TP_PROTOCOL_BAM;\r
+ }\r
+}\r
+static inline boolean J1939Tp_Internal_CheckValidCts(PduInfoType* PduInfoPtr,uint8* NumPackets) {\r
+ /*\r
+ if (PduInfoPtr->SduLength != CTS_SIZE) {\r
+ return false;\r
+ }\r
+ */\r
+ if (PduInfoPtr->SduDataPtr[CTS_BYTE_CONTROL] != CTS_CONTROL_VALUE) {\r
+ return false;\r
+ }\r
+ if (PduInfoPtr->SduDataPtr[CTS_BYTE_NUM_PACKETS] < 1) {\r
+ return false;\r
+ }\r
+ *NumPackets = PduInfoPtr->SduDataPtr[CTS_BYTE_NUM_PACKETS];\r
+ return true;\r
+}\r
+\r
+static void inline J1939Tp_Internal_SendDt(PduIdType TxPduId, J1939Tp_Internal_TxPgStateInfoType* PgState) {\r
+ uint8 requestLength = DT_DATA_SIZE;\r
+ uint8 bytesLeftToSend = PgState->TotalMessageSize - PgState->TotalSentDtCount * DT_DATA_SIZE;\r
+ if (bytesLeftToSend < DT_DATA_SIZE){\r
+ requestLength = bytesLeftToSend;\r
+ }\r
+ // prepare dt message\r
+ uint8 dtBuffer[DT_SIZE] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};\r
+ PduInfoType dtPduInfoBuffer;\r
+ dtPduInfoBuffer.SduLength = DT_SIZE;\r
+ dtPduInfoBuffer.SduDataPtr = dtBuffer;\r
\r
+ BufReq_ReturnType allocateBufferRes;\r
+ PduInfoType* dataPduInfoBuffer;\r
+ PduIdType Pdur_NSdu = J1939Tp_Internal_ConfGetPg(TxPduId)->NSdu;\r
+ allocateBufferRes = PduR_J1939TpProvideTxBuffer(Pdur_NSdu, &dataPduInfoBuffer, requestLength);\r
+ if (allocateBufferRes == BUFREQ_OK) {\r
+ dtPduInfoBuffer.SduDataPtr[DT_BYTE_SEQ_NUM] = PgState->TotalSentDtCount;\r
+ memcpy(&(dtPduInfoBuffer.SduDataPtr[DT_BYTE_DATA_1]), dataPduInfoBuffer, requestLength);\r
+ PduIdType CanIf_NSdu = J1939Tp_Internal_ConfGetPg(TxPduId)->Channel->DtNPdu;\r
+ CanIf_Transmit(CanIf_NSdu, &dtPduInfoBuffer);\r
+ PgState->TotalBytesSent += requestLength;\r
+ PgState->TotalSentDtCount++;\r
+ PgState->SentDtCount++;\r
+ } else {\r
+ /* Todo: check for error */\r
+ }\r
+\r
+}\r
static void J1939Tp_Internal_SendRts(PduIdType TxSduId, const PduInfoType* TxInfoPtr) {\r
uint8 cmRtsData[RTS_SIZE];\r
cmRtsData[RTS_BYTE_CONTROL] = 16;\r
- cmRtsData[RTS_BYTE_LENGTH_1] = TxInfoPtr->SduLength >> 8;\r
- cmRtsData[RTS_BYTE_LENGTH_2] = TxInfoPtr->SduLength << 8;\r
+ cmRtsData[RTS_BYTE_LENGTH_1] = (uint8)(TxInfoPtr->SduLength & 0x00FF);\r
+ cmRtsData[RTS_BYTE_LENGTH_2] = (uint8)(TxInfoPtr->SduLength & 0xFF00);\r
uint8 reminder = 0;\r
- if (TxInfoPtr->SduLength % J1939TP_PACKET_SIZE == 0) {\r
+ if (TxInfoPtr->SduLength % J1939TP_PACKET_SIZE != 0) {\r
reminder = 1;\r
}\r
+ const J1939Tp_PgType* pg = J1939Tp_Internal_ConfGetPg(TxSduId);\r
+ uint32 pgn = pg->Pgn;\r
cmRtsData[RTS_BYTE_NUM_PACKETS] = (TxInfoPtr->SduLength / J1939TP_PACKET_SIZE) + reminder;\r
cmRtsData[RTS_BYTE_SAE_ASSIGN] = 0xFF;\r
- cmRtsData[RTS_BYTE_PGN_1] = RTS_PGN_VALUE_1;\r
- cmRtsData[RTS_BYTE_PGN_2] = RTS_PGN_VALUE_2;\r
- cmRtsData[RTS_BYTE_PGN_3] = RTS_PGN_VALUE_3;\r
+ /* PGN is 18 bits, RTS message have 24 bits PGN payload, therefore we squeeze in pdu id */\r
+ cmRtsData[RTS_BYTE_PGN_1] = pgn; /* get first byte */\r
+ cmRtsData[RTS_BYTE_PGN_2] = pgn >> 8; /* get next byte */\r
+ cmRtsData[RTS_BYTE_PGN_3] = (pgn >> 16) & 0x3; /* get next two bits */\r
+ //cmRtsData[RTS_BYTE_PGN_3] = cmRtsData[RTS_BYTE_PGN_3] & ((TxSduId << 2) | 0x03); /* add the sdu id to the remaining 4 bits*/\r
\r
PduInfoType cmRtsPdu;\r
cmRtsPdu.SduLength = RTS_SIZE;\r
cmRtsPdu.SduDataPtr = cmRtsData;\r
CanIf_Transmit(J1939Tp_Internal_ConfGetTxChannel(TxSduId)->CmNPdu,&cmRtsPdu);\r
}\r
-\r
-\r
-static uint8 J1939Tp_Internal_GetPf(uint32 pgn) {\r
- return (pgn && 0x0000FF00) >> 8;\r
-}\r
-static J1939Tp_ProtocolType J1939Tp_Internal_GetProtocol(uint8 pf) {\r
- if (pf < 240) {\r
- return J1939TP_PROTOCOL_CMDT;\r
- } else {\r
- return J1939TP_PROTOCOL_BAM;\r
- }\r
+static inline void J1939Tp_Internal_ResetT3(J1939Tp_Internal_TxPgStateInfoType* pgState) {\r
+ pgState->T3 = 0;\r
}\r
+\r
static inline void J1939Tp_Internal_ReportError(uint8 ApiId, uint8 ErrorId) {\r
#if (CANSM_DEV_ERROR_DETECT == STD_ON)\r
Det_ReportError(MODULE_ID_J1939TP, 0, ApiId, ApiId);\r
\r
#define J1939TP_PACKET_SIZE 7\r
\r
-/** Rts message */\r
-#define RTS_SIZE 8\r
+#define CM_PGN_VALUE_1 0x00\r
+#define CM_PGN_VALUE_2 0xCE\r
+#define CM_PGN_VALUE_3 0x00\r
+\r
\r
+/* Rts message */\r
+#define RTS_SIZE 8\r
#define RTS_BYTE_CONTROL 0\r
#define RTS_BYTE_LENGTH_1 1\r
#define RTS_BYTE_LENGTH_2 2\r
#define RTS_BYTE_PGN_2 6\r
#define RTS_BYTE_PGN_3 7\r
\r
-#define RTS_PGN_VALUE_1 0x00\r
-#define RTS_PGN_VALUE_2 0xCE\r
-#define RTS_PGN_VALUE_3 0x00\r
+\r
+/* Cts message */\r
+#define CTS_SIZE 8\r
+#define CTS_BYTE_CONTROL 0\r
+#define CTS_BYTE_NUM_PACKETS 1\r
+#define CTS_BYTE_NEXT_PACKET 2\r
+#define CTS_BYTE_SAE_ASSIGN_1 3\r
+#define CTS_BYTE_SAE_ASSIGN_2 4\r
+#define CTS_BYTE_PGN_1 5\r
+#define CTS_BYTE_PGN_2 6\r
+#define CTS_BYTE_PGN_3 7\r
+\r
+#define CTS_CONTROL_VALUE 17\r
+\r
+/* Dt message */\r
+#define DT_SIZE 8\r
+#define DT_DATA_SIZE 7\r
+#define DT_BYTE_SEQ_NUM 0\r
+#define DT_BYTE_DATA_1 1\r
+#define DT_BYTE_DATA_2 2\r
+#define DT_BYTE_DATA_3 3\r
+#define DT_BYTE_DATA_4 4\r
+#define DT_BYTE_DATA_5 5\r
+#define DT_BYTE_DATA_6 6\r
+#define DT_BYTE_DATA_7 7\r
+\r
+#define DT_PGN_VALUE_1 0x00\r
+#define DT_PGN_VALUE_2 0xEB\r
+#define DT_PGN_VALUE_3 0x00\r
+\r
+/* EndOfMsgAck message */\r
+#define ENDOFMSGACK_SIZE 8\r
+#define ENDOFMSGACK_BYTE_CONTROL 0\r
+#define ENDOFMSGACK_BYTE_TOTAL_MSG_SIZE_1 1\r
+#define ENDOFMSGACK_BYTE_TOTAL_MSG_SIZE_2 2\r
+#define ENDOFMSGACK_BYTE_NUM_PACKETS 3\r
+#define ENDOFMSGACK_BYTE_SAE_ASSIGN 4\r
+#define ENDOFMSGACK_BYTE_PGN_1 5\r
+#define ENDOFMSGACK_BYTE_PGN_2 6\r
+#define ENDOFMSGACK_BYTE_PGN_3 7\r
+#define ENDOFMSGACK_CONTROL_VALUE 19\r
+\r
+\r
+#define J1939TP_T4_TIMEOUT_MS 1050\r
+#define J1939TP_T3_TIMEOUT_MS 1250\r
+#define J1939TP_T2_TIMEOUT_MS 1250\r
+#define J1939TP_T1_TIMEOUT_MS 750\r
\r
/** @req J1939TP0019 */\r
\r
typedef enum {\r
J1939TP_ON,\r
J1939TP_OFF\r
-} J1939Tp_Internal_GlobalStateType\r
+} J1939Tp_Internal_GlobalStateType;\r
+\r
typedef struct {\r
J1939Tp_Internal_GlobalStateType State;\r
- uint32 TimeElapsed;\r
} J1939Tp_Internal_GlobalStateInfoType;\r
\r
typedef enum {\r
J1939TP_IDLE,\r
- J1939TP_RTS_SENT,\r
- J1939TP_WAITING_FOR_CM,\r
+ J1939TP_WAITING_FOR_CTS,\r
+ J1939TP_SENDING_DT,\r
+ J1939TP_WAITING_FOR_END_OF_MSG_ACK\r
} J1939Tp_Internal_TxPgStateType;\r
\r
-enum states { IDLE, STATE_2, STATE_3, MAX_STATES } current_state;\r
-enum events { EVENT_1, EVENT_2, MAX_EVENTS } new_event;\r
\r
typedef struct {\r
J1939Tp_Internal_TxPgStateType State;\r
+ uint32 T3;\r
+ uint8 SentDtCount;\r
+ uint8 DtToSendBeforeCtsCount;\r
+ uint16 TotalMessageSize;\r
+ uint8 TotalSentDtCount;\r
+ uint16 TotalBytesSent;\r
+ PduIdType PduRPdu;\r
+\r
} J1939Tp_Internal_TxPgStateInfoType;\r
\r
-static inline const J1939Tp_PgType* J1939Tp_Internal_ConfGetTxPg(uint32 txPduId);\r
-static void J1939Tp_Internal_SendRts(PduIdType TxSduId, const PduInfoType* TxInfoPtr);\r
+static inline const J1939Tp_PgType* J1939Tp_Internal_ConfGetPg(PduIdType pduId);\r
+static inline boolean J1939Tp_Internal_WaitForCts(J1939Tp_Internal_TxPgStateInfoType* pgState);\r
+static inline boolean J1939Tp_Internal_WaitForEndOfMsgAck(J1939Tp_Internal_TxPgStateInfoType* pgState);\r
+static inline void J1939Tp_Internal_SetStatePg(uint32 txPduId,J1939Tp_Internal_TxPgStateType state);\r
+static inline J1939Tp_Internal_TxPgStateInfoType* J1939Tp_Internal_GetPg(uint32 txPduId);\r
+static inline const J1939Tp_ChannelType* J1939Tp_Internal_ConfGetTxChannel(uint32 txPduId);\r
+static inline boolean J1939Tp_Internal_CheckValidEndOfMsgAck(J1939Tp_Internal_TxPgStateInfoType* pgState, PduInfoType* PduInfoPtr);\r
+static inline void J1939Tp_Internal_IncAndCheckT3Timer(PduIdType pduId,J1939Tp_Internal_TxPgStateInfoType* pgState);\r
static uint8 J1939Tp_Internal_GetPf(uint32 pgn);\r
static J1939Tp_ProtocolType J1939Tp_Internal_GetProtocol(uint8 pf);\r
+static inline boolean J1939Tp_Internal_CheckValidCts(PduInfoType* PduInfoPtr,uint8* NumPackets);\r
+static void inline J1939Tp_Internal_SendDt(PduIdType RxPduId,J1939Tp_Internal_TxPgStateInfoType* PgState);\r
+static void J1939Tp_Internal_SendRts(PduIdType TxSduId, const PduInfoType* TxInfoPtr);\r
+static inline void J1939Tp_Internal_ResetT3(J1939Tp_Internal_TxPgStateInfoType* pgState);\r
static inline void J1939Tp_Internal_ReportError(uint8 ApiId, uint8 ErrorId);\r
+\r
#endif\r