asterisk: Introduce AMI_Adapter_CT

This allows to keep string handling totally internal to the AMI_Adapter
component, which also means now the CLIENT port acts asynchronously on
full AMI messages.
This allows for instance using activated altsteps to ignore events or
answer to them.

Change-Id: Ibf230d2302fecf443f34e1c4d4acfd4802f4cc79
diff --git a/asterisk/AMI_Functions.ttcn b/asterisk/AMI_Functions.ttcn
index 72420e1..6e0b8d0 100644
--- a/asterisk/AMI_Functions.ttcn
+++ b/asterisk/AMI_Functions.ttcn
@@ -167,6 +167,58 @@
 	tr_AMI_Field_ActionId(action_id)
 );
 
+
+/***********************
+ * Adapter:
+ ***********************/
+
+type port AMI_Msg_PT message {
+	inout AMI_Msg;
+} with { extension "internal" };
+
+type component AMI_Adapter_CT {
+	port TELNETasp_PT AMI;
+	port AMI_Msg_PT CLIENT;
+}
+
+function f_AMI_Adapter_main() runs on AMI_Adapter_CT {
+	var AMI_Msg msg;
+
+	var charstring rx, buf := "";
+	var integer fd;
+
+	map(self:AMI, system:AMI);
+
+	while (true) {
+
+		alt {
+		[] AMI.receive(pattern "\n") {
+			buf := buf & "\n";
+			msg := dec_AMI_Msg(buf);
+			buf := "";
+			CLIENT.send(msg);
+			};
+		[] AMI.receive(charstring:?) -> value rx {
+			buf := buf & rx;
+			};
+		[] AMI.receive(integer:?) -> value fd {
+			if (fd == -1) {
+				Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+							"AMI Telnet Connection Failure: " & int2str(fd));
+			} else {
+				/* telnet connection succeeded */
+			}
+			}
+		[] CLIENT.receive(AMI_Msg:?) -> value msg {
+			/* TODO: in the future, queue Action if there's already one Action in transit, to fullfill AMI requirements. */
+			var charstring tx_txt := enc_AMI_Msg(msg);
+			AMI.send(tx_txt);
+			}
+		}
+	}
+}
+
+
 /*
  * Functions:
  */
@@ -223,62 +275,66 @@
 	return field.val;
 }
 
-private function f_ami_wait_for_prompt_str(TELNETasp_PT pt, charstring log_label := "(?)")
-return charstring {
-	var charstring rx, buf := "";
-	var integer fd;
+function f_ami_transceive_ret(AMI_Msg_PT pt, template (value) AMI_Msg tx_msg, float rx_timeout := 10.0) return AMI_Msg {
+	var AMI_Msg rx_msg;
 	timer T;
 
-	T.start(mp_ami_prompt_timeout);
+	T.start(rx_timeout);
+	pt.send(tx_msg);
 	alt {
-	[] pt.receive(pattern "\n") { buf := buf & "\n" };
-	[] pt.receive(charstring:?) -> value rx { buf := buf & rx; repeat };
-	[] pt.receive(integer:?) -> value fd {
-		if (fd == -1) {
-			Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
-						"AMI Telnet Connection Failure: " & log_label);
-		} else {
-			repeat; /* telnet connection succeeded */
-		}
-	}
+	[] pt.receive(AMI_Msg:?) -> value rx_msg;
 	[] T.timeout {
 		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
-					"AMI Timeout for prompt: " & log_label);
-		};
+					log2str("AMI Response timeout: ", tx_msg));
+		}
 	}
 	T.stop;
-	return buf;
+	return rx_msg;
+
 }
 
-function f_ami_wait_for_prompt(TELNETasp_PT pt, charstring log_label := "(?)") return AMI_Msg {
-	var charstring buf := f_ami_wait_for_prompt_str(pt, log_label);
-	var AMI_Msg msg := dec_AMI_Msg(buf);
-	return msg;
-}
-
-/* send a AMI command and obtain response until prompt is received */
-private function f_ami_transceive_ret_str(TELNETasp_PT pt, charstring tx) return charstring {
-	pt.send(tx);
-	return f_ami_wait_for_prompt_str(pt, tx);
-}
-
-function f_ami_transceive_ret(TELNETasp_PT pt, template (value) AMI_Msg tx_msg) return AMI_Msg {
-	var charstring tx_txt := enc_AMI_Msg(valueof(tx_msg));
-	var charstring resp_txt := f_ami_transceive_ret_str(pt, tx_txt);
-	return dec_AMI_Msg(resp_txt);
-}
-
-function f_ami_transceive_match(TELNETasp_PT pt,
-				template (value) AMI_Msg tx_msg,
-				template (present) AMI_Msg exp_ret := ?) {
-	var AMI_Msg ret := f_ami_transceive_ret(pt, tx_msg);
-	if (not match(ret, exp_ret)) {
+private altstep as_ami_rx_fail(AMI_Msg_PT pt, template AMI_Msg exp_msg := *)
+{
+	var AMI_Msg msg;
+	[] pt.receive(AMI_Msg:?) -> value msg {
 		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
-			log2str("Non-matching AMI response: ", ret, " vs exp: ", exp_ret));
+					log2str("Received unexpected AMI message := ", msg, "\nvs exp := ", exp_msg));
 	}
 }
 
-function f_ami_transceive_match_response_success(TELNETasp_PT pt,
+altstep as_ami_expect_msg(AMI_Msg_PT pt, template (present) AMI_Msg msg_expect, boolean fail_others := true)
+{
+	[] pt.receive(msg_expect);
+	[fail_others] as_ami_rx_fail(pt, msg_expect);
+}
+
+function f_ami_transceive_match(AMI_Msg_PT pt,
+				template (value) AMI_Msg tx_msg,
+				template (present) AMI_Msg exp_ret := ?,
+				boolean fail_others := true,
+				float rx_timeout := 10.0) return AMI_Msg {
+	var AMI_Msg rx_msg;
+	timer T;
+
+	T.start(rx_timeout);
+	pt.send(tx_msg);
+	alt {
+	[] pt.receive(exp_ret) -> value rx_msg;
+	[not fail_others] pt.receive(AMI_Msg:?) -> value rx_msg {
+		log("AMI: Ignoring Rx msg ", rx_msg);
+		repeat;
+	}
+	[fail_others] as_ami_rx_fail(pt, exp_ret);
+	[] T.timeout {
+		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+					log2str("AMI Response timeout: ", tx_msg));
+		}
+	}
+	T.stop;
+	return rx_msg;
+}
+
+function f_ami_transceive_match_response_success(AMI_Msg_PT pt,
 						 template (value) AMI_Msg tx_msg) {
 	var template (present) AMI_Msg exp_resp;
 	var template (omit) charstring action_id := f_ami_msg_get_value(valueof(tx_msg), AMI_FIELD_ACTION_ID);
@@ -290,11 +346,11 @@
 	f_ami_transceive_match(pt, tx_msg, exp_resp);
 }
 
-function f_ami_action_login(TELNETasp_PT pt, charstring username, charstring secret) {
+function f_ami_action_login(AMI_Msg_PT pt, charstring username, charstring secret) {
 	f_ami_transceive_match_response_success(pt, ts_AMI_Action_Login(username, secret));
 }
 
-function f_ami_action_PJSIPRegister(TELNETasp_PT pt, charstring register) {
+function f_ami_action_PJSIPRegister(AMI_Msg_PT pt, charstring register) {
 	var charstring reg_action_id := f_gen_action_id();
 	f_ami_transceive_match_response_success(pt, ts_AMI_Action_PJSIPRegister(register, reg_action_id));
 }