dgorski / app_tdd

TDD Module for Asterisk
3 stars 2 forks source link

Application to send TTY message from dialplan #1

Closed InterLinked1 closed 2 years ago

InterLinked1 commented 3 years ago

Similar to the TddTx manager application (and CLI command) you have here, would you be able to add a dialplan application to do the same thing, so that one could set up the TTY audiohook, and then immediately TX something from the dialplan?

I don't think a Receive application would make much sense, but a transmit application to the TTY would be quite handy.

dgorski commented 3 years ago

Yeah, that's a reasonable idea. Should be easily implemented as the cli and manager interfaces are already there (and use a common send function).

In the mean time you can play pre-recorded TTY tones toward the caller using Playback() - I used this a lot during testing.

You can record an incoming TTY call, or use a tool to generate the audio files - I used minimodem a lot during testing.

InterLinked1 commented 3 years ago

Yeah, that's a reasonable idea. Should be easily implemented as the cli and manager interfaces are already there (and use a common send function).

In the mean time you can play pre-recorded TTY tones toward the caller using Playback() - I used this a lot during testing.

You can record an incoming TTY call, or use a tool to generate the audio files - I used minimodem a lot during testing.

Yeah, that's actually exactly what I've been doing up until now. Trying to clean up and simplify some things in my dialplan and as soon as there is an app to send, I am going to get rid of all what I have and switch to your app_tdd. Super excited!

dgorski commented 3 years ago

I had a little time between meetings add this and merge it with some other minor changes. I have not tested extensively, but I pushed it up here for you so you can see if it works for you. It builds cleanley against 18.4.0.

InterLinked1 commented 3 years ago

I had a little time between meetings add this and merge it with some other minor changes. I have not tested extensively, but I pushed it up here for you so you can see if it works for you. It builds cleanley against 18.4.0.

Awesome, thanks!

I might be doing something wrong here, but this seems to be the right usage, yet it is returning immediately it seems:

[2021-07-23 18:18:43] -- Executing [s@tty-intercept-test:5] TddRx("Local/2911@test-route-00000003;2", "") in new stack [2021-07-23 18:18:43] -- Executing [s@tty-intercept-test:6] TddTx("Local/2911@test-route-00000003;2", "_nbr reached not in service pls ck nbr and redial SKSK") in new stack [2021-07-23 18:18:43] -- Executing [s@tty-intercept-test:7] Return("Local/2911@test-route-00000003;2", "") in new stack

dgorski commented 3 years ago

Yes, it should return immediately. TX is async, it pushes the chars into the modem output buffer and returns.

InterLinked1 commented 3 years ago

Yes, it should return immediately. TX is async, it pushes the chars into the modem output buffer and returns.

Got it... so this is like PlayTones then where you have to use Wait() or something similar, I'd assume? So how would I wait just long enough to let the TTY play and then continue? Is there a variable that would contain the right amount of time to wait, for a synchronous playback, or what would be the best way to do this? Basically, how do I make this synchronous instead of async, on demand? Something like TDDWait() which waits until the buffer is empty?

dgorski commented 3 years ago

