summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.cpp380
1 files changed, 316 insertions, 64 deletions
diff --git a/src/app.cpp b/src/app.cpp
index c6ec00a..fb7659d 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -1,99 +1,351 @@
1// Standard C++ 1// Standard C++
2#include <array> 2#include <array>
3#include <iomanip> 3#include <cstdlib>
4#include <functional>
4#include <iostream> 5#include <iostream>
6#include <map>
7#include <span>
5#include <string> 8#include <string>
6#include <thread>
7 9
8// Asio 10// Asio
9#include <asio.hpp> 11#include <asio.hpp>
10 12
13#define SERIAL_PORT "/dev/cuaU0"
14
11using namespace std; 15using namespace std;
12 16
17namespace {
18
19bool
20connect(asio::serial_port& port)
21{
22 port.open(SERIAL_PORT);
23 if ( !port.is_open() ) {
24 return false;
25 }
26
27 port.set_option(asio::serial_port::baud_rate(57600));
28 port.set_option(asio::serial_port::parity(asio::serial_port::parity::none));
29 port.set_option(asio::serial_port::character_size(8));
30 port.set_option(asio::serial_port::stop_bits(asio::serial_port::stop_bits::one));
31 port.set_option(asio::serial_port::flow_control(asio::serial_port::flow_control::none));
32
33 return true;
34}
35
36std::vector<uint8_t>
37read_until(asio::serial_port& port, asio::io_context& context, uint8_t delimiter, std::chrono::milliseconds timeout)
38{
39 std::vector<uint8_t> result;
40
41 asio::steady_timer timer(context);
42 bool finished = false;
43
44 array<uint8_t, 1> byte{};
45
46 std::function<void()> startRead;
47
48 startRead = [&]() {
49 port.async_read_some(
50 asio::buffer(byte),
51 [&](std::error_code error, std::size_t /* n */) {
52 if ( finished ) {
53 return;
54 }
55
56 if ( error ) {
57 finished = true;
58 return;
59 }
60
61 result.push_back(byte[0]);
62
63 if ( byte[0] == delimiter ) {
64 finished = true;
65 timer.cancel();
66 return;
67 }
68
69 startRead();
70 });
71 };
72
73 timer.expires_after(timeout);
74
75 timer.async_wait(
76 [&](std::error_code error) {
77 if ( !error && !finished ) {
78 finished = true;
79 port.cancel();
80 }
81 });
82
83 startRead();
84
85 while ( !finished ) {
86 context.run_one();
87 }
88
89 context.restart();
90
91 return result;
92}
93
94void
95print_hex(string_view message, span<const unsigned char> data)
96{
97 if ( data.empty() ) {
98 return;
99 }
100
101 cout << message;
102 for ( const auto& chr: data ) {
103 array<char, 4> hex{};
104 ::snprintf(hex.data(), hex.size(), " %02X", chr);
105 cout << hex.data();
106 }
107 cout << '\n';
108}
109
110bool
111sync(asio::serial_port& port, asio::io_context& context)
112{
113 write(port, asio::buffer("<HEARTBEAT0>>"));
114
115 this_thread::sleep_for(100ms);
116
117 constexpr unsigned char delimiter = 0xAA;
118 constexpr auto timeout = 500ms;
119
120 write(port, asio::buffer("<GETDATETIME>>"));
121 auto data = read_until(port, context, delimiter, timeout);
122
123 print_hex("Data:", data);
124
125 return !data.empty() && data.back() == delimiter;
126}
127
128void
129get_serial(asio::serial_port& port)
130{
131 constexpr auto response_size = 7;
132 array<unsigned char, response_size> buffer{};
133
134 write(port, asio::buffer("<GETSERIAL>>"));
135 asio::read(port, asio::buffer(buffer, buffer.size()));
136
137 print_hex("Serial:", buffer);
138}
139
140void
141get_version(asio::serial_port& port)
142{
143 constexpr auto response_size = 18;
144 array<char, response_size> buffer{};
145
146 write(port, asio::buffer("<GETVER>>"));
147 asio::read(port, asio::buffer(buffer, buffer.size()));
148
149 cout << "Version: ";
150 cout.write(buffer.data(), buffer.size());
151 cout << '\n';
152}
153
154void
155get_datetime(asio::serial_port& port)
156{
157 constexpr auto response_size = 7;
158 array<unsigned char, response_size> buffer{};
159
160 write(port, asio::buffer("<GETDATETIME>>"));
161 asio::read(port, asio::buffer(buffer, buffer.size()));
162
163 if ( buffer.back() != 0xAA ) {
164 print_hex("Ungültige Antwort:", buffer);
165 return;
166 }
167
168 unsigned int year = 2000 + buffer.at(0);
169 unsigned int month = buffer.at(1);
170 unsigned int day = buffer.at(2);
171 unsigned int hour = buffer.at(3);
172 unsigned int minute = buffer.at(4);
173 unsigned int second = buffer.at(5);
174
175 cout << "Datum: "
176 << year << '-'
177 << month << '-'
178 << day << ':'
179 << hour << '-'
180 << minute << '-'
181 << second << '\n';
182}
183
13int 184int
14main() 185run()
15{ 186{
16 asio::io_context context; 187 using namespace asio;
17 188
18 try { 189 io_context context;
19 const auto baudrate = 57600; 190 serial_port port(context);
20 const auto databits = 8; 191 jthread thread;
192
193 enum class State : int8_t {
194 START,
195 CONNECT,
196 CONNECT_ERROR,
197 SYNC,
198 SYNC_ERROR,
199 SYNCED,
200 COMMAND_MODE,
201 INIT_HB,
202 WAIT_HB,
203 STOP_HB,
204 CLOSE,
205 QUIT,
206 };
21 207
22 asio::serial_port port(context); 208 for ( State state = State::START; state != State::QUIT; ) {
209 using enum State;
23 210
24 auto send = [&](string_view cmd) { 211 switch ( state ) {
25 asio::write(port, asio::buffer(cmd)); 212 case START:
26 }; 213 state = CONNECT;
214 break;
27 215
28 port.open("/dev/cuaU0"); 216 case CONNECT:
29 if ( !port.is_open() ) { 217 if ( connect(port) ) {
218 state = SYNC;
219 }
220 else {
221 state = CONNECT_ERROR;
222 }
223 break;
224
225 case CONNECT_ERROR:
30 cerr << "Kann Port nicht öffnen...\n"; 226 cerr << "Kann Port nicht öffnen...\n";
31 return EXIT_FAILURE; 227 state = QUIT;
32 } 228 break;
33 229
34 port.set_option(asio::serial_port::baud_rate(baudrate)); 230 case SYNC:
35 port.set_option(asio::serial_port::parity(asio::serial_port::parity::none)); 231 if ( sync(port, context) ) {
36 port.set_option(asio::serial_port::character_size(databits)); 232 state = SYNCED;
37 port.set_option(asio::serial_port::stop_bits(asio::serial_port::stop_bits::one)); 233 }
38 port.set_option(asio::serial_port::flow_control(asio::serial_port::flow_control::none)); 234 else {
235 state = SYNC_ERROR;
236 }
237 break;
39 238
40 send("<GETSERIAL>>"); 239 case SYNC_ERROR:
240 cerr << "Kann nicht synchronisieren\n";
241 state = QUIT;
242 break;
41 243
42 jthread thread([&context, &port]() { 244 case SYNCED:
43 array<char, 1000> buffer{}; 245 cout << "sync ...\n";
246 state = COMMAND_MODE;
247 break;
44 248
45 function<void()> readFromPort = [&]() { 249 case COMMAND_MODE: {
46 port.async_read_some(asio::buffer(buffer, buffer.size()), [&](std::error_code error, size_t length) { 250 static const map<string, function<void(serial_port&)>> callback{
47 if ( !error ) { 251 { "ser?", get_serial },
48 cout << "Daten: " << length << ": "; 252 { "ver?", get_version },
49 for ( size_t i = 0; i != length; ++i ) { 253 { "date?", get_datetime },
50 auto chr = static_cast<unsigned char>(buffer.at(i)); 254 { "poweron!", [](serial_port& port) { write(port, asio::buffer("<POWERON>>")); } },
255 { "poweroff!", [](serial_port& port) { write(port, asio::buffer("<POWEROFF>>")); } },
256 { "sync!", [&](serial_port& port) { sync(port, context); } },
257 { "quit!", [&state](serial_port&) { state = CLOSE; } },
258 { "hb!", [&state](serial_port&) { state = INIT_HB; } },
259 };
51 260
52 if ( isprint(chr) ) { 261 cout << "Command> ";
53 cout << static_cast<char>(chr);
54 }
55 else {
56 cout << '<' << hex << setw(2) << setfill('0') << static_cast<unsigned>(chr) << dec << '>';
57 }
58 }
59 cout << '\n';
60 262
61 readFromPort(); 263 string line;
264 if ( getline(cin, line) ) {
265 const auto iter = callback.find(line);
266 if ( iter != callback.cend() ) {
267 iter->second(port);
268 }
269 else {
270 if ( !line.empty() ) {
271 cerr << "unbekanntes command: " << line << '\n';
62 } 272 }
63 else { 273 }
64 if ( error != asio::error::operation_not_supported ) { 274 }
275 else {
276 state = CLOSE;
277 }
278 } break;
279
280 case INIT_HB:
281 asio::write(port, asio::buffer("<HEARTBEAT1>>"));
282
283 thread = jthread([&context, &port]() {
284 array<unsigned char, 1000> buffer{};
285
286 function<void()> readFromPort = [&]() {
287 port.async_read_some(asio::buffer(buffer, buffer.size()), [&](std::error_code error, size_t length) {
288 if ( error && error != asio::error::operation_not_supported ) {
65 cerr << "Fehler: " << error.message() << '\n'; 289 cerr << "Fehler: " << error.message() << '\n';
290 return;
66 } 291 }
67 }
68 });
69 };
70 292
71 readFromPort(); 293 print_hex("Daten:", { buffer.data(), length });
72 context.run();
73 });
74 294
75 for ( string line; getline(cin, line); ) { 295 readFromPort();
76 if ( line == "ser?" ) { 296 });
77 send("<GETSERIAL>>"); 297 };
78 } 298
79 if ( line == "ver?" ) { 299 readFromPort();
80 send("<GETVER>>"); 300
81 } 301 context.run();
82 if ( line == "date?" ) { 302 });
83 send("<GETDATETIME>>"); 303
84 } 304 state = WAIT_HB;
85 if ( line == "hb!" ) { 305 break;
86 send("<HEARTBEAT1>>");
87 }
88 306
89 if ( line == "quit" ) { 307 case WAIT_HB:
90 break; 308 for ( string line; getline(cin, line); ) {
309 cout << "Heartbeat> ";
310 if ( line == "stop" ) {
311 break;
312 }
313 if ( !line.empty() ) {
314 cerr << "unbekanntes command: " << line << '\n';
315 }
91 } 316 }
317 state = STOP_HB;
318 break;
319
320 case STOP_HB:
321 port.cancel();
322 context.stop();
323 thread.join();
324 context.restart();
325
326 state = SYNC;
327 break;
328
329 case CLOSE:
330 port.close();
331 state = QUIT;
332 break;
333
334 case QUIT:
335 break;
92 } 336 }
337 }
338
339 return EXIT_SUCCESS;
340}
341
342} // namespace
93 343
94 port.cancel(); 344int
95 context.stop(); 345main()
96 port.close(); 346{
347 try {
348 return run();
97 } 349 }
98 catch ( asio::system_error& e ) { 350 catch ( asio::system_error& e ) {
99 cerr << e.what() << '\n'; 351 cerr << e.what() << '\n';