| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // Copyright (c) 2000-2020 Ericsson Telecom AB |
| // |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v2.0 |
| // which accompanies this distribution, and is available at |
| // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html |
| /////////////////////////////////////////////////////////////////////////////// |
| // File: IFW_CoAP_Peer_Functions.ttcn |
| // Description: |
| // Rev: <RnXnn> |
| // Prodnr: CNL 113 910 |
| // Updated: 2020-02-06 |
| // Contact: http://ttcn.ericsson.se |
| /////////////////////////////////////////////////////////////////////////////// |
| module IFW_CoAP_Peer_Functions |
| { |
| import from IFW_CoAP_Peer_Definitions all; |
| import from CoAP_Types all; |
| import from IPL4asp_Types all; |
| import from IPL4asp_PortType all; |
| import from IFW_Common all; |
| |
| /////////////////////////////////////////////////////////// |
| // Module parameter: tsp_CoapPeer_maxResponseTime |
| // |
| // Purpose: |
| // Time the peer is waiting for a response message (0.0->not set) |
| // |
| // Type: |
| // *float* |
| // |
| // Default value: |
| // *0.0* |
| /////////////////////////////////////////////////////////// |
| modulepar float tsp_CoapPeer_maxResponseTime := 0.0; |
| |
| /////////////////////////////////////////////////////////// |
| // Module parameter: tsp_cherryPickOptionCheck |
| // |
| // Purpose: |
| // |
| // |
| // Type: |
| // *boolean* |
| // |
| // Default value: |
| // *true* |
| /////////////////////////////////////////////////////////// |
| modulepar boolean tsp_cherryPickOptionCheck := true; |
| |
| function f_CoAP_Peer_init() runs on IFW_COAP_CT |
| { |
| log(%definitionId, " started"); |
| var Result vl_result; |
| |
| log("Mapping started"); |
| map(self:IPL4_PCO,system:IPL4_PCO); |
| |
| if (ctx.localHost != "" and ctx.localPort != -1) |
| { |
| log("Setting up the listening socket"); |
| vl_result := f_IPL4_listen( |
| IPL4_PCO, |
| ctx.localHost, ctx.localPort, ctx.listeningProtocol, ctx.listeningOptions |
| ); |
| f_checkResult(vl_result); |
| |
| var f_IPL4_getMsgLen vl_f := refers(f_COAP_getMsgLength); |
| f_IPL4_setGetMsgLen(IPL4_PCO, vl_result.connId, vl_f, {}); |
| |
| ctx.connId_listen := vl_result.connId; |
| } |
| /* |
| log("Connecting the socket to the remote"); |
| |
| vl_result := f_IPL4_connect( |
| IPL4_PCO, |
| ctx.remoteHost, ctx.remotePort, ctx.localHost, ctx.localPort, -1, ctx.connectProtocol, ctx.connectOptions |
| ); |
| f_checkResult(vl_result); |
| */ |
| |
| if (isbound(vl_result.connId)) |
| { |
| ctx.connId := vl_result.connId; |
| } |
| |
| ctx.cherryPickOptionsCheck := tsp_cherryPickOptionCheck; |
| |
| log(%definitionId, " finished"); |
| } |
| |
| function f_CoAP_Peer_listen() runs on IFW_COAP_CT |
| { |
| log("Setting up the listening socket"); |
| var Result vl_result := f_IPL4_listen( |
| IPL4_PCO, |
| ctx.localHost, ctx.localPort, ctx.listeningProtocol, ctx.listeningOptions |
| ); |
| f_checkResult(vl_result); |
| |
| var f_IPL4_getMsgLen vl_f := refers(f_COAP_getMsgLength); |
| f_IPL4_setGetMsgLen(IPL4_PCO, vl_result.connId, vl_f, {}); |
| |
| ctx.connId_listen := vl_result.connId; |
| ctx.connId := vl_result.connId; |
| } |
| |
| function f_CoAP_Peer_connect() runs on IFW_COAP_CT |
| { |
| log("Connecting the socket to the remote"); |
| |
| var Result vl_result; |
| vl_result := f_IPL4_connect( |
| IPL4_PCO, |
| ctx.remoteHost, ctx.remotePort, ctx.localHost, ctx.localPort, -1, ctx.connectProtocol, ctx.connectOptions |
| ); |
| |
| f_checkResult(vl_result); |
| |
| if (ispresent(vl_result.errorCode) and vl_result.errorCode == ERROR_TEMPORARILY_UNAVAILABLE) |
| { |
| ctx.temporarilyUnavailable := true; |
| } |
| |
| var f_IPL4_getMsgLen vl_f := refers(f_COAP_getMsgLength); |
| f_IPL4_setGetMsgLen(IPL4_PCO, vl_result.connId, vl_f, {}); |
| |
| ctx.connId := vl_result.connId; |
| } |
| |
| function f_CoAP_Peer_getContext() runs on IFW_COAP_CT |
| return CoapContext |
| { |
| return ctx; |
| } |
| |
| function f_CoAP_Peer_setContext(in CoapContext p_ctx) runs on IFW_COAP_CT |
| { |
| ctx := p_ctx; |
| } |
| |
| function f_CoAP_Peer_setMessageToSend(in CoAP_ReqResp p_msg, in MID_Generation p_genMid, in Token_Generation p_genToken) runs on IFW_COAP_CT |
| { |
| msgToSend := p_msg; |
| |
| if (p_genMid == GENERATE_NEW_MID) |
| { |
| msgToSend.header.message_id := float2int(int2float(65000)*rnd()); |
| } |
| else if (p_genMid == USE_LAST_RECEIVED_MID) { |
| if (lastReceived != c_CoAP_ReqResp_empty) { |
| msgToSend.header.message_id := lastReceived.header.message_id; |
| } |
| } |
| |
| if (p_genToken == GENERATE_NEW_TOKEN) |
| { |
| msgToSend.token := int2oct(float2int(int2float(65000)*rnd()),4); |
| } |
| if (p_genToken == USE_LAST_RECEIVED_TOKEN) { |
| if (lastReceived != c_CoAP_ReqResp_empty) { |
| msgToSend.token := lastReceived.token; |
| } |
| } |
| } |
| |
| function f_CoAP_Peer_send() runs on IFW_COAP_CT |
| { |
| var octetstring v_encoded; |
| |
| f_CoAP_enc(CoAP_Message: {msg := msgToSend}, v_encoded); |
| |
| var ASP_SendTo vl_send; |
| |
| vl_send.connId := ctx.connId; |
| vl_send.remName := ctx.remoteHost; |
| vl_send.remPort := ctx.remotePort; |
| vl_send.proto := ctx.connectProtocol |
| |
| vl_send.msg := v_encoded; |
| |
| if (not ctx.temporarilyUnavailable) |
| { |
| action("Sending: COAP PDU: ", msgToSend); |
| IPL4_PCO.send(vl_send); |
| } |
| else |
| { |
| action("Buffering: COAP PDU: ", msgToSend); |
| buffer[sizeof(buffer)] := vl_send; |
| } |
| } |
| |
| function f_CoAP_Peer_receive() runs on IFW_COAP_CT |
| { |
| // Activate default |
| |
| timer t_Timeout := tsp_CoapPeer_maxResponseTime; |
| if (tsp_CoapPeer_maxResponseTime > 0.0) { t_Timeout.start; } |
| |
| var ASP_RecvFrom v_ipl4Recv; |
| var ASP_Event v_ipl4Event; |
| |
| alt |
| { |
| [] IPL4_PCO.receive(ASP_RecvFrom:?) -> value v_ipl4Recv |
| { |
| log("Received: ", v_ipl4Recv); |
| |
| var CoAP_Message v_coapMsg; |
| f_CoAP_dec(v_ipl4Recv.msg, v_coapMsg); |
| |
| if (ischosen(v_coapMsg.msg)) |
| { |
| lastReceived := v_coapMsg.msg; |
| action("Received: COAP PDU: ", lastReceived); |
| } |
| } |
| [] IPL4_PCO.receive(ASP_Event:?) -> value v_ipl4Event |
| { |
| log("Received: ", v_ipl4Event); |
| if (ischosen(v_ipl4Event.result)) |
| { |
| if (ctx.temporarilyUnavailable and |
| ispresent(v_ipl4Event.result.errorCode) and |
| v_ipl4Event.result.errorCode == ERROR_AVAILABLE |
| ) |
| { |
| if (sizeof(buffer)>0) |
| { |
| for (var integer i:=0; i<sizeof(buffer); i:=i+1) |
| { |
| action("Sending from buffer: ", buffer[i]); |
| IPL4_PCO.send(buffer[i]); |
| } |
| } |
| ctx.temporarilyUnavailable := false; |
| } |
| } |
| else if (ischosen(v_ipl4Event.connClosed)) |
| { |
| action("Remote closed the connection"); |
| } |
| repeat; |
| } |
| [] t_Timeout.timeout |
| { |
| lastReceived := c_CoAP_ReqResp_empty; |
| } |
| |
| // Deactivate default |
| } |
| } |
| |
| function f_CoAP_Peer_check(in template CoAP_ReqResp p_expected, in MID_Check p_checkMid := CHECK_MID) runs on IFW_COAP_CT |
| return ReturnBoolean |
| { |
| // TODO: checks |
| if (not f_COAP_isRequest(lastReceived) and lastReceived.header.msg_type == CONFIRMABLE and p_checkMid == CHECK_MID) { |
| // Check if the mid is the same in the req and in the rsp |
| if (not msgToSend.header.message_id == lastReceived.header.message_id) { |
| log("CHECK: header.message_id mismatch in request and response"); |
| return false; |
| } |
| } |
| |
| if (ctx.cherryPickOptionsCheck) { |
| |
| var template CoAP_ReqResp v_expected_withoutOptions := p_expected; |
| v_expected_withoutOptions.options := *; |
| if (not match(lastReceived, v_expected_withoutOptions)) { |
| log("CHECK: last received is not matching with expected template"); |
| return false; |
| } |
| |
| // Check options 1-by-1 |
| if (ispresent(p_expected.options) and sizeof(p_expected.options)>0) |
| { |
| if (not ispresent(lastReceived.options)) { |
| log("CHECK: Options are epxected but last received messages have them omitted"); |
| return false; |
| } |
| var boolean optionsCheck := true; |
| for (var integer i:=0; i<sizeof(p_expected.options); i:=i+1) { |
| var template CoAP_Options v_currentExpectedOption := p_expected.options[i]; |
| var boolean optionCheck := false; |
| for (var integer j:=0; j<sizeof(lastReceived.options); j:=j+1) { |
| var CoAP_Options v_currentReceivedOption := lastReceived.options[j]; |
| if (match(v_currentReceivedOption, v_currentExpectedOption)) { |
| optionCheck := true; |
| } |
| } |
| if (not optionCheck) { |
| log("CHECK: Option mismatch!"); |
| log("EXPECTED: ",v_currentExpectedOption); |
| log("RECEIVED: ",lastReceived.options); |
| optionsCheck := false; |
| } |
| } |
| if (not optionsCheck) { |
| return false; |
| } |
| } |
| } |
| else { |
| if (not match(lastReceived, p_expected)) { |
| log("CHECK: last received is not matching with expected template"); |
| return false; |
| } |
| } |
| |
| log("CHECK: return true"); |
| return true; |
| } |
| |
| |
| function f_CoAP_Peer_saveLocationPath() |
| runs on IFW_COAP_CT |
| { |
| ctx.locationPath := {}; |
| |
| for (var integer i:=0; i<sizeof(lastReceived.options); i:=i+1) |
| { |
| if (ischosen(lastReceived.options[i].location_path)) |
| { |
| ctx.locationPath[sizeof(ctx.locationPath)] := lastReceived.options[i].location_path; |
| } |
| } |
| } |
| |
| function f_CoAP_Peer_addUriPath() |
| runs on IFW_COAP_CT |
| { |
| for (var integer i:=0; i<sizeof(ctx.locationPath); i:=i+1) |
| { |
| msgToSend.options[sizeof(msgToSend.options)] := |
| { |
| uri_path := ctx.locationPath[i] |
| } |
| } |
| } |
| |
| function f_COAP_getMsgLength(in octetstring stream, inout ro_integer args) |
| return integer |
| { |
| return lengthof(stream); |
| // Only for UDP |
| // For TCP other frameing is needed (see IETF: COAP mapping to TCP); |
| } |
| |
| function f_COAP_isRequest(in CoAP_ReqResp p_msg) |
| return boolean |
| { |
| if (p_msg.header.code.class == 0) { |
| return true; |
| } |
| else { |
| return false; |
| } |
| } |
| } |