| /////////////////////////////////////////////////////////////////////////////// | 
 | // | 
 | // Copyright (c) 2000-2017 Ericsson Telecom AB | 
 | // | 
 | // All rights reserved. This program and the accompanying materials | 
 | // are made available under the terms of the Eclipse Public License v1.0 | 
 | // which accompanies this distribution, and is available at | 
 | // http://www.eclipse.org/legal/epl-v10.html | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | //  File:               IFW_CoAP_Peer_Functions.ttcn | 
 | //  Description: | 
 | //  Rev:                R1A | 
 | //  Prodnr:             LPA 108 661 | 
 | //  Updated:            2017-09-01 | 
 | //  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; | 
 | 	 | 
 | 	modulepar | 
 | 	{ | 
 | 		float tsp_CoapPeer_maxResponseTime := 0.0; | 
 | 		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; | 
 | 	  } | 
 | 	} | 
 | } |