From 27abc22b47d5375e6f8171873c6786cff47a61f5 Mon Sep 17 00:00:00 2001
From: BenceJanosSzabo <bence.janos.szabo@ericsson.com>
Date: Mon, 10 Apr 2017 12:49:19 +0200
Subject: [PATCH] Implement ports with translation capability (part5) finishing
 touches

Change-Id: If5aff1c37233b8fa3db861bc31ef825ecd953614
Signed-off-by: BenceJanosSzabo <bence.janos.szabo@ericsson.com>
---
 compiler2/ttcn3/port.c                        |  33 ++-
 core/Port.cc                                  |  12 +-
 regression_test/portTranslation/P1.cc         |  15 +-
 regression_test/portTranslation/P1.hh         |   2 +
 .../portTranslation/PortTranslation.ttcn      | 239 ++++++++++++++++--
 .../portTranslation/PortVariables.ttcn        |   1 -
 6 files changed, 272 insertions(+), 30 deletions(-)

diff --git a/compiler2/ttcn3/port.c b/compiler2/ttcn3/port.c
index 6f9eea304..ad6829233 100644
--- a/compiler2/ttcn3/port.c
+++ b/compiler2/ttcn3/port.c
@@ -81,6 +81,9 @@ static char *generate_send_mapping(char *src, const port_def *pdef,
         }
       }
       src = mputstr(src, ") {\n");
+      // Beginning of the loop of the PARTIALLY_TRANSLATED case to process all
+      // messages
+      src = mputstr(src, "do {\n");
       src = mputstr(src, "TTCN_Runtime::set_translation_mode(TRUE, this);\n");
       // Set to unset
       src = mputstr(src, "TTCN_Runtime::set_port_state(-1, \"by test environment.\", TRUE);\n");
