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

Last change on this file since 18189 was 18187, checked in by tbretz, 10 years ago
New program to readout the arduino with the new temperature and humidity sensor in the camera.
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 << "°C" ;
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_TEST/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_TEST"), 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.