source: trunk/FACT++/src/biastemp.cc @ 19438

Last change on this file since 19438 was 18916, checked in by tbretz, 2 years ago
Renamed DimData to Data and moved definition to header
File size: 15.1 KB
Line 
1#include <boost/array.hpp>
2#include <boost/property_tree/ptree.hpp>
3#include <boost/property_tree/json_parser.hpp>
4
5#include <string>    // std::string
6#include <algorithm> // std::transform
7#include <cctype>    // std::tolower
8
9#include "FACT.h"
10#include "Dim.h"
11#include "Event.h"
12#include "Shell.h"
13#include "StateMachineDim.h"
14#include "StateMachineAsio.h"
15#include "Connection.h"
16#include "LocalControl.h"
17#include "Configuration.h"
18#include "Console.h"
19
20#include "tools.h"
21
22#include "HeadersBiasTemp.h"
23
24namespace ba = boost::asio;
25namespace bs = boost::system;
26namespace pt = boost::property_tree;
27namespace dummy = ba::placeholders;
28
29using namespace std;
30using namespace BiasTemp;
31
32// ------------------------------------------------------------------------
33
34class ConnectionBiasTemp : public Connection
35{
36    uint16_t fInterval;
37
38    bool fIsVerbose;
39
40    string fSite;
41
42protected:
43
44    Time fLastReport;
45    Time fLastReception;
46
47    boost::asio::streambuf fBuffer;
48    string fData;
49
50    virtual void UpdateBiasTemp(const Data &)
51    {
52    }
53
54    void ProcessAnswer(string s)
55    {
56        try
57        {
58            std::stringstream ss;
59            ss << s;
60
61            pt::ptree tree;
62            pt::read_json(ss, tree);
63
64            Data data;
65            data.time = tree.get_child("timestamp").get_value<uint64_t>();
66
67            const auto &branch = tree.get_child("temperatures_deg_C");
68
69            // FIXME: Move to config file?
70            static const string id[10] =
71            {
72                "10 e5 54 2d 03 08 00 cc",  // between master and BIAS_0
73                "10 ca 24 2e 03 08 00 bb",  // between BIAS_0 and BIAS_1
74                "10 9f 51 2d 03 08 00 49",  // between BIAS_1 and BIAS_2
75                "10 b7 6d 2d 03 08 00 fb",  // between BIAS_3 and BIAS_4
76                "10 a5 f0 2d 03 08 00 95",  // between BIAS_4 and BIAS_5
77                "10 16 75 2d 03 08 00 d2",  // between BIAS_5 and BIAS_6
78                "10 50 77 2d 03 08 00 96",  // between BIAS_7 and "SPARE"
79                "10 9f 02 2e 03 08 00 1a",  // between "SPARE" and BIAS_9
80                "10 1d ce 2d 03 08 00 15",  // right of BIAS_9
81                "10 cc f3 2d 03 08 00 8e",  // far to the right, where no heat should be generated
82            };
83
84            data.avg = 0;
85            data.rms = 0;
86
87            for (int i=0; i<10; i++)
88            {
89                data.temp[i] = branch.get_child(id[i]).get_value<float>();
90                data.avg += data.temp[i];
91                data.rms += data.temp[i]*data.temp[i];
92            }
93
94            data.avg /= 10;
95            data.rms /= 10;
96
97            data.rms = sqrt(data.rms - data.avg*data.avg);
98
99            ostringstream out;
100            out << Tools::Form("T=%09d:", data.time);
101            for (int i=0; i<10; i++)
102                out << Tools::Form("%5.1f", data.temp[i]);
103
104            out << Tools::Form(" |%5.1f +-%4.1f", data.avg, data.rms);
105
106            Info(out);
107
108            UpdateBiasTemp(data);
109
110            fLastReport = Time();
111        }
112        catch (std::exception const& e)
113        {
114            Error(string("Parsing JSON failed: ")+e.what());
115        }
116    }
117
118    void HandleRead(const boost::system::error_code& err, size_t bytes_received)
119    {
120        // Do not schedule a new read if the connection failed.
121        if (bytes_received==0 || err)
122        {
123            if (err==ba::error::eof)
124            {
125                // Does the message contain a header?
126                const size_t p1 = fData.find("\r\n\r\n");
127                if (p1!=string::npos)
128                    ProcessAnswer(fData.substr(p1));
129                else
130                    Warn("Receives message lacks a header!");
131                fData = "";
132
133                PostClose(false);
134
135                return;
136            }
137
138            // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
139            // 125: Operation canceled
140            if (err && err!=ba::error::eof &&                     // Connection closed by remote host
141                err!=ba::error::basic_errors::not_connected &&    // Connection closed by remote host
142                err!=ba::error::basic_errors::operation_aborted)  // Connection closed by us
143            {
144                ostringstream str;
145                str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
146                Error(str);
147            }
148            PostClose(err!=ba::error::basic_errors::operation_aborted);
149            return;
150        }
151
152        fLastReception = Time();
153
154        istream is(&fBuffer);
155
156        string buffer;
157        if (!getline(is, buffer, '\n'))
158        {
159            Fatal("Received message does not contain \\n... closing connection.");
160            PostClose(false);
161            return;
162        }
163
164        if (fIsVerbose)
165            Out() << buffer << endl;
166
167        fData += buffer;
168        fData += '\n';
169
170        StartReadLine();
171    }
172
173    void StartReadLine()
174    {
175        async_read_until(*this, fBuffer, '\n',
176                             boost::bind(&ConnectionBiasTemp::HandleRead, this,
177                                         dummy::error, dummy::bytes_transferred));
178    }
179
180    ba::deadline_timer fKeepAlive;
181
182    void PostRequest()
183    {
184        const string cmd =
185            "GET "+fSite+" HTTP/1.1\r\n"
186            "\r\n";
187
188        PostMessage(cmd);
189    }
190
191    void Request()
192    {
193        PostRequest();
194
195        fKeepAlive.expires_from_now(boost::posix_time::seconds(fInterval/2));
196        fKeepAlive.async_wait(boost::bind(&ConnectionBiasTemp::HandleRequest,
197                                          this, dummy::error));
198    }
199
200    void HandleRequest(const bs::error_code &error)
201    {
202        // 125: Operation canceled (bs::error_code(125, bs::system_category))
203        if (error && error!=ba::error::basic_errors::operation_aborted)
204        {
205            ostringstream str;
206            str << "Write timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
207            Error(str);
208
209            PostClose(false);
210            return;
211        }
212
213        if (!is_open())
214        {
215            // For example: Here we could schedule a new accept if we
216            // would not want to allow two connections at the same time.
217            PostClose(true);
218            return;
219        }
220
221        // Check whether the deadline has passed. We compare the deadline
222        // against the current time since a new asynchronous operation
223        // may have moved the deadline before this actor had a chance
224        // to run.
225        if (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now())
226            return;
227
228        Request();
229    }
230
231
232private:
233    // This is called when a connection was established
234    void ConnectionEstablished()
235    {
236        Request();
237        StartReadLine();
238    }
239
240public:
241
242    static const uint16_t kMaxAddr;
243
244public:
245    ConnectionBiasTemp(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
246        fIsVerbose(true), fLastReport(Time::none), fLastReception(Time::none), fKeepAlive(ioservice)
247    {
248        SetLogStream(&imp);
249    }
250
251    void SetVerbose(bool b)
252    {
253        fIsVerbose = b;
254        Connection::SetVerbose(b);
255    }
256
257    void SetInterval(uint16_t i)
258    {
259        fInterval = i;
260    }
261
262    void SetSite(const string &site)
263    {
264        fSite = site;
265    }
266
267    int GetState() const
268    {
269        if (fLastReport.IsValid() && fLastReport+boost::posix_time::seconds(fInterval*2)>Time())
270            return 3;
271
272        if (fLastReception.IsValid() && fLastReception+boost::posix_time::seconds(fInterval*2)>Time())
273            return 2;
274
275        return 1;
276
277    }
278};
279
280const uint16_t ConnectionBiasTemp::kMaxAddr = 0xfff;
281
282// ------------------------------------------------------------------------
283
284#include "DimDescriptionService.h"
285
286class ConnectionDimBiasTemp : public ConnectionBiasTemp
287{
288private:
289
290    DimDescribedService fDimBiasTemp;
291
292    virtual void UpdateBiasTemp(const Data &data)
293    {
294        fDimBiasTemp.setData(&data, sizeof(Data));
295        fDimBiasTemp.Update();
296    }
297
298public:
299    ConnectionDimBiasTemp(ba::io_service& ioservice, MessageImp &imp) :
300        ConnectionBiasTemp(ioservice, imp),
301        fDimBiasTemp("BIAS_TEMP/DATA", "X:1;F:10;D:1;D:1",
302                     "|time[s]:Seconds since device start"
303                     "|T[degC]:Temperature"
304                     "|Tavg[degC]:Average temperature"
305                     "|Trms[degC]:RMS of temperatures")
306    {
307    }
308};
309
310// ------------------------------------------------------------------------
311
312template <class T, class S>
313class StateMachineBiasTemp : public StateMachineAsio<T>
314{
315private:
316    S fBiasTemp;
317
318    bool CheckEventSize(size_t has, const char *name, size_t size)
319    {
320        if (has==size)
321            return true;
322
323        ostringstream msg;
324        msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
325        T::Fatal(msg);
326        return false;
327    }
328
329    int SetVerbosity(const EventImp &evt)
330    {
331        if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
332            return T::kSM_FatalError;
333
334        fBiasTemp.SetVerbose(evt.GetBool());
335
336        return T::GetCurrentState();
337    }
338/*
339    int Disconnect()
340    {
341        // Close all connections
342        fBiasTemp.PostClose(false);
343
344        return T::GetCurrentState();
345    }
346
347    int Reconnect(const EventImp &evt)
348    {
349        // Close all connections to supress the warning in SetEndpoint
350        fBiasTemp.PostClose(false);
351
352        // Now wait until all connection have been closed and
353        // all pending handlers have been processed
354        ba::io_service::poll();
355
356        if (evt.GetBool())
357            fBiasTemp.SetEndpoint(evt.GetString());
358
359        // Now we can reopen the connection
360        fBiasTemp.PostClose(true);
361
362        return T::GetCurrentState();
363    }
364*/
365    int Execute()
366    {
367        return fBiasTemp.GetState();
368    }
369
370public:
371    StateMachineBiasTemp(ostream &out=cout) :
372        StateMachineAsio<T>(out, "BIAS_TEMP"), fBiasTemp(*this, *this)
373    {
374        // State names
375        T::AddStateName(State::kDisconnected, "NoConnection",
376                     "No connection to web-server could be established recently");
377
378        T::AddStateName(State::kConnected, "Invalid",
379                     "Connection to webserver can be established, but received data is not recent or invalid");
380
381        T::AddStateName(State::kReceiving, "Valid",
382                     "Connection to webserver can be established, receint data received");
383
384        // Verbosity commands
385        T::AddEvent("SET_VERBOSE", "B")
386            (bind(&StateMachineBiasTemp::SetVerbosity, this, placeholders::_1))
387            ("set verbosity state"
388             "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
389/*
390        // Conenction commands
391        AddEvent("DISCONNECT")
392            (bind(&StateMachineBiasTemp::Disconnect, this))
393            ("disconnect from ethernet");
394
395        AddEvent("RECONNECT", "O")
396            (bind(&StateMachineBiasTemp::Reconnect, this, placeholders::_1))
397            ("(Re)connect ethernet connection to FTM, a new address can be given"
398             "|[host][string]:new ethernet address in the form <host:port>");
399*/
400    }
401
402    int EvalOptions(Configuration &conf)
403    {
404        fBiasTemp.SetVerbose(!conf.Get<bool>("quiet"));
405        fBiasTemp.SetInterval(conf.Get<uint16_t>("interval"));
406        fBiasTemp.SetDebugTx(conf.Get<bool>("debug-tx"));
407        fBiasTemp.SetSite(conf.Get<string>("url"));
408        fBiasTemp.SetEndpoint(conf.Get<string>("addr"));
409        fBiasTemp.StartConnect();
410
411        return -1;
412    }
413};
414
415
416
417// ------------------------------------------------------------------------
418
419#include "Main.h"
420
421
422template<class T, class S, class R>
423int RunShell(Configuration &conf)
424{
425    return Main::execute<T, StateMachineBiasTemp<S, R>>(conf);
426}
427
428void SetupConfiguration(Configuration &conf)
429{
430    po::options_description control("Bias Crate temperature readout");
431    control.add_options()
432        ("no-dim,d",  po_switch(),    "Disable dim services")
433        ("addr,a",  var<string>("10.0.100.101:80"),  "Network address of the hardware")
434        ("url,u",  var<string>("/index.html"),  "File name and path to load")
435        ("quiet,q", po_bool(true),  "Disable printing contents of all received messages (except dynamic data) in clear text.")
436        ("interval,i", var<uint16_t>(15), "Interval between two updates on the server in seconds")
437        ("debug-tx", po_bool(), "Enable debugging of ethernet transmission.")
438        ;
439
440    conf.AddOptions(control);
441}
442
443/*
444 Extract usage clause(s) [if any] for SYNOPSIS.
445 Translators: "Usage" and "or" here are patterns (regular expressions) which
446 are used to match the usage synopsis in program output.  An example from cp
447 (GNU coreutils) which contains both strings:
448  Usage: cp [OPTION]... [-T] SOURCE DEST
449    or:  cp [OPTION]... SOURCE... DIRECTORY
450    or:  cp [OPTION]... -t DIRECTORY SOURCE...
451 */
452void PrintUsage()
453{
454    cout <<
455        "The biastemp is an interface to the temperature sensors in the bias crate.\n"
456        "\n"
457        "The default is that the program is started without user intercation. "
458        "All actions are supposed to arrive as DimCommands. Using the -c "
459        "option, a local shell can be initialized. With h or help a short "
460        "help message about the usuage can be brought to the screen.\n"
461        "\n"
462        "Usage: biastemp [-c type] [OPTIONS]\n"
463        "  or:  biastemp [OPTIONS]\n";
464    cout << endl;
465}
466
467void PrintHelp()
468{
469//    Main::PrintHelp<StateMachineFTM<StateMachine, ConnectionFTM>>();
470
471    /* Additional help text which is printed after the configuration
472     options goes here */
473
474    /*
475     cout << "bla bla bla" << endl << endl;
476     cout << endl;
477     cout << "Environment:" << endl;
478     cout << "environment" << endl;
479     cout << endl;
480     cout << "Examples:" << endl;
481     cout << "test exam" << endl;
482     cout << endl;
483     cout << "Files:" << endl;
484     cout << "files" << endl;
485     cout << endl;
486     */
487}
488
489int main(int argc, const char* argv[])
490{
491    Configuration conf(argv[0]);
492    conf.SetPrintUsage(PrintUsage);
493    Main::SetupConfiguration(conf);
494    SetupConfiguration(conf);
495
496    if (!conf.DoParse(argc, argv, PrintHelp))
497        return 127;
498
499    //try
500    {
501        // No console access at all
502        if (!conf.Has("console"))
503        {
504            if (conf.Get<bool>("no-dim"))
505                return RunShell<LocalStream, StateMachine, ConnectionBiasTemp>(conf);
506            else
507                return RunShell<LocalStream, StateMachineDim, ConnectionDimBiasTemp>(conf);
508        }
509        // Cosole access w/ and w/o Dim
510        if (conf.Get<bool>("no-dim"))
511        {
512            if (conf.Get<int>("console")==0)
513                return RunShell<LocalShell, StateMachine, ConnectionBiasTemp>(conf);
514            else
515                return RunShell<LocalConsole, StateMachine, ConnectionBiasTemp>(conf);
516        }
517        else
518        {
519            if (conf.Get<int>("console")==0)
520                return RunShell<LocalShell, StateMachineDim, ConnectionDimBiasTemp>(conf);
521            else
522                return RunShell<LocalConsole, StateMachineDim, ConnectionDimBiasTemp>(conf);
523        }
524    }
525    /*catch (std::exception& e)
526    {
527        cerr << "Exception: " << e.what() << endl;
528        return -1;
529    }*/
530
531    return 0;
532}
Note: See TracBrowser for help on using the repository browser.