/* Asterisk's AMI interface functions in TTCN-3 * (C) 2024 by sysmocom - s.f.m.c. GmbH * Author: Pau Espin Pedrol * All rights reserved. * * Released under the terms of GNU General Public License, Version 2 or * (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * https://docs.asterisk.org/Configuration/Interfaces/Asterisk-Manager-Interface-AMI/AMI-v2-Specification/ */ module AMI_Functions { import from Misc_Helpers all; import from Osmocom_Types all; import from IPL4asp_Types all; import from IPL4asp_PortType all; import from Socket_API_Definitions all; import from TCCConversion_Functions all; modulepar { float mp_ami_prompt_timeout := 10.0; } const charstring AMI_FIELD_ACTION := "Action"; const charstring AMI_FIELD_ACTION_ID := "ActionID"; const charstring AMI_FIELD_CHANNEL := "Channel"; const charstring AMI_FIELD_CHAN_TYPE := "ChannelType"; const charstring AMI_FIELD_CONTEXT := "Context"; const charstring AMI_FIELD_DOMAIN := "Domain"; const charstring AMI_FIELD_EVENT := "Event"; const charstring AMI_FIELD_INFO := "Info"; const charstring AMI_FIELD_RESPONSE := "Response"; const charstring AMI_FIELD_SECRET := "Secret"; const charstring AMI_FIELD_STATUS := "Status"; const charstring AMI_FIELD_USERNAME := "Username"; /* Extensions: */ const charstring AMI_FIELD_ALGORITHM := "Algorithm"; const charstring AMI_FIELD_AUTN := "AUTN"; const charstring AMI_FIELD_AUTS := "AUTS"; const charstring AMI_FIELD_CK := "CK"; const charstring AMI_FIELD_IK := "IK"; const charstring AMI_FIELD_RAND := "RAND"; const charstring AMI_FIELD_RES := "RES"; const charstring AMI_FIELD_REGISTRATION := "Registration"; type record AMI_Field { charstring key, charstring val } with { encode "TEXT" variant "SEPARATOR(': ', ':\s+')" }; type set of AMI_Field AMI_Msg with { encode "TEXT" variant "SEPARATOR('\r\n', '(\r\n)|[\n]')" variant "END('\r\n', '(\r\n)|[\n]')" }; external function enc_AMI_Msg(in AMI_Msg msg) return charstring with { extension "prototype(convert) encode(TEXT)" } external function dec_AMI_Msg(in charstring stream) return AMI_Msg with { extension "prototype(convert) decode(TEXT)" } template (value) AMI_Field ts_AMI_Field(template (value) charstring key, template (value) charstring val) := { key := key, val := val }; template (present) AMI_Field tr_AMI_Field(template (present) charstring key := ?, template (present) charstring val := ?) := { key := key, val := val }; /* * Field Templates: */ template (value) AMI_Field ts_AMI_Field_Action(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION, val); template (value) AMI_Field ts_AMI_Field_ActionId(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION_ID, val); template (value) AMI_Field ts_AMI_Field_Channel(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_CHANNEL, val); template (value) AMI_Field ts_AMI_Field_ChannelType(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_CHAN_TYPE, val); template (value) AMI_Field ts_AMI_Field_Context(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_CONTEXT, val); template (value) AMI_Field ts_AMI_Field_Domain(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_DOMAIN, val); template (value) AMI_Field ts_AMI_Field_Event(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_EVENT, val); template (value) AMI_Field ts_AMI_Field_Info(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_INFO, val); template (value) AMI_Field ts_AMI_Field_Secret(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_SECRET, val); template (value) AMI_Field ts_AMI_Field_Status(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_STATUS, val); template (value) AMI_Field ts_AMI_Field_Username(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_USERNAME, val); /* Extensions: */ template (value) AMI_Field ts_AMI_Field_Algorithm(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ALGORITHM, val); template (value) AMI_Field ts_AMI_Field_AUTN(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_AUTN, val); template (value) AMI_Field ts_AMI_Field_AUTS(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_AUTS, val); template (value) AMI_Field ts_AMI_Field_CK(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_CK, val); template (value) AMI_Field ts_AMI_Field_IK(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_IK, val); template (value) AMI_Field ts_AMI_Field_RAND(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_RAND, val); template (value) AMI_Field ts_AMI_Field_RES(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_RES, val); template (value) AMI_Field ts_AMI_Field_Registration(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_REGISTRATION, val); template (present) AMI_Field tr_AMI_Field_Action(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_ACTION, val); template (present) AMI_Field tr_AMI_Field_ActionId(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_ACTION_ID, val); template (present) AMI_Field tr_AMI_Field_Channel(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_CHANNEL, val); template (present) AMI_Field tr_AMI_Field_ChannelType(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_CHAN_TYPE, val); template (present) AMI_Field tr_AMI_Field_Context(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_CONTEXT, val); template (present) AMI_Field tr_AMI_Field_Domain(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_DOMAIN, val); template (present) AMI_Field tr_AMI_Field_Event(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_EVENT, val); template (present) AMI_Field tr_AMI_Field_Info(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_INFO, val); template (present) AMI_Field tr_AMI_Field_Response(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_RESPONSE, val); template (present) AMI_Field tr_AMI_Field_Secret(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_SECRET, val); template (present) AMI_Field tr_AMI_Field_Status(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_STATUS, val); template (present) AMI_Field tr_AMI_Field_Username(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_USERNAME, val); /* Extensions: */ template (present) AMI_Field tr_AMI_Field_Algorithm(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_ALGORITHM, val); template (present) AMI_Field tr_AMI_Field_AUTN(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_AUTN, val); template (present) AMI_Field tr_AMI_Field_AUTS(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_AUTS, val); template (present) AMI_Field tr_AMI_Field_CK(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_CK, val); template (present) AMI_Field tr_AMI_Field_IK(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_IK, val); template (present) AMI_Field tr_AMI_Field_RAND(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_RAND, val); template (present) AMI_Field tr_AMI_Field_RES(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_RES, val); template (present) AMI_Field tr_AMI_Field_Registration(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_REGISTRATION, val); template (present) AMI_Field tr_AMI_Field_ResponseSuccess := tr_AMI_Field(pattern @nocase AMI_FIELD_RESPONSE, "Success"); /*********************** * Message Templates: ***********************/ /* * ACTIONS */ /* Action: AuthResponse * Registration: volte_ims * AUTS: */ template (value) AMI_Msg ts_AMI_Action_AuthResponse_AUTS(template (value) charstring registration, template (value) charstring auts, template (value) charstring action_id := "0001") := { ts_AMI_Field_Action("AuthResponse"), ts_AMI_Field_ActionId(action_id), ts_AMI_Field_Registration(registration), ts_AMI_Field_AUTS(auts) }; /* Action: AuthResponse * Registration: volte_ims * RES: * CK: * IK: */ template (value) AMI_Msg ts_AMI_Action_AuthResponse_RES(template (value) charstring registration, template (value) charstring res, template (value) charstring ck, template (value) charstring ik, template (value) charstring action_id := "0001") := { ts_AMI_Field_Action("AuthResponse"), ts_AMI_Field_ActionId(action_id), ts_AMI_Field_Registration(registration), ts_AMI_Field_RES(res), ts_AMI_Field_CK(ck), ts_AMI_Field_IK(ik) }; /* Action: DedicatedBearerStatus * ActionID: * Channel: * Status: */ template (value) AMI_Msg ts_AMI_Action_DedicatedBearerStatus(template (value) charstring channel, template (value) charstring status, template (value) charstring action_id := "0001") := { ts_AMI_Field_Action("DedicatedBearerStatus"), ts_AMI_Field_ActionId(action_id), ts_AMI_Field_Channel(channel), ts_AMI_Field_Status(status) }; /* Action: Login * Username: * Secret: */ template (value) AMI_Msg ts_AMI_Action_Login(charstring username, charstring secret, template (value) charstring action_id := "0001") := { ts_AMI_Field_Action("Login"), ts_AMI_Field_ActionId(action_id), ts_AMI_Field_Username(username), ts_AMI_Field_Secret(secret) }; template (present) AMI_Msg tr_AMI_Action_Login(template(present) charstring username := ?, template(present) charstring secret := ?, template (present) charstring action_id := ?) := superset( tr_AMI_Field_Action("Login"), tr_AMI_Field_ActionId(action_id), tr_AMI_Field_Username(username), tr_AMI_Field_Secret(secret) ); /* Action: PJSIPAccessNetworkInfo * Registration: volte_ims * Info: 3GPP-E-UTRAN-FDD; utran-cell-id-3gpp=2380100010000101 */ template (value) AMI_Msg ts_AMI_Action_PJSIPAccessNetworkInfo(template (value) charstring registration := "volte_ims", template (value) charstring info := "", template (value) charstring action_id := "0001") := { ts_AMI_Field_Action("PJSIPAccessNetworkInfo"), ts_AMI_Field_ActionId(action_id), ts_AMI_Field_Registration(registration), ts_AMI_Field_Info(info) }; function f_ami_gen_PJSIPAccessNetworkInfo_Info_EUTRAN(charstring uli_str) return charstring { return "3GPP-E-UTRAN-FDD; utran-cell-id-3gpp=" & uli_str; } /* Action: PJSIPRegister * ActionID: * Registration: volte_ims */ template (value) AMI_Msg ts_AMI_Action_PJSIPRegister(template (value) charstring registration := "volte_ims", template (value) charstring action_id := "0001") := { ts_AMI_Field_Action("PJSIPRegister"), ts_AMI_Field_ActionId(action_id), ts_AMI_Field_Registration(registration) }; template (present) AMI_Msg tr_AMI_Action_PJSIPRegister(template (present) charstring registration := ?, template (present) charstring action_id := ?) := { tr_AMI_Field_Action("PJSIPRegister"), tr_AMI_Field_ActionId(action_id), tr_AMI_Field_Registration(registration) }; /* Action: PJSIPUnregister * ActionID: * Registration: volte_ims */ template (value) AMI_Msg ts_AMI_Action_PJSIPUnregister(template (value) charstring registration := "volte_ims", template (value) charstring action_id := "0001") := { ts_AMI_Field_Action("PJSIPUnregister"), ts_AMI_Field_ActionId(action_id), ts_AMI_Field_Registration(registration) }; template (present) AMI_Msg tr_AMI_Action_PJSIPUnregister(template (present) charstring registration := ?, template (present) charstring action_id := ?) := { tr_AMI_Field_Action("PJSIPUnregister"), tr_AMI_Field_ActionId(action_id), tr_AMI_Field_Registration(registration) }; /* * RESPONSES */ /* Response: Success */ template (present) AMI_Msg tr_AMI_Response_Success := superset( tr_AMI_Field_ResponseSuccess ); /* Response: Success * ActionId: */ template (present) AMI_Msg tr_AMI_Response_Success_ActionId(template (present) charstring action_id := ?) := superset( tr_AMI_Field_ResponseSuccess, tr_AMI_Field_ActionId(action_id) ); /* * EVENTS */ template (present) AMI_Msg tr_AMI_Event(template (present) charstring ev_name := ?) := superset( tr_AMI_Field_Event(ev_name) ); /* Event: FullyBooted * Privilege: system,all * Status: Fully Booted * Uptime: 4 * LastReload: 4 * */ template (present) AMI_Msg tr_AMI_Event_FullyBooted := tr_AMI_Event("FullyBooted"); /* Event: AuthRequest * Privilege: * Registration: volte_ims * Algorithm: AKAv1-MD5 * RAND: 14987631f65f8e3788a0798b6ebcd08e * AUTN: f6e19a7ccb028000a06b19c9544516e5 */ template (present) AMI_Msg tr_AMI_Event_AuthRequest(template (present) charstring registration := ?, template (present) charstring algorithm := "AKAv1-MD5", template (present) charstring rand := ?, template (present) charstring autn := ?) := superset( tr_AMI_Field_Event("AuthRequest"), tr_AMI_Field_Registration(registration), tr_AMI_Field_Algorithm(algorithm), tr_AMI_Field_RAND(rand), tr_AMI_Field_AUTN(autn) ); /* Event: Registry * Privilege: system,all * ChannelType: PJSIP * Username: sip:238010000090828@172.18.155.103 * Domain: sip:172.18.155.103 * Status: Registered */ template (present) AMI_Msg tr_AMI_Event_Registry(template (present) charstring username := ?, template (present) charstring domain := ?, template (present) charstring status := ?, template (present) charstring chan_type := "PJSIP") := superset( tr_AMI_Field_Event("Registry"), tr_AMI_Field_ChannelType(chan_type), tr_AMI_Field_Username(username), tr_AMI_Field_Domain(domain), tr_AMI_Field_Status(status) ); /* Event: Newchannel * Privilege: call,all * Channel: PJSIP/volte_ims-00000001 * ChannelState: 0 * ChannelStateDesc: Down * CallerIDNum: * CallerIDName: * ConnectedLineNum: * ConnectedLineName: * Language: en * AccountCode: * Context: volte_ims * Exten: s * Priority: 1 * Uniqueid: 1718732522.1 * LinkEvent: Newstate * Privilege: call,all * Channel: PJSIP/volte_ims-00000001 * ChannelState: 5 * ChannelStateDesc: Ringing * CallerIDNum: 90829 * CallerIDName: * ConnectedLineNum: 0501 * ConnectedLineName: * Language: en * AccountCode: * Context: volte_ims * Exten: 90829 * Priority: 1 * Uniqueid: 1718732522.1 * Linkedid: 1718732522.0 */ template (present) AMI_Msg tr_AMI_Event_Newchannel(template (present) charstring context := ?) := superset( tr_AMI_Field_Event("Newchannel"), tr_AMI_Field_Context(context) ); /*********************** * Adapter: ***********************/ type record AMI_Adapter_Parameters { charstring remote_host, IPL4asp_Types.PortNumber remote_port, charstring local_host, IPL4asp_Types.PortNumber local_port, charstring welcome_str } const AMI_Adapter_Parameters c_default_AMI_Adapter_pars := { remote_host := "127.0.0.1", remote_port := 5038, local_host := "0.0.0.0", local_port := 0, welcome_str := "Asterisk Call Manager/9.0.0\r\n" }; type port AMI_Msg_PT message { inout AMI_Msg; } with { extension "internal" }; type component AMI_Adapter_CT { port IPL4asp_PT IPL4; port AMI_Msg_PT CLIENT; var AMI_Adapter_Parameters g_pars; /* Connection identifier of the client itself */ var IPL4asp_Types.ConnectionId g_self_conn_id := -1; } /* Function to use to connect as client to a remote IPA Server */ private function f_AMI_Adapter_connect() runs on AMI_Adapter_CT { var IPL4asp_Types.Result res; map(self:IPL4, system:IPL4); res := IPL4asp_PortType.f_IPL4_connect(IPL4, g_pars.remote_host, g_pars.remote_port, g_pars.local_host, g_pars.local_port, 0, { tcp:={} }); if (not ispresent(res.connId)) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Could not connect AMI socket from ", g_pars.local_host, " port ", g_pars.local_port, " to ", g_pars.remote_host, " port ", g_pars.remote_port, "; check your configuration")); } g_self_conn_id := res.connId; log("AMI connected, ConnId=", g_self_conn_id) } private function f_ASP_RecvFrom_msg_to_charstring(ASP_RecvFrom rx_rf) return charstring { return oct2char(rx_rf.msg); } /* Function to use to connect as client to a remote IPA Server */ private function f_AMI_Adapter_wait_rx_welcome_str() runs on AMI_Adapter_CT { var ASP_RecvFrom rx_rf; var charstring rx_str; timer Twelcome := 3.0; Twelcome.start; alt { [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf { rx_str := f_ASP_RecvFrom_msg_to_charstring(rx_rf); if (g_pars.welcome_str != rx_str) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("AMI Welcome message mismatch: '", rx_str, "' vs exp '", g_pars.welcome_str, "'")); } } [] Twelcome.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("AMI Welcome timeout")); } } Twelcome.stop; log("AMI Welcome message received: '", rx_str, "'"); } private function dec_AMI_Msg_ext(charstring txt) return AMI_Msg { log("AMI dec: '", txt, "'"); /* TEXT Enc/dec is not happy with empty values, workaround it: */ var Misc_Helpers.ro_charstring fields := { "AccountCode", "AccountID", "Challenge", "DestExten", "Exten", "Extension", "Hint", "Value" }; var charstring patched_txt := txt; for (var integer i := 0; i < lengthof(fields); i := i +1) { patched_txt := f_str_replace(patched_txt, fields[i] & ": \r\n", ""); } /* "AppData" field sometimes has a value containing separator ": ", which makes * TEXT dec not happy. Workaround it for now by removing the whole field line: * "AppData: 5,0502: Call pjsip endpoint from 0501\r\n" */ var integer pos := f_strstr(patched_txt, "AppData: ", 0); if (pos >= 0) { var integer pos_end := f_strstr(patched_txt, "\r\n", pos) + lengthof("\r\n"); var charstring to_remove := substr(patched_txt, pos, pos_end - pos); patched_txt := f_str_replace(patched_txt, to_remove, ""); } log("AMI patched dec: '", patched_txt, "'"); return dec_AMI_Msg(patched_txt); } function f_AMI_Adapter_main(AMI_Adapter_Parameters pars := c_default_AMI_Adapter_pars) runs on AMI_Adapter_CT { var AMI_Msg msg; var charstring rx, buf := ""; var integer fd; var ASP_RecvFrom rx_rf; var ASP_Event rx_ev; g_pars := pars; f_AMI_Adapter_connect(); f_AMI_Adapter_wait_rx_welcome_str(); while (true) { alt { [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf { var charstring rx_str := f_ASP_RecvFrom_msg_to_charstring(rx_rf); log("AMI rx: '", rx_str, "'"); buf := buf & rx_str; log("AMI buf: '", buf, "'"); /* If several messages come together */ var boolean last_is_complete := f_str_endswith(buf, "\r\n\r\n"); var Misc_Helpers.ro_charstring msgs := f_str_split(buf, "\r\n\r\n"); log("AMI split: ", msgs); if (lengthof(msgs) > 0) { for (var integer i := 0; i < lengthof(msgs) - 1; i := i + 1) { var charstring txt := msgs[i] & "\r\n"; msg := dec_AMI_Msg_ext(txt); CLIENT.send(msg); } if (last_is_complete) { var charstring txt := msgs[lengthof(msgs) - 1] & "\r\n"; msg := dec_AMI_Msg_ext(txt); CLIENT.send(msg); buf := ""; } else { buf := msgs[lengthof(msgs) - 1]; } } log("AMI remain buf: '", buf, "'"); } [] IPL4.receive(ASP_ConnId_ReadyToRelease:?) { } [] IPL4.receive(ASP_Event:?) -> value rx_ev { log("Rx AMI ASP_Event: ", rx_ev); } [] 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) & "\r\n"; var ASP_SendTo tx := { connId := g_self_conn_id, remName := g_pars.remote_host, remPort := g_pars.remote_port, proto := { tcp := {} }, msg := char2oct(tx_txt) }; IPL4.send(tx); } } } } /* * Functions: */ /* Generate a random "ActionId" value: */ function f_gen_action_id() return charstring { return hex2str(f_rnd_hexstring(16)); } function f_ami_msg_find(AMI_Msg msg, template (present) charstring key := ?) return template (omit) AMI_Field { var integer i; for (i := 0; i < lengthof(msg); i := i + 1) { if (not ispresent(msg[i])) { continue; } if (match(msg[i].key, key)) { return msg[i]; } } return omit; } function f_ami_msg_find_or_fail(AMI_Msg msg, template (present) charstring key := ?) return AMI_Field { var template (omit) AMI_Field field; field := f_ami_msg_find(msg, key); if (istemplatekind(field, "omit")) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Key ", key, " not found in ", msg)); } return valueof(field); } function f_ami_msg_get_value(AMI_Msg msg, template (present) charstring key := ?) return template (omit) charstring { var template (omit) AMI_Field field; field := f_ami_msg_find(msg, key); if (istemplatekind(field, "omit")) { return omit; } return field.val; } function f_ami_msg_get_value_or_fail(AMI_Msg msg, template (present) charstring key := ?) return template charstring { var AMI_Field field; field := f_ami_msg_find_or_fail(msg, key); return field.val; } 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(rx_timeout); pt.send(tx_msg); alt { [] pt.receive(AMI_Msg:?) -> value rx_msg; [] T.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("AMI Response timeout: ", tx_msg)); } } T.stop; return rx_msg; } altstep as_ami_rx_ignore(AMI_Msg_PT pt) { var AMI_Msg msg; [] pt.receive(AMI_Msg:?) -> value msg { log("Ignoring AMI message := ", msg); repeat; } } 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("Received unexpected AMI message := ", msg, "\nvs exp := ", exp_msg)); } } 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_wait_rx_msg(AMI_Msg_PT pt, template (present) AMI_Msg msg_expect := ?, boolean fail_others := true, float rx_timeout := 10.0) return AMI_Msg { var AMI_Msg ami_msg; timer tAMI; tAMI.start(rx_timeout); alt { [] pt.receive(msg_expect) -> value ami_msg; [not fail_others] as_ami_rx_ignore(pt); [fail_others] as_ami_rx_fail(pt, msg_expect); [] tAMI.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("AMI msg timeout: ", msg_expect)); } } tAMI.stop; return ami_msg; } 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 { pt.send(tx_msg); return f_ami_wait_rx_msg(pt, exp_ret, fail_others, rx_timeout); } function f_ami_transceive_match_response_success(AMI_Msg_PT pt, template (value) AMI_Msg tx_msg, boolean fail_others := true) { 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); if (isvalue(action_id)) { exp_resp := tr_AMI_Response_Success_ActionId(action_id); } else { exp_resp := tr_AMI_Response_Success; } f_ami_transceive_match(pt, tx_msg, exp_resp, fail_others := fail_others); } function f_ami_action_login(AMI_Msg_PT pt, charstring username, charstring secret) { var charstring reg_action_id := f_gen_action_id(); f_ami_transceive_match_response_success(pt, ts_AMI_Action_Login(username, secret, reg_action_id)); } function f_ami_action_PJSIPAccessNetworkInfo(AMI_Msg_PT pt, template (value) charstring registration, template (value) charstring info) { var charstring reg_action_id := f_gen_action_id(); f_ami_transceive_match_response_success(pt, ts_AMI_Action_PJSIPAccessNetworkInfo(registration, info, reg_action_id)); } function f_ami_action_DedicatedBearerStatus(AMI_Msg_PT pt, template (value) charstring channel, template (value) charstring status, boolean fail_others := true) { var charstring reg_action_id := f_gen_action_id(); f_ami_transceive_match_response_success(pt, ts_AMI_Action_DedicatedBearerStatus(channel, status, reg_action_id), fail_others := fail_others); } 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)); } function f_ami_action_PJSIPUnregister(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_PJSIPUnregister(register, reg_action_id)); } function f_ami_action_AuthResponse_AUTS(AMI_Msg_PT pt, template (value) charstring registration, template (value) charstring auts) { var charstring reg_action_id := f_gen_action_id(); f_ami_transceive_match_response_success(pt, ts_AMI_Action_AuthResponse_AUTS(registration, auts, reg_action_id)); } function f_ami_action_AuthResponse_RES(AMI_Msg_PT pt, template (value) charstring registration, template (value) charstring res, template (value) charstring ck, template (value) charstring ik) { var charstring reg_action_id := f_gen_action_id(); f_ami_transceive_match_response_success(pt, ts_AMI_Action_AuthResponse_RES(registration, res, ck, ik, reg_action_id)); } private function f_ami_selftest_decode(charstring txt) { log("Text to decode: '", txt, "'"); var AMI_Msg msg := dec_AMI_Msg(txt); log("AMI_Msg decoded: ", msg); } function f_ami_selftest() { f_ami_selftest_decode("AppData: 5,0502: Call pjsip endpoint from 0501\r\n"); } }