Initial Commit
[uktrainplanner] / src / servicewindow.cpp
1 #include "servicewindow.h"
2 #include "ui_servicewindow.h"
3
4 ServiceWindow::ServiceWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::ServiceWindow)
5 {
6     ui->setupUi(this);
7
8     manager = new QNetworkAccessManager(this);
9     connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(readResponse(QNetworkReply*)));
10
11     ui->scrollArea->setProperty("FingerScrollable", true);
12     setupDialog();
13 }
14
15 /*
16  * Initialises the progress dialog for the network request
17  */
18 void ServiceWindow::setupDialog()
19 {
20     dialog = new QDialog(0);
21         layout = new QHBoxLayout();
22             progress = new QProgressBar();
23             progress->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
24             cancelButton = new QPushButton("Cancel");
25
26             layout->addWidget(progress);
27             layout->addWidget(cancelButton);
28         dialog->setLayout(layout);
29     dialog->show();
30
31     connect(cancelButton, SIGNAL(clicked()), dialog, SLOT(reject()));
32     connect(dialog, SIGNAL(rejected()), this, SLOT(cancel()));
33 }
34
35 /*
36  * Loads the service window for the requested train
37  */
38 void ServiceWindow::show(QString sID)
39 {
40     serviceID = sID;
41     //Load the departure board data.
42     setupRequest();
43     sendRequest();
44     //Note that QMainWindow::show() is absent here. Instead we show a progress dialog, then show the
45     //departure board only when we have loaded the data & created the display.
46     errorFlag = false;
47     firstTime = true;
48 }
49
50 /*
51  * Creates the network request, including the SOAP request XML.
52  */
53 void ServiceWindow::setupRequest()
54 {
55     QDomDocument document;
56
57     QDomElement env = document.createElement("soapenv:Envelope");
58     env.setAttribute("xmlns:soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
59     env.setAttribute("xmlns:typ", "http://thalesgroup.com/RTTI/2007-10-10/ldb/types");
60
61         QDomElement header = document.createElement("soapenv:Header");
62         QDomElement body = document.createElement("soapenv:Body");
63
64             QDomElement typ = document.createElement("typ:GetServiceDetailsRequest");
65
66                 QDomElement id = document.createElement("typ:serviceID");
67                 id.appendChild(document.createTextNode(serviceID));
68
69     document.appendChild(env);
70         env.appendChild(header);
71         env.appendChild(body);
72             body.appendChild(typ);
73                 typ.appendChild(id);
74
75     requestData = document.toString(-1).prepend("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
76
77     request = QNetworkRequest(QUrl("http://realtime.nationalrail.co.uk/LDBWS/ldb2.asmx"));
78     request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("text/xml;charset=utf-8"));
79     request.setRawHeader("SOAPAction", "http://thalesgroup.com/RTTI/2008-02-20/ldb/GetServiceDetails");
80 }
81
82 /*
83  * Sends the SOAP request
84  */
85 void ServiceWindow::sendRequest()
86 {
87     m_reply = manager->post(request, requestData.toUtf8().constData());
88     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError)));
89     connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64, qint64)));
90     connect(m_reply, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(uploadProgress(qint64, qint64)));
91 }
92
93 /*
94  * Cancel the current request
95  */
96 void ServiceWindow::cancel()
97 {
98     //Abort the request; an error signal will be emitted, so we don't need to delete the network reply here
99     m_reply->abort();
100 }
101
102 /*
103  * Updates the progress bar when making the request
104  */
105 void ServiceWindow::uploadProgress(qint64 rec, qint64 total)
106 {
107     dialog->setWindowTitle("Sending request");
108     progress->setRange(0, total);
109     progress->setValue(rec);
110 }
111
112 /*
113  * Updates the progress bar when receiving the reply to the request
114  */
115 void ServiceWindow::downloadProgress(qint64 rec, qint64 total)
116 {
117     dialog->setWindowTitle("Receiving reply");
118     progress->setRange(0, total);
119     progress->setValue(rec);
120 }
121
122 /*
123  * Slot for when there is a network error
124  */
125 void ServiceWindow::networkError(QNetworkReply::NetworkError error)
126 {
127     //If the request was cancelled, we show no error message
128     if(error == QNetworkReply::OperationCanceledError)
129     {
130         dialog->hide();
131         //Set the error flag to true, so that we can exit out of the readResponse method
132         errorFlag = true;
133     }
134     else
135     {
136         //If this error is a result of the first network request, we assume there is no connection to the internet
137         if(firstTime)
138         {
139             firstTime = false;
140             ensureConnection();
141             errorFlag = true;
142         }
143         else
144         {
145             QMessageBox msg;
146             msg.setText(QString("Network error: %1, %2").arg(m_reply->errorString()).arg(error));
147             msg.exec();
148             dialog->hide();
149             errorFlag = true;
150         }
151     }
152 }
153
154 /*
155  * Attempt to wait for connection on either wifi or gprs
156  */
157 void ServiceWindow::ensureConnection()
158 {
159     dialog->setWindowTitle("Connecting");
160
161     QNetworkInterface wlan = QNetworkInterface::interfaceFromName("wlan0");
162     QNetworkInterface gprs = QNetworkInterface::interfaceFromName("gprs0");
163
164     //qDebug() << "wlan0" << wlan.flags().testFlag(QNetworkInterface::IsUp) << wlan.flags().testFlag(QNetworkInterface::IsRunning);
165     //qDebug() << "\tgprs0" << gprs.flags().testFlag(QNetworkInterface::IsUp) << gprs.flags().testFlag(QNetworkInterface::IsRunning);
166
167     if( (wlan.isValid() && wlan.flags().testFlag(QNetworkInterface::IsUp)) || (gprs.isValid() && gprs.flags().testFlag(QNetworkInterface::IsUp)) )
168     {
169         //We are connected, so try sending the network request again
170         errorFlag = false;
171         sendRequest();
172     }
173     else
174     {
175         QTimer::singleShot(100, this, SLOT(ensureConnection()));
176     }
177 }
178
179 #define NAMESPACE   "declare namespace t=\"http://thalesgroup.com/RTTI/2007-10-10/ldb/types\";" \
180                     "declare function local:if-absent( $arg as item()*, $value as item()* ) as item()* { if (exists($arg)) then $arg else $value } ;"
181
182 /*
183  * Reads the response to the network request, and extracts the data we want.
184  */
185 void ServiceWindow::readResponse(QNetworkReply* reply)
186 {
187     QByteArray in = reply->readAll();
188
189     if(errorFlag == true)
190     {
191         m_reply->deleteLater();
192         return;
193     }
194
195     received.setData(in);
196     received.open(QIODevice::ReadOnly);
197
198     QXmlQuery query;
199     query.bindVariable("data", &received);
200
201     QStringList previousNames = queryList(query, "for $x in doc($data)//t:previousCallingPoints/t:callingPointList/t:callingPoint return fn:string(local:if-absent($x/t:locationName, ''))");
202     QStringList previousST = queryList(query, "for $x in doc($data)//t:previousCallingPoints/t:callingPointList/t:callingPoint return fn:string(local:if-absent($x/t:st, ''))");
203     QStringList previousAT = queryList(query, "for $x in doc($data)//t:previousCallingPoints/t:callingPointList/t:callingPoint return fn:string(local:if-absent($x/t:at, ''))");
204
205     QStringList nextNames = queryList(query, "for $x in doc($data)//t:subsequentCallingPoints/t:callingPointList/t:callingPoint return fn:string(local:if-absent($x/t:locationName, ''))");
206     QStringList nextST = queryList(query, "for $x in doc($data)//t:subsequentCallingPoints/t:callingPointList/t:callingPoint return fn:string(local:if-absent($x/t:st, ''))");
207     QStringList nextET = queryList(query, "for $x in doc($data)//t:subsequentCallingPoints/t:callingPointList/t:callingPoint return fn:string(local:if-absent($x/t:at, ''))");
208
209     for(int i = 0; i < previousNames.size(); i++)
210     {
211         DepartureWidget * w = new DepartureWidget(previousST.at(i), previousNames.at(i), previousAT.at(i), "", "");
212         ui->scrollArea->widget()->layout()->addWidget(w);
213         w->setShowPlatform(false);
214     }
215
216     QString name = queryOne(query, "for $x in doc($data)//t:GetServiceDetailsResult return fn:string(local:if-absent($x/t:locationName, ''))");
217     QString platform = queryOne(query, "for $x in doc($data)//t:GetServiceDetailsResult return fn:string(local:if-absent($x/t:platform, '-'))");
218     QString std = queryOne(query, "for $x in doc($data)//t:GetServiceDetailsResult return fn:string(local:if-absent($x/t:std, '-'))");
219     QString etd = queryOne(query, "for $x in doc($data)//t:GetServiceDetailsResult return fn:string(local:if-absent($x/t:etd, '-'))");
220
221     DepartureWidget * w = new DepartureWidget(std, name, etd, platform, "");
222     ui->scrollArea->widget()->layout()->addWidget(w);
223
224     for(int i = 0; i < nextNames.size(); i++)
225     {
226         DepartureWidget * w = new DepartureWidget(nextST.at(i), nextNames.at(i), nextET.at(i), "", "");
227         ui->scrollArea->widget()->layout()->addWidget(w);
228         w->setShowPlatform(false);
229     }
230
231     dialog->accept();
232     m_reply->deleteLater();
233     received.close();
234
235     QMainWindow::show();
236 }
237
238 QString ServiceWindow::queryOne(QXmlQuery & q, QString query)
239 {
240     QStringList output;
241     output.clear();
242     q.setQuery(query.prepend(NAMESPACE));
243     q.evaluateTo(&output);
244     return (output.size() == 0) ? QString() : output.at(0);
245 }
246
247 QStringList ServiceWindow::queryList(QXmlQuery & q, QString query)
248 {
249     QStringList output;
250     output.clear();
251     q.setQuery(query.prepend(NAMESPACE));
252     q.evaluateTo(&output);
253     return output;
254 }
255
256 ServiceWindow::~ServiceWindow()
257 {
258     delete ui;
259     delete manager;
260     delete dialog;
261 }
262
263 void ServiceWindow::changeEvent(QEvent *e)
264 {
265     QMainWindow::changeEvent(e);
266     switch (e->type()) {
267     case QEvent::LanguageChange:
268         ui->retranslateUi(this);
269         break;
270     default:
271         break;
272     }
273 }