1 #include <boost/algorithm/string.hpp> 17 namespace ba = boost::asio;
18 namespace bs = boost::system;
19 namespace dummy = ba::placeholders;
40 void HandleRead(
const boost::system::error_code& err,
size_t bytes_received)
43 if (bytes_received==0 || err)
45 if (err==ba::error::eof)
46 Warn(
"Connection closed by remote host.");
50 if (err && err!=ba::error::eof &&
51 err!=ba::error::basic_errors::not_connected &&
52 err!=ba::error::basic_errors::operation_aborted)
55 str <<
"Reading from " << URL() <<
": " << err.message() <<
" (" << err <<
")";
65 if (!getline(is, buffer,
'\n'))
67 Fatal(
"Received message does not contain \\n... closing connection.");
72 buffer = buffer.substr(0, buffer.size()-1);
76 Out() <<
Time().
GetAsStr(
"%H:%M:%S.%f") <<
"[" << buffer.size() <<
"]: " << buffer <<
"|" << endl;
84 boost::split(vec, buffer, boost::is_any_of(
","));
89 throw runtime_error(
"Unknown number of fields in received data");
92 throw runtime_error(
"Not a proper answer");
96 data.
mag = stof(vec[1]);
97 data.
freq = stol(vec[2]);
98 data.
counts = stol(vec[3]);
99 data.
period = stof(vec[4]);
100 data.
temp = stof(vec[5]);
106 catch (
const exception &e)
109 Warn(
"Parsing first message failed ["+
string(e.what())+
"]");
112 Error(
"Parsing received message failed ["+
string(e.what())+
"]");
113 Error(
"Received: "+buffer);
121 fTrigger.expires_at(fTrigger.expires_at()+boost::posix_time::milliseconds(fTimeout));
123 this, dummy::error));
125 fFirstMessage =
false;
131 if (error && error!=ba::error::basic_errors::operation_aborted)
134 str <<
"ReadTimeout of " << URL() <<
" failed: " << error.message() <<
" (" << error <<
")";
151 if (error==ba::error::basic_errors::operation_aborted)
158 if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
162 str <<
"No valid answer received from " << URL() <<
" within " << ceil(fTimeout*1.5) <<
"ms";
167 fInTimeout.expires_from_now(boost::posix_time::milliseconds(1000));
169 this, dummy::error));
176 if (error && error!=ba::error::basic_errors::operation_aborted)
179 str <<
"RequestTrigger failed of " << URL() <<
" failed: " << error.message() <<
" (" << error <<
")";
198 if (fTrigger.expires_at() > ba::deadline_timer::traits_type::now())
206 PostMessage(
string(
"rx\n"), 3);
211 async_read_until(*
this, fBuffer,
'\n',
213 dummy::error, dummy::bytes_transferred));
216 fInTimeout.expires_from_now(boost::posix_time::milliseconds(fTimeout*1.5));
218 this, dummy::error));
226 fFirstMessage =
true;
230 async_read_until(*
this, fBuffer,
'\n',
232 dummy::error, dummy::bytes_transferred));
235 fTrigger.expires_at(
Time()+boost::posix_time::milliseconds(1000));
237 this, dummy::error));
245 fIsVerbose(true), fTimeout(0), fTrigger(ioservice)
282 fDim(
"SQM_CONTROL/DATA",
"F:1;I:1;I:1;F:1;F:1",
283 "Data received from sky quality meter" 284 "|Mag[mag/arcsec^2]:Magnitude (0 means upper brightness limit)" 285 "|Freq[Hz]:Frequency of sensor" 286 "|Counts:Period of sensor (counts occur at 14.7456MHz/32)" 287 "|Period[s]:Period of sensor" 288 "|Temp[deg C]:Sensor temperature in deg C")
300 template <
class T,
class S>
312 msg << name <<
" - Received event has " << has <<
" bytes, but expected " << size <<
".";
320 fSQM.PostClose(
false);
322 return T::GetCurrentState();
328 fSQM.PostClose(
false);
332 ba::io_service::poll();
338 fSQM.PostClose(
true);
340 return T::GetCurrentState();
345 if (!CheckEventSize(evt.
GetSize(),
"SetVerbosity", 1))
346 return T::kSM_FatalError;
348 fSQM.SetVerbose(evt.
GetBool());
350 return T::GetCurrentState();
355 const string tx = cmd+
"\r\n";
356 fSQM.PostMessage(tx, tx.size());
357 return T::GetCurrentState();
367 return fSQM.GetState();
377 "No connection to Sky Quality Meter");
380 "Connection established, but no valid message received");
383 "Valid message received");
391 T::AddEvent(
"SET_VERBOSE",
"B")
393 (
"set verbosity state" 394 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
405 T::AddEvent(
"DISCONNECT")
407 (
"disconnect from ethernet");
409 T::AddEvent(
"RECONNECT",
"O")
411 (
"(Re)connect ethernet connection to SQM, a new address can be given" 412 "|[host][string]:new ethernet address in the form <host:port>");
418 fSQM.SetVerbose(!conf.
Get<
bool>(
"quiet"));
419 fSQM.SetTimeout(conf.
Get<uint16_t>(
"request-interval"));
420 fSQM.SetDebugTx(conf.
Get<
bool>(
"debug-tx"));
421 fSQM.SetEndpoint(conf.
Get<
string>(
"addr"));
433 template<
class T,
class S,
class R>
436 return Main::execute<T, StateMachineSQMControl<S, R>>(conf);
441 po::options_description control(
"SQM control");
442 control.add_options()
443 (
"no-dim,d",
po_switch(),
"Disable dim services")
444 (
"addr,a", var<string>(
"10.0.100.208:10001"),
"Network address of the lid controling Arduino including port")
445 (
"quiet,q",
po_bool(
true),
"Disable printing contents of all received messages (except dynamic data) in clear text.")
446 (
"debug-tx",
po_bool(),
"Enable debugging of ethernet transmission.")
447 (
"request-interval", var<uint16_t>(5000),
"How often to request a report [milliseconds].")
465 "The sqmctrl is an interface to the Sky Quality Meter.\n" 467 "The default is that the program is started without user intercation. " 468 "All actions are supposed to arrive as DimCommands. Using the -c " 469 "option, a local shell can be initialized. With h or help a short " 470 "help message about the usuage can be brought to the screen.\n" 472 "Usage: sqmctrl [-c type] [OPTIONS]\n" 473 " or: sqmctrl [OPTIONS]\n";
499 int main(
int argc,
const char* argv[])
510 if (!conf.
Has(
"console"))
512 if (conf.
Get<
bool>(
"no-dim"))
513 return RunShell<LocalStream, StateMachine, ConnectionSQM>(conf);
515 return RunShell<LocalStream, StateMachineDim, ConnectionDimWeather>(conf);
518 if (conf.
Get<
bool>(
"no-dim"))
520 if (conf.
Get<
int>(
"console")==0)
521 return RunShell<LocalShell, StateMachine, ConnectionSQM>(conf);
523 return RunShell<LocalConsole, StateMachine, ConnectionSQM>(conf);
527 if (conf.
Get<
int>(
"console")==0)
528 return RunShell<LocalShell, StateMachineDim, ConnectionDimWeather>(conf);
530 return RunShell<LocalConsole, StateMachineDim, ConnectionDimWeather>(conf);
void SetTimeout(uint16_t t)
A general base-class describing events issues in a state machine.
virtual void Update(const SQM::Data &)
void ConnectionEstablished()
void SetupConfiguration(Configuration &conf)
The base implementation of a distributed messaging system.
Adds some functionality to boost::posix_time::ptime for our needs.
void SetPrintUsage(const std::function< void(void)> &func)
T Get(const std::string &var)
bool CheckEventSize(size_t has, const char *name, size_t size)
po::typed_value< bool > * po_switch()
int SendCommand(const EventImp &evt)
int RunShell(Configuration &conf)
ConnectionSQM(ba::io_service &ioservice, MessageImp &imp)
std::string GetString() const
static const uint16_t kMaxAddr
StateMachineSQMControl(ostream &out=cout)
void HandleReadTimeout(const bs::error_code &error)
bool Has(const std::string &var)
void AddOptions(const po::options_description &opt, bool visible=true)
void SetupConfiguration(Configuration &conf)
boost::asio::deadline_timer fTrigger
boost::asio::streambuf fBuffer
void SetVerbose(bool b=true)
Commandline parsing, resource file parsing and database access.
void HandleRequestTrigger(const bs::error_code &error)
int Reconnect(const EventImp &evt)
int SetVerbosity(const EventImp &evt)
int Send(const string &cmd)
po::typed_value< bool > * po_bool(bool def=false)
int main(int argc, const char *argv[])
std::string GetAsStr(const char *fmt="%Y-%m-%d %H:%M:%S") const
int EvalOptions(Configuration &conf)
bool DoParse(int argc, const char **argv, const std::function< void()> &func=std::function< void()>())
ConnectionDimWeather(ba::io_service &ioservice, MessageImp &imp)
void Update(const SQM::Data &data)
virtual size_t GetSize() const
void HandleRead(const boost::system::error_code &err, size_t bytes_received)