blob: c1211a922a8f71aa73f2eb4cba586681c1ffb43d [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 Pedrol4362dbd2024-04-26 19:31:27 +020029const charstring AMI_FIELD_ACTION_ID := "ActionId";
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020030const charstring AMI_FIELD_USERNAME := "Username";
31const charstring AMI_FIELD_SECRET := "Secret";
32const charstring AMI_FIELD_RESPONSE := "Response";
33
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020034/* Extensions: */
35const charstring AMI_FIELD_REGISTRATION := "Registration";
36
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020037type record AMI_Field {
38 charstring key,
39 charstring val
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +020040} with {
41 encode "TEXT"
42 variant "SEPARATOR(': ', ':\s+')"
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020043};
Pau Espin Pedrolde7a4852024-04-19 16:20:45 +020044
45type set of AMI_Field AMI_Msg with {
46 encode "TEXT"
47 variant "SEPARATOR('\r\n', '(\r\n)|[\n]')"
48 variant "END('\r\n', '(\r\n)|[\n]')"
49};
50
51external function enc_AMI_Msg(in AMI_Msg msg) return charstring
52 with { extension "prototype(convert) encode(TEXT)" }
53external function dec_AMI_Msg(in charstring stream) return AMI_Msg
54 with { extension "prototype(convert) decode(TEXT)" }
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020055
56template (value) AMI_Field
57ts_AMI_Field(template (value) charstring key,
58 template (value) charstring val) := {
59 key := key,
60 val := val
61};
62
63template (present) AMI_Field
64tr_AMI_Field(template (present) charstring key := ?,
65 template (present) charstring val := ?) := {
66 key := key,
67 val := val
68};
69
70/*
71 * Field Templates:
72 */
73
74template (value) AMI_Field
75ts_AMI_Field_Action(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION, val);
76template (value) AMI_Field
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020077ts_AMI_Field_ActionId(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION_ID, val);
78template (value) AMI_Field
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020079ts_AMI_Field_Username(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_USERNAME, val);
80template (value) AMI_Field
81ts_AMI_Field_Secret(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_SECRET, val);
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020082/* Extensions: */
83template (value) AMI_Field
84ts_AMI_Field_Registration(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_REGISTRATION, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020085
86template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +020087tr_AMI_Field_Action(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_ACTION, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020088template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +020089tr_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 +020090template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +020091tr_AMI_Field_Username(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_USERNAME, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020092template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +020093tr_AMI_Field_Secret(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_SECRET, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020094template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +020095tr_AMI_Field_Response(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_RESPONSE, val);
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +020096/* Extensions: */
97template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +020098tr_AMI_Field_Registration(template (present) charstring val := ?) := tr_AMI_Field(pattern @nocase AMI_FIELD_REGISTRATION, val);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +020099
100
101template (present) AMI_Field
Pau Espin Pedrol21c084f2024-05-09 16:42:16 +0200102tr_AMI_Field_ResponseSuccess := tr_AMI_Field(pattern @nocase AMI_FIELD_RESPONSE, "Success");
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200103
104
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200105/***********************
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200106 * Message Templates:
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200107 ***********************/
108
109/*
110 * ACTIONS
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200111 */
112
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200113/* Action: Login
114 * Username: <value>
115 * Secret: <value>
116 */
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200117template (value) AMI_Msg
118ts_AMI_Action_Login(charstring username, charstring secret) := {
119 ts_AMI_Field_Action("Login"),
120 ts_AMI_Field_Username(username),
121 ts_AMI_Field_Secret(secret)
122};
123
124template (present) AMI_Msg
125tr_AMI_Action_Login(template(present) charstring username := ?,
126 template(present) charstring secret := ?) := superset(
127 tr_AMI_Field_Action("Login"),
128 tr_AMI_Field_Username(username),
129 tr_AMI_Field_Secret(secret)
130);
131
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200132/* Action: PJSIPRegister
133 * ActionID: <value>
134 * Registration: volte_ims
135 */
136template (value) AMI_Msg
137ts_AMI_Action_PJSIPRegister(template (value) charstring registration := "volte_ims",
138 template (value) charstring action_id := "0001") := {
139 ts_AMI_Field_Action("PJSIPRegister"),
140 ts_AMI_Field_ActionId(action_id),
141 ts_AMI_Field_Registration(registration)
142};
143template (present) AMI_Msg
144tr_AMI_Action_PJSIPRegister(template (present) charstring registration := ?,
145 template (present) charstring action_id := ?) := {
146 tr_AMI_Field_Action("PJSIPRegister"),
147 tr_AMI_Field_ActionId(action_id),
148 tr_AMI_Field_Registration(registration)
149};
150
151/*
152 * RESPONSES
153 */
154
155/* Response: Success
156 */
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200157template (present) AMI_Msg
158tr_AMI_Response_Success := superset(
159 tr_AMI_Field_ResponseSuccess
160);
161
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200162/* Response: Success
163 * ActionId: <value>
164 */
165template (present) AMI_Msg
166tr_AMI_Response_Success_ActionId(template (present) charstring action_id := ?) := superset(
167 tr_AMI_Field_ResponseSuccess,
168 tr_AMI_Field_ActionId(action_id)
169);
170
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200171
172/***********************
173 * Adapter:
174 ***********************/
175
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200176type record AMI_Adapter_Parameters {
177 charstring remote_host,
178 IPL4asp_Types.PortNumber remote_port,
179 charstring local_host,
180 IPL4asp_Types.PortNumber local_port,
181 charstring welcome_str
182}
183
184const AMI_Adapter_Parameters c_default_AMI_Adapter_pars := {
185 remote_host := "127.0.0.1",
186 remote_port := 5038,
187 local_host := "0.0.0.0",
188 local_port := 0,
189 welcome_str := "Asterisk Call Manager/9.0.0\r\n"
190};
191
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200192type port AMI_Msg_PT message {
193 inout AMI_Msg;
194} with { extension "internal" };
195
196type component AMI_Adapter_CT {
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200197 port IPL4asp_PT IPL4;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200198 port AMI_Msg_PT CLIENT;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200199 var AMI_Adapter_Parameters g_pars;
200
201 /* Connection identifier of the client itself */
202 var IPL4asp_Types.ConnectionId g_self_conn_id := -1;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200203}
204
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200205/* Function to use to connect as client to a remote IPA Server */
206private function f_AMI_Adapter_connect() runs on AMI_Adapter_CT {
207 var IPL4asp_Types.Result res;
208 map(self:IPL4, system:IPL4);
209 res := IPL4asp_PortType.f_IPL4_connect(IPL4, g_pars.remote_host, g_pars.remote_port,
210 g_pars.local_host, g_pars.local_port, 0, { tcp:={} });
211 if (not ispresent(res.connId)) {
212 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
213 log2str("Could not connect AMI socket from ", g_pars.local_host, " port ",
214 g_pars.local_port, " to ", g_pars.remote_host, " port ", g_pars.remote_port,
215 "; check your configuration"));
216 }
217 g_self_conn_id := res.connId;
218 log("AMI connected, ConnId=", g_self_conn_id)
219}
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200220
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200221private function f_ASP_RecvFrom_msg_to_charstring(ASP_RecvFrom rx_rf) return charstring {
222 return oct2char(rx_rf.msg);
223}
224
225/* Function to use to connect as client to a remote IPA Server */
226private function f_AMI_Adapter_wait_rx_welcome_str() runs on AMI_Adapter_CT {
227 var ASP_RecvFrom rx_rf;
228 var charstring rx_str;
229 timer Twelcome := 3.0;
230
231 Twelcome.start;
232 alt {
233 [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf {
234 rx_str := f_ASP_RecvFrom_msg_to_charstring(rx_rf);
235 if (g_pars.welcome_str != rx_str) {
236 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
237 log2str("AMI Welcome message mismatch: '", rx_str,
238 "' vs exp '", g_pars.welcome_str, "'"));
239 }
240 }
241 [] Twelcome.timeout {
242 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
243 log2str("AMI Welcome timeout"));
244 }
245 }
246 Twelcome.stop;
247 log("AMI Welcome message received: '", rx_str, "'");
248}
249
250private function dec_AMI_Msg_ext(charstring txt) return AMI_Msg {
251 log("AMI dec: '", txt, "'");
252 /* TEXT Enc/dec is not happy with empty values, workaround it: */
253 var charstring patched_txt := f_str_replace(txt, "Challenge: \r\n", "");
254 patched_txt := f_str_replace(patched_txt, "AccountCode: \r\n", "");
255 patched_txt := f_str_replace(patched_txt, "Value: \r\n", "");
256 patched_txt := f_str_replace(patched_txt, "DestExten: \r\n", "");
257 patched_txt := f_str_replace(patched_txt, "Exten: \r\n", "");
258 patched_txt := f_str_replace(patched_txt, "Extension: \r\n", "");
259
260 /* "AppData" field sometimes has a value containing separator ": ", which makes
261 * TEXT dec not happy. Workaround it for now by removing the whole field line:
262 * "AppData: 5,0502: Call pjsip endpoint from 0501\r\n"
263 */
264 var integer pos := f_strstr(patched_txt, "AppData: ", 0);
265 if (pos >= 0) {
266 var integer pos_end := f_strstr(patched_txt, "\r\n", pos) + lengthof("\r\n");
267 var charstring to_remove := substr(patched_txt, pos, pos_end - pos);
268 patched_txt := f_str_replace(patched_txt, to_remove, "");
269 }
270
271 log("AMI patched dec: '", patched_txt, "'");
272 return dec_AMI_Msg(patched_txt);
273}
274
275function f_AMI_Adapter_main(AMI_Adapter_Parameters pars := c_default_AMI_Adapter_pars)
276 runs on AMI_Adapter_CT {
277 var AMI_Msg msg;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200278 var charstring rx, buf := "";
279 var integer fd;
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200280 var ASP_RecvFrom rx_rf;
281 var ASP_Event rx_ev;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200282
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200283 g_pars := pars;
284
285 f_AMI_Adapter_connect();
286
287 f_AMI_Adapter_wait_rx_welcome_str();
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200288
289 while (true) {
290
291 alt {
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200292 [] IPL4.receive(ASP_RecvFrom:?) -> value rx_rf {
293 var charstring rx_str := f_ASP_RecvFrom_msg_to_charstring(rx_rf);
294 log("AMI rx: '", rx_str, "'");
295 buf := buf & rx_str;
296 log("AMI buf: '", buf, "'");
297
298 /* If several messages come together */
299 var boolean last_is_complete := f_str_endswith(buf, "\r\n\r\n");
300 var Misc_Helpers.ro_charstring msgs := f_str_split(buf, "\r\n\r\n");
301 log("AMI split: ", msgs);
302 if (lengthof(msgs) > 0) {
303 for (var integer i := 0; i < lengthof(msgs) - 1; i := i + 1) {
304 var charstring txt := msgs[i] & "\r\n";
305 msg := dec_AMI_Msg_ext(txt);
306 CLIENT.send(msg);
307 }
308 if (last_is_complete) {
309 var charstring txt := msgs[lengthof(msgs) - 1] & "\r\n";
310 msg := dec_AMI_Msg_ext(txt);
311 CLIENT.send(msg);
312 buf := "";
313 } else {
314 buf := msgs[lengthof(msgs) - 1];
315 }
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200316 }
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200317 log("AMI remain buf: '", buf, "'");
318 }
319 [] IPL4.receive(ASP_ConnId_ReadyToRelease:?) {
320 }
321
322 [] IPL4.receive(ASP_Event:?) -> value rx_ev {
323 log("Rx AMI ASP_Event: ", rx_ev);
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200324 }
325 [] CLIENT.receive(AMI_Msg:?) -> value msg {
326 /* 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 +0200327 var charstring tx_txt := enc_AMI_Msg(msg) & "\r\n";
328
329 var ASP_SendTo tx := {
330 connId := g_self_conn_id,
331 remName := g_pars.remote_host,
332 remPort := g_pars.remote_port,
333 proto := { tcp := {} },
334 msg := char2oct(tx_txt)
335 };
336 IPL4.send(tx);
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200337 }
338 }
339 }
340}
341
342
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200343/*
344 * Functions:
345 */
346
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200347/* Generate a random "ActionId" value: */
348function f_gen_action_id() return charstring {
349 return hex2str(f_rnd_hexstring(16));
350}
351
352function f_ami_msg_find(AMI_Msg msg,
353 template (present) charstring key := ?)
354return template (omit) AMI_Field {
355 var integer i;
356
357 for (i := 0; i < lengthof(msg); i := i + 1) {
358 if (not ispresent(msg[i])) {
359 continue;
360 }
361 if (match(msg[i].key, key)) {
362 return msg[i];
363 }
364 }
365 return omit;
366}
367
368function f_ami_msg_find_or_fail(AMI_Msg msg,
369 template (present) charstring key := ?)
370return AMI_Field {
371 var template (omit) AMI_Field field;
372 field := f_ami_msg_find(msg, key);
373 if (istemplatekind(field, "omit")) {
374 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
375 log2str("Key ", key, " not found in ", msg));
376 }
377 return valueof(field);
378}
379
380function f_ami_msg_get_value(AMI_Msg msg,
381 template (present) charstring key := ?)
382return template (omit) charstring {
383 var template (omit) AMI_Field field;
384 field := f_ami_msg_find(msg, key);
385 if (istemplatekind(field, "omit")) {
386 return omit;
387 }
388 return field.val;
389}
390
391function f_ami_msg_get_value_or_fail(AMI_Msg msg,
392 template (present) charstring key := ?)
393return template charstring {
394 var AMI_Field field;
395 field := f_ami_msg_find_or_fail(msg, key);
396 return field.val;
397}
398
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200399function f_ami_transceive_ret(AMI_Msg_PT pt, template (value) AMI_Msg tx_msg, float rx_timeout := 10.0) return AMI_Msg {
400 var AMI_Msg rx_msg;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200401 timer T;
402
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200403 T.start(rx_timeout);
404 pt.send(tx_msg);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200405 alt {
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200406 [] pt.receive(AMI_Msg:?) -> value rx_msg;
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200407 [] T.timeout {
408 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200409 log2str("AMI Response timeout: ", tx_msg));
410 }
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200411 }
412 T.stop;
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200413 return rx_msg;
414
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200415}
416
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200417private altstep as_ami_rx_fail(AMI_Msg_PT pt, template AMI_Msg exp_msg := *)
418{
419 var AMI_Msg msg;
420 [] pt.receive(AMI_Msg:?) -> value msg {
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200421 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200422 log2str("Received unexpected AMI message := ", msg, "\nvs exp := ", exp_msg));
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200423 }
424}
425
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200426altstep as_ami_expect_msg(AMI_Msg_PT pt, template (present) AMI_Msg msg_expect, boolean fail_others := true)
427{
428 [] pt.receive(msg_expect);
429 [fail_others] as_ami_rx_fail(pt, msg_expect);
430}
431
432function f_ami_transceive_match(AMI_Msg_PT pt,
433 template (value) AMI_Msg tx_msg,
434 template (present) AMI_Msg exp_ret := ?,
435 boolean fail_others := true,
436 float rx_timeout := 10.0) return AMI_Msg {
437 var AMI_Msg rx_msg;
438 timer T;
439
440 T.start(rx_timeout);
441 pt.send(tx_msg);
442 alt {
443 [] pt.receive(exp_ret) -> value rx_msg;
444 [not fail_others] pt.receive(AMI_Msg:?) -> value rx_msg {
445 log("AMI: Ignoring Rx msg ", rx_msg);
446 repeat;
447 }
448 [fail_others] as_ami_rx_fail(pt, exp_ret);
449 [] T.timeout {
450 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
451 log2str("AMI Response timeout: ", tx_msg));
452 }
453 }
454 T.stop;
455 return rx_msg;
456}
457
458function f_ami_transceive_match_response_success(AMI_Msg_PT pt,
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200459 template (value) AMI_Msg tx_msg) {
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200460 var template (present) AMI_Msg exp_resp;
461 var template (omit) charstring action_id := f_ami_msg_get_value(valueof(tx_msg), AMI_FIELD_ACTION_ID);
462 if (isvalue(action_id)) {
463 exp_resp := tr_AMI_Response_Success_ActionId(action_id);
464 } else {
465 exp_resp := tr_AMI_Response_Success;
466 }
467 f_ami_transceive_match(pt, tx_msg, exp_resp);
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200468}
469
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200470function f_ami_action_login(AMI_Msg_PT pt, charstring username, charstring secret) {
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200471 f_ami_transceive_match_response_success(pt, ts_AMI_Action_Login(username, secret));
472}
473
Pau Espin Pedrolbcb4e822024-04-26 20:16:47 +0200474function f_ami_action_PJSIPRegister(AMI_Msg_PT pt, charstring register) {
Pau Espin Pedrol4362dbd2024-04-26 19:31:27 +0200475 var charstring reg_action_id := f_gen_action_id();
476 f_ami_transceive_match_response_success(pt, ts_AMI_Action_PJSIPRegister(register, reg_action_id));
477}
478
Pau Espin Pedrol01f1df82024-05-08 16:55:55 +0200479private function f_ami_selftest_decode(charstring txt) {
480 log("Text to decode: '", txt, "'");
481 var AMI_Msg msg := dec_AMI_Msg(txt);
482 log("AMI_Msg decoded: ", msg);
483}
484
485function f_ami_selftest() {
486 f_ami_selftest_decode("AppData: 5,0502: Call pjsip endpoint from 0501\r\n");
487}
488
Pau Espin Pedrol54b614a2024-04-17 18:58:36 +0200489}