@@ -168,7 +171,7 @@ static char *generate_send_mapping(char *src, const port_def *pdef,
     if (!pdef->legacy && pdef->port_type == USER) {
       src = mputstr(src,
         "TTCN_Runtime::set_translation_mode(FALSE, NULL);\n"
-        "if (port_state == TRANSLATED) {\n");
+        "if (port_state == TRANSLATED || port_state == PARTIALLY_TRANSLATED) {\n");
     }
     src = mputprintf(src, "if (TTCN_Logger::log_this_event("
       "TTCN_Logger::PORTEVENT_DUALSEND)) {\n"
@@ -195,7 +198,13 @@ static char *generate_send_mapping(char *src, const port_def *pdef,
       if (pdef->testport_type != INTERNAL) src = mputstr(src, "}\n");
     }
     if (has_condition) {
+      if (!pdef->legacy && pdef->port_type == USER) {
+        src = mputstr(src, "if (port_state != PARTIALLY_TRANSLATED) {\n");
+      }
       src = mputstr(src, "return;\n");
+      if (!pdef->legacy && pdef->port_type == USER) {
+        src = mputstr(src, "}\n");
+      }
       if (pdef->legacy) {
         src = mputstr(src, "}\n");
       }
@@ -203,12 +212,16 @@ static char *generate_send_mapping(char *src, const port_def *pdef,
     }
     if (!pdef->legacy && pdef->port_type == USER) {
       src = mputprintf(src,
+        "} else if (port_state == FRAGMENTED) {\n"
+        "return;\n"
         "} else if (port_state == UNSET) {\n"
         "TTCN_error(\"The state of the port %%s remained unset after the mapping function %s finished.\", port_name);\n"
         "}\n", target->mapping.function.dispname);
     }
     if (mapped_type->nTargets > 1) src = mputstr(src, "}\n");
     if (!pdef->legacy && pdef->port_type == USER) {
+      // End of the do while loop to process all the messages
+      src = mputstr(src, "} while (port_state == PARTIALLY_TRANSLATED);\n");
       // end of the outgoing messages of port with mapping target check
       src = mputstr(src, "}\n");
     }
@@ -255,8 +268,11 @@ static char *generate_incoming_mapping(char *src, const port_def *pdef,
       /* has_buffer will be set to TRUE later */
     }
     if (!pdef->legacy && pdef->port_type == USER) {
+      // Beginning of the loop of the PARTIALLY_TRANSLATED case to process all
+      // messages
+      src = mputstr(src, "do {\n");
       src = mputstr(src, "TTCN_Runtime::set_translation_mode(TRUE, this);\n");
-      src = mputstr(src, "port_state = UNSET;\n");
+      src = mputstr(src, "TTCN_Runtime::set_port_state(-1, \"by test environment.\", TRUE);\n");
     }
     if (mapped_type->nTargets > 1) src = mputstr(src, "{\n");
     switch (target->mapping_type) {
@@ -380,7 +396,7 @@ static char *generate_incoming_mapping(char *src, const port_def *pdef,
       "new_item->sender_component = sender_component;\n",
       !pdef->legacy && pdef->port_type == USER ?
         "TTCN_Runtime::set_translation_mode(FALSE, NULL);\n"
-        "if (port_state == TRANSLATED) {\n" : "",
+        "if (port_state == TRANSLATED || port_state == PARTIALLY_TRANSLATED) {\n" : "",
       target->target_dispname,
       (unsigned long) target->target_index,
       (unsigned long) target->target_index);
@@ -405,6 +421,10 @@ static char *generate_incoming_mapping(char *src, const port_def *pdef,
           "}");
         if (pdef->port_type == USER && !pdef->legacy) {
           src = mputprintf(src,
+            " else if (port_state == FRAGMENTED) {\n"
+            "delete mapped_par;\n"
+            "return;\n"
+            "}"
             " else if (port_state == UNSET) {\n"
             "delete mapped_par;\n"
             "TTCN_error(\"The state of the port %%s remained unset after the mapping function %s finished.\", port_name);\n"
@@ -415,6 +435,9 @@ static char *generate_incoming_mapping(char *src, const port_def *pdef,
       report_error = TRUE;
     }
     if (mapped_type->nTargets > 1) src = mputstr(src, "}\n");
+    if (pdef->port_type == USER && !pdef->legacy) {
+      src = mputstr(src, "} while (port_state == PARTIALLY_TRANSLATED);\n");
+    }
   } /* next mapping target */
   if (has_discard) {
     if (mapped_type->nTargets > 1) {
@@ -2528,12 +2551,12 @@ void defPortClass(const port_def* pdef, output_struct* output)
       "}\n", mapped_type->dispname);
       // Print the simple mapping after the not simple mappings
       if (!is_simple || !pdef->legacy) {
-        if (!pdef->legacy) {
+        if (!pdef->legacy && mapped_type->nTargets > (is_simple ? 1 : 0)) {
           // If in translation mode then receive according to incoming mappings
           src = mputstr(src, "if (in_translation_mode()) {\n");
         }
         src = generate_incoming_mapping(src, pdef, mapped_type, is_simple);
-        if (!pdef->legacy) {
+        if (!pdef->legacy && mapped_type->nTargets > (is_simple ? 1 : 0)) {
           src = mputstr(src, "}\n");
         }
       }
diff --git a/core/Port.cc b/core/Port.cc
index 844b3ee2a..15eba7dd3 100644
--- a/core/Port.cc
+++ b/core/Port.cc
@@ -302,6 +302,12 @@ void PORT::activate_port()
     msg_tail_count = 0;
     proc_head_count = 0;
     proc_tail_count = 0;
+    
+    // Only has effect when the translation port has port variables with
+    // default values. Only call when it is activated.
+    if (n_system_mappings == 0) {
+      init_port_variables();
+    }
   }
 }
 
@@ -2149,12 +2155,6 @@ void PORT::map(const char *system_port)
   TTCN_Logger::log_port_misc(
     TitanLoggerApi::Port__Misc_reason::port__was__mapped__to__system,
     port_name, SYSTEM_COMPREF, system_port);
-  
-  // Only has effect when the translation port has port variables with
-  // default values. Only call when it is mapped first.
-  if (n_system_mappings == 0) {
-    init_port_variables();
-  }
 
   // the mapping shall be registered in the table only if user_map() was
   // successful
diff --git a/regression_test/portTranslation/P1.cc b/regression_test/portTranslation/P1.cc
index 3fb1fd746..d5644e0b4 100644
--- a/regression_test/portTranslation/P1.cc
+++ b/regression_test/portTranslation/P1.cc
@@ -86,7 +86,15 @@ void P1_PROVIDER::outgoing_send(const OCTETSTRING& send_par)
 
 void P1_PROVIDER::outgoing_send(const BITSTRING& send_par)
 {
-	incoming_message(send_par);
+	// Test that the receive mapping handles fragmented case
+	if (send_par.lengthof() == 48) {
+		for (int i = 0; i < 48; i++) {
+			BITSTRING bs = send_par[i];
+			incoming_message(bs);
+		}
+	} else {
+		incoming_message(send_par);
+	}
 }
 
 void P1_PROVIDER::outgoing_send(const CHARSTRING& send_par)
@@ -99,5 +107,10 @@ void P1_PROVIDER::outgoing_send(const INTEGER& send_par)
 	incoming_message(send_par);
 }
 
+void P1_PROVIDER::outgoing_send(const HEXSTRING& send_par)
+{
+	incoming_message(send_par);
+}
+
 } /* end of namespace */
 
diff --git a/regression_test/portTranslation/P1.hh b/regression_test/portTranslation/P1.hh
index 15dcee8d3..b8a430793 100644
--- a/regression_test/portTranslation/P1.hh
+++ b/regression_test/portTranslation/P1.hh
@@ -51,10 +51,12 @@ public:
 	void outgoing_send(const BITSTRING& send_par);
 	void outgoing_send(const CHARSTRING& send_par);
 	void outgoing_send(const INTEGER& send_par);
+	void outgoing_send(const HEXSTRING& send_par);
 	virtual void incoming_message(const INTEGER& incoming_par) = 0;
 	virtual void incoming_message(const BITSTRING& incoming_par) = 0;
 	virtual void incoming_message(const OCTETSTRING& incoming_par) = 0;
 	virtual void incoming_message(const CHARSTRING& incoming_par) = 0;
+	virtual void incoming_message(const HEXSTRING& incoming_par) = 0;
 };
 
 } /* end of namespace */
diff --git a/regression_test/portTranslation/PortTranslation.ttcn b/regression_test/portTranslation/PortTranslation.ttcn
index d57dde601..5a9c0ef89 100644
--- a/regression_test/portTranslation/PortTranslation.ttcn
+++ b/regression_test/portTranslation/PortTranslation.ttcn
@@ -101,19 +101,94 @@ module PortTranslation {
 		extension "prototype(fast)";
 	}
 
-	function hex_to_bit(in hexstring i, out bitstring j) {
-		if (lengthof(i) > 4) {
-			j := hex2bit(substr(i, 0, 4));
-			i := substr(i, 4, lengthof(i)-4);
-			port.setstate(3); // partially translated, more messages to decode
+	function hex_to_hex(in hexstring i, out hexstring j) port PT2 {
+		log("asdf");
+		if (lengthof(i) != 14) {
+			port.setstate(1, "hex_to_hex");
 		} else {
-			j := hex2bit(substr(i, 0, lengthof(i)));
-			port.setstate(0); // translated
+			if (lengthof(i) > index + 4) {
+				j := substr(i, index, 4);
+				index := index + 4;
+				port.setstate(3, "hex_to_hex"); // partially translated, more messages to decode
+			} else {
+				j := substr(i, index, lengthof(i) - index);
+				port.setstate(0); // translated
+			}
+		}
+	} with {
+		extension "prototype(fast)";
+	}
+
+	function hex_to_bit_buffer(in hexstring i, out bitstring j) port PT2 {
+		log("asdf2");
+		if (lengthof(i) == 12 or lengthof(i) == 10) {
+			port.setstate(1);
+		} else {
+			if (lengthof(buffer) != 14) {
+				buffer := buffer & i;
+				port.setstate(2, "hex_to_bit_buffer");
+			}
+			if (lengthof(buffer) == 14) {
+				j := hex2bit(buffer);
+				port.setstate(0, "hex_to_bit_buffer");
+				buffer := ''H;
+			}
+		}
+	} with {
+		extension "prototype(fast)";
+	}
+
+	function hex_to_bit2(in hexstring i, out bitstring j) {
+		log("ff");
+		j := hex2bit(i);
+		port.setstate(0, "hex_to_bit2");
+	} with {
+		extension "prototype(fast)";
+	}
+
+	function bit_to_bit_buffer(in bitstring i, out bitstring j) port PT2 {
+		if (lengthof(i) != 1) {
+			port.setstate(1);
+		} else {
+			if (lengthof(bit_buffer) != 48) {
+				bit_buffer := bit_buffer & i;
+				port.setstate(2);
+			}
+			if (lengthof(bit_buffer) == 48) {
+				j := bit_buffer;
+				port.setstate(0);
+				bit_buffer := ''B;
+			}
 		}
 	} with {
 		extension "prototype(fast)";
 	}
 
+	function bit_to_bit_fragmented(in bitstring i, out bitstring j) port PT2 {
+		log("a");
+		if (lengthof(i) != 40) {
+			port.setstate(1);
+		} else {
+			if (lengthof(i) > index2 + 16) {
+				j := substr(i, index2, 16);
+				index2 := index2 + 16;
+				port.setstate(3); // partially translated, more messages to decode
+			} else {
+				j := substr(i, index2, lengthof(i) - index2);
+				port.setstate(0, "bit_to_bit_fragmented"); // translated
+			}
+		}
+	} with {
+		extension "prototype(fast)";
+	}
+
+	function bit_to_bit(in bitstring i, out bitstring j) port PT2 {
+		j := i;
+		port.setstate(0, "bit_to_bit");
+	} with {
+		extension "prototype(fast)";
+	}
+
 	function hex_to_char(in hexstring i, out charstring j) {
 		// dummy
 	} with {
@@ -141,8 +216,8 @@ module PortTranslation {
 	}
 
 	type port P1 message {
-		in bitstring, octetstring, charstring
-		out MyRec, octetstring, bitstring, charstring
+		in bitstring, octetstring, charstring, hexstring
+		out MyRec, octetstring, bitstring, charstring, hexstring
 		inout integer
 	} with {
 		extension "provider"
@@ -166,8 +241,14 @@ module PortTranslation {
 		in integer, octetstring, hexstring from charstring with char_to_hex() : charstring with char_to_hex2()
 		out MyRec to octetstring with MyRec_to_oct() : integer with MyRec_to_int() : MyRec with MyRec_to_MyRec() : charstring with MyRec_to_char() : hexstring with MyRec_to_hex()
 		out octetstring
-		inout bitstring
-		out hexstring to bitstring with hex_to_bit() : charstring with hex_to_char()
+		out bitstring
+		in bitstring from bitstring with bit_to_bit_buffer() : bitstring with bit_to_bit_fragmented() : bitstring with bit_to_bit()
+		out hexstring to hexstring with hex_to_hex() : bitstring with hex_to_bit_buffer() : bitstring with hex_to_bit2() : charstring with hex_to_char()
+
+		var integer index := 0;
+		var integer index2 := 0;
+		var hexstring buffer := ''H;
+		var bitstring bit_buffer := ''B;
 	}
 
 
@@ -249,6 +330,7 @@ module PortTranslation {
 		}
 		t.stop;
 
+		// Send back the bitstring
 		var bitstring bs := '010101'B;
 		p.send(bs);
 		t.start;
@@ -461,16 +543,136 @@ module PortTranslation {
 		map(self:p, system:p1);
 		var hexstring hs := '12345678ABCDEF'H;
 		p.send(hs);
-		// TODO: wait for port variables
+		timer t := 1.0;
+		t.start;
+		alt {
+			[] p.receive('1234'H) { setverdict(pass); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(fail); }
+		}
+		t.stop;
+
+		t.start;
+		alt {
+			[] p.receive('5678'H) { setverdict(pass); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(fail); }
+		}
+		t.stop;
+
+		t.start;
+		alt {
+			[] p.receive('ABCD'H) { setverdict(pass); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(fail); }
+		}
+		t.stop;
+
+		t.start;
+		alt {
+			[] p.receive('EF'H) { setverdict(pass); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(fail); }
+		}
+		t.stop;
 	}
 
 	testcase tc_send_fragmented() runs on MyComp system System {
 		map(self:p, system:p1);
-		var hexstring hs := '12345678ABCDEF'H;
-		p.send(hs);
-		// TODO: wait for port variables
+
+		p.send('1234'H);
+		timer t := 0.5;
+		t.start;
+		alt {
+			[] p.receive(bitstring:?) { setverdict(fail); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(pass); }
+		}
+		t.stop;
+
+		p.send('5678'H);
+		t.start;
+		alt {
+			[] p.receive(bitstring:?) { setverdict(fail); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(pass); }
+		}
+		t.stop;
+
+		p.send('ABCD'H);
+		t.start;
+		alt {
+			[] p.receive(bitstring:?) { setverdict(fail); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(pass); }
+		}
+		t.stop;
+
+		p.send('EF'H);
+		t.start;
+		alt {
+			[] p.receive(hex2bit('12345678ABCDEF'H)) { setverdict(pass); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(fail); }
+		}
+		t.stop;
+	}
+
+	testcase tc_receive_fragmented() runs on MyComp system System {
+		map(self:p, system:p1);
+
+		p.send('12345678ABCD'H);
+		timer t := 0.5;
+		t.start;
+		alt {
+			[] p.receive(hex2bit('12345678ABCD'H)) { setverdict(pass); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(fail); }
+		}
+		t.stop;
 	}
 
+	testcase tc_receive_partially_translated() runs on MyComp system System {
+		map(self:p, system:p1);
+
+		p.send('12345678AB'H);
+		timer t := 0.5;
+		t.start;
+		alt {
+			[] p.receive(hex2bit('1234'H)) { setverdict(pass); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(fail); }
+		}
+		t.stop;
+
+		t.start;
+		alt {
+			[] p.receive(hex2bit('5678'H)) { setverdict(pass); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(fail); }
+		}
+		t.stop;
+
+		t.start;
+		alt {
+			[] p.receive(hex2bit('AB'H)) { setverdict(pass); }
+			[] p.receive(integer:?) { setverdict(fail); }
+			[] p.receive(octetstring:?) { setverdict(fail); }
+			[] t.timeout    { setverdict(fail); }
+		}
+		t.stop;
+	}
 
 	control {
 		execute(tc_send())
@@ -481,8 +683,11 @@ module PortTranslation {
 
 		execute(tc_send_and_receive_without_mapping());
 
-		//execute(tc_send_partially_translated());
-		//execute(tc_send_fragmented());
+		execute(tc_send_partially_translated());
+		execute(tc_send_fragmented());
+
+		execute(tc_receive_partially_translated());
+		execute(tc_receive_fragmented());
 	}
 
 }
\ No newline at end of file
diff --git a/regression_test/portTranslation/PortVariables.ttcn b/regression_test/portTranslation/PortVariables.ttcn
index 780d72310..7f8fd0278 100644
--- a/regression_test/portTranslation/PortVariables.ttcn
+++ b/regression_test/portTranslation/PortVariables.ttcn
@@ -292,7 +292,6 @@ module PortVariables {
 			}
 			t.stop;
 		}
-
 	}
 
 	control {
-- 
GitLab