Abort all connections in progress when device enters disconnected state.
[yandex-traffic] / traffic.cpp
1 #include <QtCore>
2 #include <QtXml>
3
4 #include "traffic.hpp"
5 #include "log.hpp"
6 #include "connection.hpp"
7
8
9 // --------------------------------------------------
10 // TrafficInfo
11 // --------------------------------------------------
12 TrafficInfo::TrafficInfo (const QDomElement& elem) throw (const QString&)
13 {
14     _valid = false;
15     _len1 = getFloatNode (elem, "length_1", 0);
16     _len2 = getFloatNode (elem, "length_2", 0);
17     _len  = getFloatNode (elem, "length", 0);
18     _ts   = getTSNode (elem, "timestamp");
19     _isotime = getStringNode (elem, "isotime");
20     _localtime = getStringNode (elem, "localtime");
21     _valid = true;
22 }
23
24
25 float TrafficInfo::getFloatNode (const QDomElement& elem, const char* node, float def)
26 {
27     QDomElement e;
28     bool ok;
29     float val;
30
31     e = elem.firstChildElement (node);
32     if (e.isNull ())
33         return def;
34     val = e.text ().toFloat (&ok);
35     if (!ok)
36         return def;
37     return val;
38 }
39
40
41 int TrafficInfo::getIntNode (const QDomElement& elem, const char* node, int def)
42 {
43     QDomElement e;
44     bool ok;
45     int val;
46
47     e = elem.firstChildElement (node);
48     if (e.isNull ())
49         return def;
50     val = e.text ().toInt (&ok);
51     if (!ok)
52         return def;
53     return val;
54 }
55
56
57 QString TrafficInfo::getStringNode (const QDomElement& elem, const char* node) throw (const QString&)
58 {
59     QDomElement e;
60     QString val;
61
62     e = elem.firstChildElement (node);
63     if (e.isNull ())
64         throw QString (QString::fromAscii (node) + " not found");
65     return e.text ();
66 }
67
68
69 QDateTime TrafficInfo::getTSNode (const QDomElement& elem, const char* node) throw (const QString&)
70 {
71     QDomElement e;
72     bool ok;
73     uint val;
74     QDateTime ts;
75
76     e = elem.firstChildElement (node);
77     if (e.isNull ())
78         throw QString (QString::fromAscii (node) + " not found");
79     val = e.text ().toUInt (&ok);
80     if (!ok)
81         throw QString (QString::fromAscii (node) + " is not a timestamp");
82     ts.setTime_t (val);
83     return ts;
84 }
85
86
87 void TrafficInfo::dump ()
88 {
89     Log::instance ()->add (QString ("TrafficInfo (%1): time = %2").arg (_valid ? "valid" : "not valid").arg (_localtime));
90 }
91
92
93 // --------------------------------------------------
94 // ExtendedTrafficInfo
95 // --------------------------------------------------
96 ExtendedTrafficInfo::ExtendedTrafficInfo (const QDomElement& elem) throw (const QString&)
97     : TrafficInfo (elem)
98 {
99     QString color;
100
101     setValid (false);
102     _level_raw = getFloatNode (elem, "level_raw", 0);
103     _level = getIntNode (elem, "level", 1);
104     _tend = getIntNode (elem, "tend", 0);
105     _hint = getStringNode (elem, "hint");
106
107     color = getStringNode (elem, "icon");
108     if (color == "green")
109         _color = Green;
110     else if (color == "yellow")
111         _color = Yellow;
112     else if (color == "red")
113         _color = Red;
114     else
115         throw "Color is unknown";
116
117     setValid (true);
118 }
119
120
121 void ExtendedTrafficInfo::dump ()
122 {
123     TrafficInfo::dump ();
124     Log::instance ()->add (QString ("ExtTrafficInfo: level = %1, hint = %2").arg (_level).arg (_hint));
125 }
126
127
128 // --------------------------------------------------
129 // Traffic
130 // --------------------------------------------------
131 Traffic::Traffic ()
132     : QObject ()
133 {
134     connect (&_fetcher, SIGNAL (done (const QByteArray&)),
135              SLOT (fetchDone (const QByteArray&)));
136     connect (ConnectionChecker::instance (), SIGNAL (connected (bool)), SLOT (connectionChanged (bool)));
137 }
138
139
140 // Perform asyncronous refresh of traffic information. If another update
141 // request is in progress, new is discarded. If update request finished
142 // successfully, updated() signal called.
143 void Traffic::update ()
144 {
145     if (_fetcher.busy ()) {
146         Log::instance ()->add ("Traffic::update: fetcher is busy");
147         return;
148     }
149
150     Log::instance ()->add ("Traffic::update: Request status download");
151     _fetcher.fetch ("http://trf.maps.yandex.net/trf/stat.xml");
152 }
153
154
155 void Traffic::fetchDone (const QByteArray& data)
156 {
157     // parse data got
158     if (parse_traffic_data (QString::fromUtf8 (data.data ()))) {
159         Log::instance ()->add ("Traffic::fetchDone: data parsed successfully");
160         updated ();
161     }
162     else
163         Log::instance ()->add ("Traffic::fetchDone: data parse error");
164 }
165
166
167 bool Traffic::parse_traffic_data (const QString& xml)
168 {
169     QDomDocument doc;
170     QDateTime new_ts;
171     QDomElement e;
172     QDomNode n;
173     bool ok;
174     QString s;
175     QMap<QString, TrafficInfo> new_info;
176     QMap<QString, ExtendedTrafficInfo> new_ext_info;
177
178     if (!doc.setContent (xml))
179         return false;
180
181     // get timestamp
182     e = doc.documentElement ();
183     if (e.isNull () || e.tagName () != "jams_stat")
184         return false;
185
186     s = e.attribute ("timestamp");
187     if (s.isNull ())
188         return false;
189
190     new_ts.setTime_t (s.toUInt (&ok));
191     if (!ok)
192         return false;
193
194     // parse all regions
195     n = e.firstChild ();
196     while (!n.isNull ()) {
197         e = n.toElement ();
198         if (!e.isNull () && e.tagName () == "region") {
199             s = e.attribute ("id");
200             try {
201                 // Check that it is an extended traffic info
202                 if (!e.firstChildElement ("level").isNull ()) {
203                     ExtendedTrafficInfo info (e);
204                     if (info.valid ())
205                         new_ext_info[s] = info;
206                 }
207                 else {
208                     TrafficInfo info (e);
209                     if (info.valid ())
210                         new_info[s] = info;
211                 }
212             }
213             catch (const QString& msg) {
214             }
215         }
216         n = n.nextSibling ();
217     }
218
219     _ts = new_ts;
220     _info = new_info;
221     _ext_info = new_ext_info;
222
223     return true;
224 }
225
226
227 TrafficInfo Traffic::lookup (const QString &id) const
228 {
229     QMap<QString, TrafficInfo>::const_iterator it = _info.find (id);
230
231     if (it == _info.end ())
232         return TrafficInfo ();
233     else
234         return it.value ();
235 }
236
237
238 ExtendedTrafficInfo Traffic::lookup_ext (const QString &id) const
239 {
240     QMap<QString, ExtendedTrafficInfo>::const_iterator it = _ext_info.find (id);
241
242     if (it == _ext_info.end ())
243         return ExtendedTrafficInfo ();
244     else
245         return it.value ();
246 }
247
248
249 void Traffic::connectionChanged (bool active)
250 {
251     if (!active)
252         _fetcher.reset ();
253 }