]>
code.octet-stream.net Git - m17rt/blob - tools/m17rt-netclient/src/main.rs
1 use std
::{io
::stdin
, sync
::Arc
};
5 adapter
::StreamAdapter
,
7 link_setup
::M17Address
,
8 reflector
::{ReflectorClientConfig
, ReflectorClientTnc
, StatusHandler
},
10 use m17codec2
::{rx
::Codec2RxAdapter
, tx
::Codec2TxAdapter
};
14 #[arg(short = 's', help = "Domain or IP of reflector")]
18 default_value
= "17000",
19 help
= "Reflector listening port"
22 #[arg(short = 'c', value_parser = valid_callsign, help = "Your callsign for reflector registration and transmissions")]
24 #[arg(short = 'r', value_parser = valid_callsign, help = "Reflector designator/callsign, often starting with 'M17-'")]
25 reflector
: M17Address
,
26 #[arg(short = 'm', value_parser = valid_module, help = "Module to connect to (A-Z)")]
30 help
= "Soundcard name for microphone, otherwise system default"
32 input
: Option
<String
>,
35 help
= "Soundcard name for speaker, otherwise system default"
37 output
: Option
<String
>,
41 let args
= Args
::parse();
43 // It is current convention that mrefd requires the destination of transmissions to match the reflector.
44 // If you are connected to "M17-XXX" on module B then you must set the dst to "M17-XXX B".
45 // This requirement is likely to change but for the purposes of this test client we'll hard-code the
46 // behaviour for the time being.
47 let ref_with_mod
= format
!("{} {}", args
.reflector
, args
.module
);
48 let Ok(reflector
) = M17Address
::from_callsign(&ref_with_mod
) else {
50 "Unable to create valid destination address for reflector + callsign '{ref_with_mod}'"
52 std
::process
::exit(1);
55 let mut tx
= Codec2TxAdapter
::new(args
.callsign
.clone(), reflector
);
56 if let Some(input
) = args
.inp
ut
{
57 tx
.set_input_card(input
);
61 let mut rx
= Codec2RxAdapter
::new();
62 if let Some(output
) = args
.output
{
63 rx
.set_output_card(output
);
66 let config
= ReflectorClientConfig
{
67 hostname
: args
.hostname
,
70 local_callsign
: args
.callsign
,
72 let tnc
= ReflectorClientTnc
::new(config
, ConsoleStatusHandler
);
73 let app
= M17App
::new(tnc
);
74 app
.add_stream_adapter(ConsoleAdapter
).unwrap
();
75 app
.add_stream_adapter(tx
).unwrap
();
76 app
.add_stream_adapter(rx
).unwrap
();
79 println
!(">>> PRESS ENTER TO TOGGLE PTT <<<");
80 let mut buf
= String
::new();
83 let _
= stdin().read_line(&mut buf
);
85 println
!("PTT ON: PRESS ENTER TO END");
87 let _
= stdin().read_line(&mut buf
);
93 fn valid_module(m
: &str) -> Result
<char, String
> {
94 let m
= m
.to_ascii_uppercase();
95 if m
.len() != 1 || !m
.chars().next().unwrap
().is
_alphabet
ic
() {
96 return Err("Module must be a single letter from A to Z".to_owned());
98 Ok(m
.chars().next().unwrap
())
101 fn valid_callsign(c
: &str) -> Result
<M17Address
, String
> {
102 M17Address
::from_callsign(c
).map_err(|e
| e
.to_string())
105 struct ConsoleAdapter
;
106 impl StreamAdapter
for ConsoleAdapter
{
107 fn stream_began(&self, link_setup
: m17app
::link_setup
::LinkSetup
) {
109 "Incoming transmission begins. From: {} To: {}",
111 link_setup
.destination()
115 fn stream_data(&self, _frame_number
: u16, is_final
: bool
, _data
: Arc
<[u8; 16]>) {
117 println
!("Incoming transmission ends.");
122 struct ConsoleStatusHandler
;
123 impl StatusHandler
for ConsoleStatusHandler
{
124 fn status_changed(&mut self, status
: m17app
::reflector
::TncStatus
) {
125 println
!("Client status: {status:?}")