Probably have to Wait() for the right amount of time. In this particular case you could use a long time as the SKSK indicates ot the TTY user that no other message is forthcoming (so it's safe to wait well longer than needed). 45.45 bps implies about 9 (safer to assume 8) characters persecond, so you could just do the math.

dgorski commented 3 years ago

Something like TDDWait() which waits until the buffer is empty?

Unfortunately, the TX modem doesn't produce any sort of event to say it has finished transmitting.

InterLinked1 commented 3 years ago

Something like TDDWait() which waits until the buffer is empty?

Unfortunately, the TX modem doesn't produce any sort of event to say it has finished transmitting.

Got it, ah well, even then, still way better than what's existed so far.

Added a Wait, I still don't hear anything. I even answered the channel, in case that was an issue, but still silence:

[2021-07-23 18:49:59] -- Executing [s@tty-intercept-test:6] TddRx("Local/2911@test-route-0000000f;2", "") in new stack [2021-07-23 18:49:59] -- Executing [s@tty-intercept-test:7] TddTx("Local/2911@test-route-0000000f;2", "_nbr reached not in service pls ck nbr and redial SKSK") in new stack [2021-07-23 18:49:59] -- Executing [s@tty-intercept-test:8] Wait("Local/2911@test-route-0000000f;2", "5.94059405940594059") in new stack [2021-07-23 18:50:05] -- Executing [s@tty-intercept-test:9] Return("Local/2911@test-route-0000000f;2", "") in new stack

dgorski commented 3 years ago

the channel needs to be actively transmitting, which basically means it must be bridged (playback would also work, anything that is sending audio out of the channel). If there is no audio TX then modem can't send voice frames (because it effectively uses the original voice frames as the audio transport).

dgorski commented 3 years ago

This is called out inside of the code in a few places:

/ TODO: tx really only works when audiohook is getting write frames (like from a bridge) /

InterLinked1 commented 3 years ago

the channel needs to be actively transmitting, which basically means it must be bridged (playback would also work, anything that is sending audio out of the channel). If there is no audio TX then modem can't send voice frames (because it effectively uses the original voice frames as the audio transport).

Hmm, didn't work with this either: Playback("Local/2911@test-route-00000017;2", "silence/10,noanswer")

I even dropped it into a ConfBridge, which should definitely force a media stream, no luck with that, either.

dgorski commented 3 years ago

Definitely have to answer first. This is how I test:

  1. Answer()
  2. TddRx()
  3. Dial(SIP/1004)
  4. Hangup()

Then I call from my TTY and answer at 1004 (just a generic phone) and use the cli or manager to send a message. This is why I did not create a dialplan app, because it really doesn't make a lot of sense - canned messages (like in your example above) can be sent using recordings. Dynamic messages would probably never use a dialplan app anyway. I probably should have realized that before I added this but it so simple... I should pull it back out as it will probably never actually work.

InterLinked1 commented 3 years ago

definitely have to answer first.

Yup, I tried answering explicitly as well, same thing. It sounds like this behavior is related to this bug: https://issues.asterisk.org/jira/browse/ASTERISK-24397, but in this case I am using ConfBridge, answered, and I don't hear anything, which is strange.

InterLinked1 commented 3 years ago

After much playing around with this, it does appear it works in simpler scenarios:

[2021-07-24 17:52:14] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier up (-2) [2021-07-24 17:52:14] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier down (-1) [2021-07-24 17:52:14] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 message buffer: 0 [2021-07-24 17:52:15] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier up (-2) [2021-07-24 17:52:15] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier down (-1) [2021-07-24 17:52:15] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 message buffer: 0 [2021-07-24 17:52:15] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier up (-2) [2021-07-24 17:52:15] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier down (-1) [2021-07-24 17:52:15] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 message buffer: 0 [2021-07-24 17:52:16] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier up (-2) [2021-07-24 17:52:16] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier down (-1) [2021-07-24 17:52:16] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 message buffer: 0 [2021-07-24 17:52:17] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier up (-2) [2021-07-24 17:52:17] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier down (-1) [2021-07-24 17:52:17] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 message buffer: 0 [2021-07-24 17:52:17] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier up (-2) [2021-07-24 17:52:17] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier down (-1) [2021-07-24 17:52:17] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 message buffer: 0 [2021-07-24 17:52:17] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier up (-2) [2021-07-24 17:52:17] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier down (-1) [2021-07-24 17:52:17] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 message buffer: 0 [2021-07-24 17:52:18] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier up (-2) [2021-07-24 17:52:18] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 signal status is Carrier down (-1) [2021-07-24 17:52:18] DEBUG[8029][C-00000035]: app_tdd.c:225 spandsp_log: FLOW V.18 message buffer: 0

Works like a charm, in these cases!

However, anytime ConfBridge() is involved, things break down, and playing this through a ConfBridge doesn't seem to work, even if optimization is disabled and DTMF passthru is enabled.

I tried other hacks using ChanSpy and Playback, etc. but no cigar.

I see this when it fails to work so it may be that this is a bug with audiohooks... DEBUG[9919][C-00000056]: audiohook.c:275 audiohook_read_frame_both: Read factory 0x7f6b542bcc28 and write factory 0x7f6b542bd668 both fail to provide 160 samples

InterLinked1 commented 3 years ago

Still cannot get sending of TTY to work well, but receiving (which is what I was not able to do before, and am arguably more excited about) works perfectly! AMI for that is actually a natural choice so I'm excited about this.

Only suggestion on this side is maybe rename some of the things: e.g. TddRxMsg -> TDDRxMsg

The OCD in me wants to see "Tdd" as "TDD" since it's an acronym, so it'd be nice if it was like that.

No joy on TX, but I guess I can fall back to my audio files for that. Good work, I look forward to seeing this up for review in Gerrit soon!

InterLinked1 commented 3 years ago

Actually, here is one thing I'll point out.

While it does work asynchronously, it seems to execute in "batches". That is, it waits until there is a pause before an AMI event gets dispatched. Depending on how long a person types without stopping, that could be a very long string that it receives:

e.g. 15:39:59 Event: TddRxMsg Privilege: call,all Timestamp: 1629232799.325271 SystemName: redacted Channel: redacted Message: HELLO\nWHAT IS YOUR NAME?

I could see this being problematic in some places where real-time delivery of the digits is needed, e.g. for a TRS operator relaying a TRS call.

I'm not sure if there's anything that can be done about this, looking at the code, but it's worth keeping in mind. (i.e. if it's a TDD talking to a computer, this probably isn't an issue. If it's some kind of relay/realtime thing, it doesn't really seem to work for that, since it's synchronous relatively).

dgorski commented 3 years ago

It sends any buffered (received) characters when the modem transitions to carrier-down, or when the buffer fills - whichever comes first. If the buffer fills up it will send the buffered chars and continue to buffer until the buffer is full again or the carrier transitions. The hard-coded value is 256 which is what the underlying spandsp library would do if I didn't override it's receiver. It would be easy to modify it to send each digit as it arrives but the messaging overhead would be significant, the ARI message is something like 150-200 bytes just to convey a single char.

Line 311 in the current head:

if (s->rx_msg_len >= 256)

You could change that to a smaller value to get more messages at the expense of ARI messaging overhead.

InterLinked1 commented 3 years ago

Got it, thanks! Could you make that a configurable option, e.g.

b(256) for max buffer = 256.

I think in some applications, a relatively "large" buffer like 256 is just fine - and as the default, it's fine. In certain applications, like TRS, which is a real time thing where digit by digit is required, at least every few bytes would be good so being able to customize this on a case-by-case basis would be really helpful. I don't use ARI, just AMI, so I don't think the overhead would impact me anyways.

Additionally, I was able to get TX to finally work on some other scenarios, so it's all coming together. If this option is added, it would be "production ready" for me. Thanks again!

dgorski commented 3 years ago

I said ARI but I meant AMI - ARI would be worse as those contain channel snapshots! As of now there are no options, which is on the todo list.

dgorski commented 2 years ago

Added option processing in latest which allows specification of this buffer in the dialplan.