blob: 4969905f6e8b1895d9bc4dbc9fbe58727336289d [file] [log] [blame]
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +02001/* Asterisk's AMI interface functions in TTCN-3
2 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
3 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12/*
13 * https://docs.asterisk.org/Configuration/Interfaces/Asterisk-Manager-Interface-AMI/AMI-v2-Specification/
14 */
15module AMI_Functions {
16
17import from Misc_Helpers all;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020018import from Osmocom_Types all;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +020019import from IPL4asp_Types all;
20import from IPL4asp_PortType all;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020021import from Socket_API_Definitions all;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +020022import from TCCConversion_Functions all;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020023
24modulepar {
25 float mp_ami_prompt_timeout := 10.0;
26}
27
28const charstring AMI_FIELD_ACTION := "Action";
Pau Espin Pedrol941ca3c2024-05-09 16:46:58 +020029const charstring AMI_FIELD_ACTION_ID := "ActionID";
Pau Espin Pedrole98f4412024-05-17 19:33:46 +020030const charstring AMI_FIELD_EVENT := "Event";
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +020031const charstring AMI_FIELD_INFO := "Info";
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020032const charstring AMI_FIELD_USERNAME := "Username";
33const charstring AMI_FIELD_SECRET := "Secret";
34const charstring AMI_FIELD_RESPONSE := "Response";
35
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020036/* Extensions: */
37const charstring AMI_FIELD_REGISTRATION := "Registration";
38
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020039type record AMI_Field {
40 charstring key,
41 charstring val
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +020042} with {
43 encode "TEXT"
44 variant "SEPARATOR(': ', ':\s+')"
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020045};
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +020046
47type set of AMI_Field AMI_Msg with {
48 encode "TEXT"
49 variant "SEPARATOR('\r\n', '(\r\n)|[\n]')"
50 variant "END('\r\n', '(\r\n)|[\n]')"
51};
52
53external function enc_AMI_Msg(in AMI_Msg msg) return charstring
54 with { extension "prototype(convert) encode(TEXT)" }
55external function dec_AMI_Msg(in charstring stream) return AMI_Msg
56 with { extension "prototype(convert) decode(TEXT)" }
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020057
58template (value) AMI_Field
59ts_AMI_Field(template (value) charstring key,
60 template (value) charstring val) := {
61 key := key,
62 val := val
63};
64
65template (present) AMI_Field
66tr_AMI_Field(template (present) charstring key := ?,
67 template (present) charstring val := ?) := {
68 key := key,
69 val := val
70};
71
72/*
73 * Field Templates:
74 */
75
76template (value) AMI_Field
77ts_AMI_Field_Action(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION, val);
78template (value) AMI_Field
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020079ts_AMI_Field_ActionId(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION_ID, val);
80template (value) AMI_Field
Pau Espin Pedrole98f4412024-05-17 19:33:46 +020081ts_AMI_Field_Event(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_EVENT, val);
82template (value) AMI_Field
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +020083ts_AMI_Field_Info(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_INFO, val);
84template (value) AMI_Field
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020085ts_AMI_Field_Username(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_USERNAME, val);
86template (value) AMI_Field
87ts_AMI_Field_Secret(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_SECRET, val);
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020088/* Extensions: */
89template (value) AMI_Field
90ts_AMI_Field_Registration(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_REGISTRATION, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020091
92template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +020093tr_AMI_Field_Action(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_ACTION, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020094template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +020095tr_AMI_Field_ActionId(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_ACTION_ID, val);
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020096template (present) AMI_Field
Pau Espin Pedrole98f4412024-05-17 19:33:46 +020097tr_AMI_Field_Event(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_EVENT, val);
98template (present) AMI_Field
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +020099tr_AMI_Field_Info(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_INFO, val);
100template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200101tr_AMI_Field_Username(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_USERNAME, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200102template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200103tr_AMI_Field_Secret(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_SECRET, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200104template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200105tr_AMI_Field_Response(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_RESPONSE, val);
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200106/* Extensions: */
107template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200108tr_AMI_Field_Registration(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_REGISTRATION, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200109
110
111template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200112tr_AMI_Field_ResponseSuccess := tr_AMI_Field(pattern @nocase AMI_FIELD_RESPONSE, "Success");
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200113
114
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200115/***********************
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200116 * Message Templates:
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200117 ***********************/
118
119/*
120 * ACTIONS
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200121 */
122
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200123/* Action: Login
124 * Username: <value>
125 * Secret: <value>
126 */
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200127template (value) AMI_Msg
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200128ts_AMI_Action_Login(charstring username,
129 charstring secret,
130 template (value) charstring action_id := "0001") := {
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200131 ts_AMI_Field_Action("Login"),
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200132 ts_AMI_Field_ActionId(action_id),
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200133 ts_AMI_Field_Username(username),
134 ts_AMI_Field_Secret(secret)
135};
136
137template (present) AMI_Msg
138tr_AMI_Action_Login(template(present) charstring username := ?,
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200139 template(present) charstring secret := ?,
140 template (present) charstring action_id := ?) := superset(
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200141 tr_AMI_Field_Action("Login"),
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200142 tr_AMI_Field_ActionId(action_id),
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200143 tr_AMI_Field_Username(username),
144 tr_AMI_Field_Secret(secret)
145);
146
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200147/* Action: PJSIPAccessNetworkInfo
148 * Registration: volte_ims
149 * Info: 3GPP-E-UTRAN-FDD; utran-cell-id-3gpp=2380100010000101
150 */
151template (value) AMI_Msg
152ts_AMI_Action_PJSIPAccessNetworkInfo(template (value) charstring registration := "volte_ims",
153 template (value) charstring info := "",
154 template (value) charstring action_id := "0001") := {
155 ts_AMI_Field_Action("PJSIPAccessNetworkInfo"),
156 ts_AMI_Field_ActionId(action_id),
157 ts_AMI_Field_Registration(registration),
158 ts_AMI_Field_Info(info)
159};
160function f_ami_gen_PJSIPAccessNetworkInfo_Info_EUTRAN(charstring uli_str) return charstring {
161 return "3GPP-E-UTRAN-FDD; utran-cell-id-3gpp=" & uli_str;
162}
163
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200164/* Action: PJSIPRegister
165 * ActionID: <value>
166 * Registration: volte_ims
167 */
168template (value) AMI_Msg
169ts_AMI_Action_PJSIPRegister(template (value) charstring registration := "volte_ims",
170 template (value) charstring action_id := "0001") := {
171 ts_AMI_Field_Action("PJSIPRegister"),
172 ts_AMI_Field_ActionId(action_id),
173 ts_AMI_Field_Registration(registration)
174};
175template (present) AMI_Msg
176tr_AMI_Action_PJSIPRegister(template (present) charstring registration := ?,
177 template (present) charstring action_id := ?) := {
178 tr_AMI_Field_Action("PJSIPRegister"),
179 tr_AMI_Field_ActionId(action_id),
180 tr_AMI_Field_Registration(registration)
181};
182
183/*
184 * RESPONSES
185 */
186
187/* Response: Success
188 */
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200189template (present) AMI_Msg
190tr_AMI_Response_Success := superset(
191 tr_AMI_Field_ResponseSuccess
192);
193
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200194/* Response: Success
195 * ActionId: <value>
196 */
197template (present) AMI_Msg
198tr_AMI_Response_Success_ActionId(template (present) charstring action_id := ?) := superset(
199 tr_AMI_Field_ResponseSuccess,
200 tr_AMI_Field_ActionId(action_id)
201);
202
Pau Espin Pedrole98f4412024-05-17 19:33:46 +0200203/*
204 * EVENTS
205 */
206template (present) AMI_Msg
207tr_AMI_Event(template (present) charstring ev_name := ?) := superset(
208 tr_AMI_Field_Event(ev_name)
209);
210
211/* Event: FullyBooted
212 * Privilege: system,all
213 * Status: Fully Booted
214 * Uptime: 4
215 * LastReload: 4 *
216 */
217template (present) AMI_Msg
218tr_AMI_Event_FullyBooted := tr_AMI_Event("FullyBooted");
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200219
220/***********************
221 * Adapter:
222 ***********************/
223
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200224type record AMI_Adapter_Parameters {
225 charstring remote_host,
226 IPL4asp_Types.PortNumber remote_port,
227 charstring local_host,
228 IPL4asp_Types.PortNumber local_port,
229 charstring welcome_str
230}
231
232const AMI_Adapter_Parameters c_default_AMI_Adapter_pars := {
233 remote_host := "127.0.0.1",
234 remote_port := 5038,
235 local_host := "0.0.0.0",
236 local_port := 0,
237 welcome_str := "Asterisk Call Manager/9.0.0\r\n"
238};
239
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200240type port AMI_Msg_PT message {
241 inout AMI_Msg;
242} with { extension "internal" };
243
244type component AMI_Adapter_CT {
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200245 port IPL4asp_PT IPL4;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200246 port AMI_Msg_PT CLIENT;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200247 var AMI_Adapter_Parameters g_pars;
248
249 /* Connection identifier of the client itself */
250 var IPL4asp_Types.ConnectionId g_self_conn_id := -1;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200251}
252
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200253/* Function to use to connect as client to a remote IPA Server */
254private function f_AMI_Adapter_connect() runs on AMI_Adapter_CT {
255 var IPL4asp_Types.Result res;
256 map(self:IPL4, system:IPL4);
257 res := IPL4asp_PortType.f_IPL4_connect(IPL4, g_pars.remote_host, g_pars.remote_port,
258 g_pars.local_host, g_pars.local_port, 0, { tcp:={} });
259 if (not ispresent(res.connId)) {
260 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
261 log2str("Could not connect AMI socket from ", g_pars.local_host, " port ",
262 g_pars.local_port, " to ", g_pars.remote_host, " port ", g_pars.remote_port,
263 "; check your configuration"));
264 }
265 g_self_conn_id := res.connId;
266 log("AMI connected, ConnId=", g_self_conn_id)
267}
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200268
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200269private function f_ASP_RecvFrom_msg_to_charstring(ASP_RecvFrom rx_rf) return charstring {
270 return oct2char(rx_rf.msg);
271}
272
273/* Function to use to connect as client to a remote IPA Server */
274private function f_AMI_Adapter_wait_rx_welcome_str() runs on AMI_Adapter_CT {
275 var ASP_RecvFrom rx_rf;
276 var charstring rx_str;
277 timer Twelcome := 3.0;
278
279 Twelcome.start;
280 alt {
281 [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf {
282 rx_str := f_ASP_RecvFrom_msg_to_charstring(rx_rf);
283 if (g_pars.welcome_str != rx_str) {
284 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
285 log2str("AMI Welcome message mismatch: '", rx_str,
286 "' vs exp '", g_pars.welcome_str, "'"));
287 }
288 }
289 [] Twelcome.timeout {
290 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
291 log2str("AMI Welcome timeout"));
292 }
293 }
294 Twelcome.stop;
295 log("AMI Welcome message received: '", rx_str, "'");
296}
297
298private function dec_AMI_Msg_ext(charstring txt) return AMI_Msg {
299 log("AMI dec: '", txt, "'");
300 /* TEXT Enc/dec is not happy with empty values, workaround it: */
301 var charstring patched_txt := f_str_replace(txt, "Challenge: \r\n", "");
302 patched_txt := f_str_replace(patched_txt, "AccountCode: \r\n", "");
303 patched_txt := f_str_replace(patched_txt, "Value: \r\n", "");
304 patched_txt := f_str_replace(patched_txt, "DestExten: \r\n", "");
305 patched_txt := f_str_replace(patched_txt, "Exten: \r\n", "");
306 patched_txt := f_str_replace(patched_txt, "Extension: \r\n", "");
Pau Espin Pedrolbf0e14d2024-05-16 18:34:13 +0200307 patched_txt := f_str_replace(patched_txt, "Hint: \r\n", "");
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200308
309 /* "AppData" field sometimes has a value containing separator ": ", which makes
310 * TEXT dec not happy. Workaround it for now by removing the whole field line:
311 * "AppData: 5,0502: Call pjsip endpoint from 0501\r\n"
312 */
313 var integer pos := f_strstr(patched_txt, "AppData: ", 0);
314 if (pos >= 0) {
315 var integer pos_end := f_strstr(patched_txt, "\r\n", pos) + lengthof("\r\n");
316 var charstring to_remove := substr(patched_txt, pos, pos_end - pos);
317 patched_txt := f_str_replace(patched_txt, to_remove, "");
318 }
319
320 log("AMI patched dec: '", patched_txt, "'");
321 return dec_AMI_Msg(patched_txt);
322}
323
324function f_AMI_Adapter_main(AMI_Adapter_Parameters pars := c_default_AMI_Adapter_pars)
325 runs on AMI_Adapter_CT {
326 var AMI_Msg msg;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200327 var charstring rx, buf := "";
328 var integer fd;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200329 var ASP_RecvFrom rx_rf;
330 var ASP_Event rx_ev;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200331
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200332 g_pars := pars;
333
334 f_AMI_Adapter_connect();
335
336 f_AMI_Adapter_wait_rx_welcome_str();
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200337
338 while (true) {
339
340 alt {
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200341 [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf {
342 var charstring rx_str := f_ASP_RecvFrom_msg_to_charstring(rx_rf);
343 log("AMI rx: '", rx_str, "'");
344 buf := buf & rx_str;
345 log("AMI buf: '", buf, "'");
346
347 /* If several messages come together */
348 var boolean last_is_complete := f_str_endswith(buf, "\r\n\r\n");
349 var Misc_Helpers.ro_charstring msgs := f_str_split(buf, "\r\n\r\n");
350 log("AMI split: ", msgs);
351 if (lengthof(msgs) > 0) {
352 for (var integer i := 0; i < lengthof(msgs) - 1; i := i + 1) {
353 var charstring txt := msgs[i] & "\r\n";
354 msg := dec_AMI_Msg_ext(txt);
355 CLIENT.send(msg);
356 }
357 if (last_is_complete) {
358 var charstring txt := msgs[lengthof(msgs) - 1] & "\r\n";
359 msg := dec_AMI_Msg_ext(txt);
360 CLIENT.send(msg);
361 buf := "";
362 } else {
363 buf := msgs[lengthof(msgs) - 1];
364 }
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200365 }
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200366 log("AMI remain buf: '", buf, "'");
367 }
368 [] IPL4.receive(ASP_ConnId_ReadyToRelease:?) {
369 }
370
371 [] IPL4.receive(ASP_Event:?) -> value rx_ev {
372 log("Rx AMI ASP_Event: ", rx_ev);
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200373 }
374 [] CLIENT.receive(AMI_Msg:?) -> value msg {
375 /* TODO: in the future, queue Action if there's already one Action in transit, to fullfill AMI requirements. */
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200376 var charstring tx_txt := enc_AMI_Msg(msg) & "\r\n";
377
378 var ASP_SendTo tx := {
379 connId := g_self_conn_id,
380 remName := g_pars.remote_host,
381 remPort := g_pars.remote_port,
382 proto := { tcp := {} },
383 msg := char2oct(tx_txt)
384 };
385 IPL4.send(tx);
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200386 }
387 }
388 }
389}
390
391
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200392/*
393 * Functions:
394 */
395
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200396/* Generate a random "ActionId" value: */
397function f_gen_action_id() return charstring {
398 return hex2str(f_rnd_hexstring(16));
399}
400
401function f_ami_msg_find(AMI_Msg msg,
402 template (present) charstring key := ?)
403return template (omit) AMI_Field {
404 var integer i;
405
406 for (i := 0; i < lengthof(msg); i := i + 1) {
407 if (not ispresent(msg[i])) {
408 continue;
409 }
410 if (match(msg[i].key, key)) {
411 return msg[i];
412 }
413 }
414 return omit;
415}
416
417function f_ami_msg_find_or_fail(AMI_Msg msg,
418 template (present) charstring key := ?)
419return AMI_Field {
420 var template (omit) AMI_Field field;
421 field := f_ami_msg_find(msg, key);
422 if (istemplatekind(field, "omit")) {
423 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
424 log2str("Key ", key, " not found in ", msg));
425 }
426 return valueof(field);
427}
428
429function f_ami_msg_get_value(AMI_Msg msg,
430 template (present) charstring key := ?)
431return template (omit) charstring {
432 var template (omit) AMI_Field field;
433 field := f_ami_msg_find(msg, key);
434 if (istemplatekind(field, "omit")) {
435 return omit;
436 }
437 return field.val;
438}
439
440function f_ami_msg_get_value_or_fail(AMI_Msg msg,
441 template (present) charstring key := ?)
442return template charstring {
443 var AMI_Field field;
444 field := f_ami_msg_find_or_fail(msg, key);
445 return field.val;
446}
447
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200448function f_ami_transceive_ret(AMI_Msg_PT pt, template (value) AMI_Msg tx_msg, float rx_timeout := 10.0) return AMI_Msg {
449 var AMI_Msg rx_msg;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200450 timer T;
451
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200452 T.start(rx_timeout);
453 pt.send(tx_msg);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200454 alt {
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200455 [] pt.receive(AMI_Msg:?) -> value rx_msg;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200456 [] T.timeout {
457 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200458 log2str("AMI Response timeout: ", tx_msg));
459 }
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200460 }
461 T.stop;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200462 return rx_msg;
463
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200464}
465
Pau Espin Pedrole98f4412024-05-17 19:33:46 +0200466altstep as_ami_rx_ignore(AMI_Msg_PT pt)
467{
468 var AMI_Msg msg;
469 [] pt.receive(AMI_Msg:?) -> value msg {
470 log("Ignoring AMI message := ", msg);
471 repeat;
472 }
473}
474
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200475private altstep as_ami_rx_fail(AMI_Msg_PT pt, template AMI_Msg exp_msg := *)
476{
477 var AMI_Msg msg;
478 [] pt.receive(AMI_Msg:?) -> value msg {
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200479 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200480 log2str("Received unexpected AMI message := ", msg, "\nvs exp := ", exp_msg));
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200481 }
482}
483
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200484altstep as_ami_expect_msg(AMI_Msg_PT pt, template (present) AMI_Msg msg_expect, boolean fail_others := true)
485{
486 [] pt.receive(msg_expect);
487 [fail_others] as_ami_rx_fail(pt, msg_expect);
488}
489
490function f_ami_transceive_match(AMI_Msg_PT pt,
491 template (value) AMI_Msg tx_msg,
492 template (present) AMI_Msg exp_ret := ?,
493 boolean fail_others := true,
494 float rx_timeout := 10.0) return AMI_Msg {
495 var AMI_Msg rx_msg;
496 timer T;
497
498 T.start(rx_timeout);
499 pt.send(tx_msg);
500 alt {
501 [] pt.receive(exp_ret) -> value rx_msg;
502 [not fail_others] pt.receive(AMI_Msg:?) -> value rx_msg {
503 log("AMI: Ignoring Rx msg ", rx_msg);
504 repeat;
505 }
506 [fail_others] as_ami_rx_fail(pt, exp_ret);
507 [] T.timeout {
508 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
509 log2str("AMI Response timeout: ", tx_msg));
510 }
511 }
512 T.stop;
513 return rx_msg;
514}
515
516function f_ami_transceive_match_response_success(AMI_Msg_PT pt,
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200517 template (value) AMI_Msg tx_msg) {
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200518 var template (present) AMI_Msg exp_resp;
519 var template (omit) charstring action_id := f_ami_msg_get_value(valueof(tx_msg), AMI_FIELD_ACTION_ID);
520 if (isvalue(action_id)) {
521 exp_resp := tr_AMI_Response_Success_ActionId(action_id);
522 } else {
523 exp_resp := tr_AMI_Response_Success;
524 }
525 f_ami_transceive_match(pt, tx_msg, exp_resp);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200526}
527
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200528function f_ami_action_login(AMI_Msg_PT pt, charstring username, charstring secret) {
Pau Espin Pedrol77976a62024-05-08 21:17:38 +0200529 var charstring reg_action_id := f_gen_action_id();
530 f_ami_transceive_match_response_success(pt, ts_AMI_Action_Login(username, secret, reg_action_id));
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200531}
532
Pau Espin Pedrol0c5c6472024-05-21 13:13:49 +0200533function f_ami_action_PJSIPAccessNetworkInfo(AMI_Msg_PT pt,
534 template (value) charstring registration,
535 template (value) charstring info) {
536 var charstring reg_action_id := f_gen_action_id();
537 f_ami_transceive_match_response_success(pt, ts_AMI_Action_PJSIPAccessNetworkInfo(registration, info, reg_action_id));
538}
539
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200540function f_ami_action_PJSIPRegister(AMI_Msg_PT pt, charstring register) {
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200541 var charstring reg_action_id := f_gen_action_id();
542 f_ami_transceive_match_response_success(pt, ts_AMI_Action_PJSIPRegister(register, reg_action_id));
543}
544
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200545private function f_ami_selftest_decode(charstring txt) {
546 log("Text to decode: '", txt, "'");
547 var AMI_Msg msg := dec_AMI_Msg(txt);
548 log("AMI_Msg decoded: ", msg);
549}
550
551function f_ami_selftest() {
552 f_ami_selftest_decode("AppData: 5,0502: Call pjsip endpoint from 0501\r\n");
553}
554
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200555}