]>
code.octet-stream.net Git - m17rt/blob - tools/m17rt-netclient/src/main.rs
1 use std
::{io
::stdin
, sync
::Arc
};
3 use clap
::{Arg
, value_parser
};
5 adapter
::StreamAdapter
,
7 link_setup
::M17Address
,
8 reflector
::{ReflectorClientConfig
, ReflectorClientTnc
, StatusHandler
},
10 use m17codec2
::{rx
::Codec2RxAdapter
, tx
::Codec2TxAdapter
};
13 let args
= clap
::Command
::new("m17rt-netclient")
19 .help("Domain or IP of reflector"),
25 .value_parser(value_parser
!(u16))
26 .default_value("17000")
27 .help("Reflector listening port"),
33 .value_parser(valid_callsign
)
35 .help("Your callsign for reflector registration and transmissions"),
41 .value_parser(valid_callsign
)
43 .help("Reflector designator/callsign, often starting with 'M17-'"),
49 .value_parser(valid_module
)
51 .help("Module to connect to (A-Z)"),
57 .help("Soundcard name for microphone, otherwise system default"),
63 .help("Soundcard name for speaker, otherwise system default"),
67 let hostname
= args
.get_one
::<String
>("hostname").unwrap
();
68 let port
= args
.get_one
::<u16>("port").unwrap
();
69 let callsign
= args
.get_one
::<M17Address
>("callsign").unwrap
();
70 let reflector
= args
.get_one
::<M17Address
>("reflector").unwrap
();
71 let module
= args
.get_one
::<char>("module").unwrap
();
72 let input
= args
.get_one
::<String
>("input");
73 let output
= args
.get_one
::<String
>("output");
75 // It is current convention that mrefd requires the destination of transmissions to match the reflector.
76 // If you are connected to "M17-XXX" on module B then you must set the dst to "M17-XXX B".
77 // This requirement is likely to change but for the purposes of this test client we'll hard-code the
78 // behaviour for the time being.
79 let ref_with_mod
= format
!("{} {}", reflector
, module
);
80 let Ok(reflector
) = M17Address
::from_callsign(&ref_with_mod
) else {
82 "Unable to create valid destination address for reflector + callsign '{ref_with_mod}'"
84 std
::process
::exit(1);
87 let mut tx
= Codec2TxAdapter
::new(callsign
.clone(), reflector
);
88 if let Some(input
) = input
{
89 tx
.set_input_card(input
);
93 let mut rx
= Codec2RxAdapter
::new();
94 if let Some(output
) = output
{
95 rx
.set_output_card(output
);
98 let config
= ReflectorClientConfig
{
99 hostname
: hostname
.clone(),
102 local_callsign
: callsign
.clone(),
104 let tnc
= ReflectorClientTnc
::new(config
, ConsoleStatusHandler
);
105 let app
= M17App
::new(tnc
);
106 app
.add_stream_adapter(ConsoleAdapter
).unwrap
();
107 app
.add_stream_adapter(tx
).unwrap
();
108 app
.add_stream_adapter(rx
).unwrap
();
109 app
.start().unwrap
();
111 println
!(">>> PRESS ENTER TO TOGGLE PTT <<<");
112 let mut buf
= String
::new();
115 let _
= stdin().read_line(&mut buf
);
117 println
!("PTT ON: PRESS ENTER TO END");
119 let _
= stdin().read_line(&mut buf
);
125 fn valid_module(m
: &str) -> Result
<char, String
> {
126 let m
= m
.to_ascii_uppercase();
127 if m
.len() != 1 || !m
.chars().next().unwrap
().is
_alphabet
ic
() {
128 return Err("Module must be a single letter from A to Z".to_owned());
130 Ok(m
.chars().next().unwrap
())
133 fn valid_callsign(c
: &str) -> Result
<M17Address
, String
> {
134 M17Address
::from_callsign(c
).map_err(|e
| e
.to_string())
137 struct ConsoleAdapter
;
138 impl StreamAdapter
for ConsoleAdapter
{
139 fn stream_began(&self, link_setup
: m17app
::link_setup
::LinkSetup
) {
141 "Incoming transmission begins. From: {} To: {}",
143 link_setup
.destination()
147 fn stream_data(&self, _frame_number
: u16, is_final
: bool
, _data
: Arc
<[u8; 16]>) {
149 println
!("Incoming transmission ends.");
154 struct ConsoleStatusHandler
;
155 impl StatusHandler
for ConsoleStatusHandler
{
156 fn status_changed(&mut self, status
: m17app
::reflector
::TncStatus
) {
157 println
!("Client status: {status:?}")