source: trunk/FACT++/src/pfminictrl.cc@ 20115

Last change on this file since 20115 was 18976, checked in by tbretz, 7 years ago
Replaced an 8-bit ascii charcter by its unicode representation.
File size: 12.2 KB
Line 
1#include "FACT.h"
2#include "Dim.h"
3#include "Event.h"
4#include "StateMachineDim.h"
5#include "StateMachineAsio.h"
6#include "Connection.h"
7#include "LocalControl.h"
8#include "Configuration.h"
9#include "Console.h"
10
11#include "tools.h"
12
13#include "HeadersPFmini.h"
14
15namespace ba = boost::asio;
16namespace bs = boost::system;
17namespace dummy = ba::placeholders;
18
19using namespace std;
20
21class ConnectionPFmini : public Connection
22{
23protected:
24 virtual void Update(const PFmini::Data &)
25 {
26 }
27
28private:
29 bool fIsVerbose;
30 uint16_t fInterval;
31
32 bool fReceived;
33
34 int fState;
35
36 vector<int16_t> fBuffer;
37
38 void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int /*type*/)
39 {
40 // Do not schedule a new read if the connection failed.
41 if (bytes_received==0 || (err && err!=ba::error::eof))
42 {
43 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
44 // 125: Operation canceled
45 if (err && err!=ba::error::eof && // Connection closed by remote host
46 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
47 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
48 {
49 ostringstream str;
50 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
51 Error(str);
52 }
53 PostClose(false);
54 return;
55 }
56
57 const uint16_t chk0 = Tools::Fletcher16(fBuffer.data(), 2);
58 const uint16_t chk1 = uint16_t(fBuffer[2]);
59
60 if (chk0!=chk1)
61 {
62 ostringstream out;
63 out << "Checksum error (";
64 out << hex << setfill('0');
65 out << setw(4) << fBuffer[0] << ":";
66 out << setw(4) << fBuffer[1] << "|";
67 out << setw(4) << fBuffer[2] << "!=";
68 out << setw(4) << chk1 << ")";
69
70 Error(out);
71
72 PostClose(false);
73
74 return;
75 }
76
77 PFmini::Data data;
78 data.hum = 110*fBuffer[0]/1024.;
79 data.temp = 110*fBuffer[1]/1024.-20;
80
81 Update(data);
82
83 ostringstream msg;
84 msg << fixed << setprecision(1) << "H=" << data.hum << "% T=" << data.temp << "\u00b0C";
85 Message(msg);
86
87 fState = PFmini::State::kReceiving;
88 fReceived = true;
89 }
90
91 boost::asio::deadline_timer fKeepAlive;
92
93 void HandleRequest(const bs::error_code &error)
94 {
95 // 125: Operation canceled (bs::error_code(125, bs::system_category))
96 if (error && error!=ba::error::basic_errors::operation_aborted)
97 {
98 ostringstream str;
99 str << "Timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
100 Error(str);
101
102 PostClose(false);
103 return;
104 }
105
106 // Check whether the deadline has passed. We compare the deadline
107 // against the current time since a new asynchronous operation
108 // may have moved the deadline before this actor had a chance
109 // to run.
110 if (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now())
111 return;
112
113 // Re-open connection
114 PostClose(true);
115 }
116
117 void HandleReadTimeout(const bs::error_code &error)
118 {
119 // 125: Operation canceled (bs::error_code(125, bs::system_category))
120 if (error && error!=ba::error::basic_errors::operation_aborted)
121 {
122 ostringstream str;
123 str << "Read timeout of " << URL() << " timed out: " << error.message() << " (" << error << ")";// << endl;
124 Error(str);
125
126 PostClose(false);
127 return;
128 }
129
130 if (!fReceived)
131 PostClose(false);
132 }
133
134 void Request()
135 {
136 fReceived = false;
137
138 string cmd = "GET / HTTP/1.1\r\n\r\n";
139 PostMessage(cmd);
140
141 fBuffer.resize(6);
142 AsyncRead(ba::buffer(fBuffer));
143 AsyncWait(fInTimeout, 3000, &Connection::HandleReadTimeout);
144
145 fKeepAlive.expires_from_now(boost::posix_time::seconds(fInterval));
146 fKeepAlive.async_wait(boost::bind(&ConnectionPFmini::HandleRequest,
147 this, dummy::error));
148 }
149
150 // This is called when a connection was established
151 void ConnectionEstablished()
152 {
153 // Keep state kReceiving
154 if (fState<PFmini::State::kConnected)
155 fState = PFmini::State::kConnected;
156
157 Request();
158 }
159
160public:
161 static const uint16_t kMaxAddr;
162
163public:
164 ConnectionPFmini(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
165 fIsVerbose(true), fKeepAlive(ioservice)
166 {
167 SetLogStream(&imp);
168 }
169
170 void SetVerbose(bool b)
171 {
172 fIsVerbose = b;
173 Connection::SetVerbose(b);
174 }
175
176 void SetInterval(uint16_t i)
177 {
178 fInterval = i;
179 }
180
181 int GetState() const
182 {
183 if (!is_open())
184 return PFmini::State::kDisconnected;
185
186 return fState;
187 }
188};
189
190const uint16_t ConnectionPFmini::kMaxAddr = 0xfff;
191
192// ------------------------------------------------------------------------
193
194#include "DimDescriptionService.h"
195
196class ConnectionDimWeather : public ConnectionPFmini
197{
198private:
199 DimDescribedService fDim;
200
201public:
202 ConnectionDimWeather(ba::io_service& ioservice, MessageImp &imp) :
203 ConnectionPFmini(ioservice, imp),
204 fDim("PFMINI_CONTROL/DATA", "F:1;F:1",
205 "Humidity and temperature as read out from the PFmini arduino"
206 "|Humidity[%]:Measures humidity"
207 "|Temperature[deg]:Measured temperature")
208 {
209 }
210
211 void Update(const PFmini::Data &data)
212 {
213 fDim.Update(data);
214 }
215};
216
217// ------------------------------------------------------------------------
218
219template <class T, class S>
220class StateMachinePFminiControl : public StateMachineAsio<T>
221{
222private:
223 S fPFmini;
224 Time fLastCommand;
225
226 bool CheckEventSize(size_t has, const char *name, size_t size)
227 {
228 if (has==size)
229 return true;
230
231 ostringstream msg;
232 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
233 T::Fatal(msg);
234 return false;
235 }
236
237 int Disconnect()
238 {
239 // Close all connections
240 fPFmini.PostClose(false);
241
242 return T::GetCurrentState();
243 }
244
245 int Reconnect(const EventImp &evt)
246 {
247 // Close all connections to supress the warning in SetEndpoint
248 fPFmini.PostClose(false);
249
250 // Now wait until all connection have been closed and
251 // all pending handlers have been processed
252 ba::io_service::poll();
253
254 if (evt.GetBool())
255 fPFmini.SetEndpoint(evt.GetString());
256
257 // Now we can reopen the connection
258 fPFmini.PostClose(true);
259
260 return T::GetCurrentState();
261 }
262
263 int SetVerbosity(const EventImp &evt)
264 {
265 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
266 return T::kSM_FatalError;
267
268 fPFmini.SetVerbose(evt.GetBool());
269
270 return T::GetCurrentState();
271 }
272
273 int Execute()
274 {
275 return fPFmini.GetState();
276 }
277
278
279public:
280 StateMachinePFminiControl(ostream &out=cout) :
281 StateMachineAsio<T>(out, "PFMINI_CONTROL"), fPFmini(*this, *this)
282 {
283 // State names
284 T::AddStateName(PFmini::State::kDisconnected, "Disconnected",
285 "No connection to web-server could be established recently");
286
287 T::AddStateName(PFmini::State::kConnected, "Connected",
288 "Connection established, but status still not known");
289
290 T::AddStateName(PFmini::State::kReceiving, "Receiving",
291 "Connection established, receiving reports");
292
293 // Commands
294 // Verbosity commands
295 T::AddEvent("SET_VERBOSE", "B")
296 (bind(&StateMachinePFminiControl::SetVerbosity, this, placeholders::_1))
297 ("set verbosity state"
298 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
299
300 // Conenction commands
301 T::AddEvent("DISCONNECT")
302 (bind(&StateMachinePFminiControl::Disconnect, this))
303 ("disconnect from ethernet");
304
305 T::AddEvent("RECONNECT", "O")
306 (bind(&StateMachinePFminiControl::Reconnect, this, placeholders::_1))
307 ("(Re)connect ethernet connection to PFmini, a new address can be given"
308 "|[host][string]:new ethernet address in the form <host:port>");
309
310 }
311
312 int EvalOptions(Configuration &conf)
313 {
314 fPFmini.SetVerbose(!conf.Get<bool>("quiet"));
315 fPFmini.SetDebugTx(conf.Get<bool>("debug-tx"));
316 fPFmini.SetEndpoint(conf.Get<string>("addr"));
317 fPFmini.SetInterval(conf.Get<uint16_t>("interval"));
318 fPFmini.StartConnect();
319
320 return -1;
321 }
322};
323
324// ------------------------------------------------------------------------
325
326#include "Main.h"
327
328
329template<class T, class S, class R>
330int RunShell(Configuration &conf)
331{
332 return Main::execute<T, StateMachinePFminiControl<S, R>>(conf);
333}
334
335void SetupConfiguration(Configuration &conf)
336{
337 po::options_description control("PFmini control");
338 control.add_options()
339 ("no-dim,d", po_switch(), "Disable dim services")
340 ("addr,a", var<string>("10.0.130.140:80"), "Network address of the lid controling Arduino including port")
341 ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
342 ("debug-tx", po_bool(), "Enable debugging of ethernet transmission.")
343 ("interval", var<uint16_t>(15), "Interval in seconds at which a report is requested.")
344 ;
345
346 conf.AddOptions(control);
347}
348
349/*
350 Extract usage clause(s) [if any] for SYNOPSIS.
351 Translators: "Usage" and "or" here are patterns (regular expressions) which
352 are used to match the usage synopsis in program output. An example from cp
353 (GNU coreutils) which contains both strings:
354 Usage: cp [OPTION]... [-T] SOURCE DEST
355 or: cp [OPTION]... SOURCE... DIRECTORY
356 or: cp [OPTION]... -t DIRECTORY SOURCE...
357 */
358void PrintUsage()
359{
360 cout <<
361 "The pfminictrl is an interface to the PFmini arduino.\n"
362 "\n"
363 "The default is that the program is started without user intercation. "
364 "All actions are supposed to arrive as DimCommands. Using the -c "
365 "option, a local shell can be initialized. With h or help a short "
366 "help message about the usuage can be brought to the screen.\n"
367 "\n"
368 "Usage: pfminictrl [-c type] [OPTIONS]\n"
369 " or: pfminictrl [OPTIONS]\n";
370 cout << endl;
371}
372
373void PrintHelp()
374{
375// Main::PrintHelp<StateMachineFTM<StateMachine, ConnectionFTM>>();
376
377 /* Additional help text which is printed after the configuration
378 options goes here */
379
380 /*
381 cout << "bla bla bla" << endl << endl;
382 cout << endl;
383 cout << "Environment:" << endl;
384 cout << "environment" << endl;
385 cout << endl;
386 cout << "Examples:" << endl;
387 cout << "test exam" << endl;
388 cout << endl;
389 cout << "Files:" << endl;
390 cout << "files" << endl;
391 cout << endl;
392 */
393}
394
395int main(int argc, const char* argv[])
396{
397 Configuration conf(argv[0]);
398 conf.SetPrintUsage(PrintUsage);
399 Main::SetupConfiguration(conf);
400 SetupConfiguration(conf);
401
402 if (!conf.DoParse(argc, argv, PrintHelp))
403 return 127;
404
405 // No console access at all
406 if (!conf.Has("console"))
407 {
408 if (conf.Get<bool>("no-dim"))
409 return RunShell<LocalStream, StateMachine, ConnectionPFmini>(conf);
410 else
411 return RunShell<LocalStream, StateMachineDim, ConnectionDimWeather>(conf);
412 }
413 // Cosole access w/ and w/o Dim
414 if (conf.Get<bool>("no-dim"))
415 {
416 if (conf.Get<int>("console")==0)
417 return RunShell<LocalShell, StateMachine, ConnectionPFmini>(conf);
418 else
419 return RunShell<LocalConsole, StateMachine, ConnectionPFmini>(conf);
420 }
421 else
422 {
423 if (conf.Get<int>("console")==0)
424 return RunShell<LocalShell, StateMachineDim, ConnectionDimWeather>(conf);
425 else
426 return RunShell<LocalConsole, StateMachineDim, ConnectionDimWeather>(conf);
427 }
428
429 return 0;
430}
Note: See TracBrowser for help on using the repository browser.